1. 程式人生 > 實用技巧 >第040講:類和物件:一些相關的BIF

第040講:類和物件:一些相關的BIF

#搬運自FishC論壇,該系列已完結,共有00~96節,本人學習過程中的記錄等。

#FishC論壇:http://bbs.fishc.com/forum.php

#小甲魚課程規劃帖:http://bbs.fishc.com/thread-1053-1-1.html 此教程適合完全零基礎的朋友學習,

課題筆記

issubclass()函式

issubclass()方法用於判斷引數 class 是否是型別引數 classinfo 的子類issubclass(class, classinfo),如果 class 是 classinfo 的子類返回 True,否則返回 False。

它這種檢查是屬於非嚴格的檢查,1、一個類,他會被認為是自身的子類;2、classinfo可以是類物件組成的元組,只要class是與其中一個候選類的子類,則返回True,一個一個檢索過去,其他型別就會丟擲Type Error

>>> class A:
    pass

>>> class B:
    pass

>>> issubclass(B, A)
False
>>> class B(A):
    pass

>>> issubclass(B, A)
True
>>> issubclass(B, B)
True
>>> issubclass(B, object)
True
>>> #object是所有類的一個基類

isinstance() 函式

isinstance() 函式來判斷一個物件是否是一個已知的型別,類似 type()。

isinstance() 與 type() 區別:

type() 不會認為子類是一種父類型別,不考慮繼承關係。

isinstance() 會認為子類是一種父類型別,考慮繼承關係。

如果要判斷兩個型別是否相同推薦使用 isinstance()。

用法:isinstance(object, classinfo) object -- 例項物件,classinfo -- 可以是直接或間接類名、基本型別或者由它們組成的元組。

注意:1、如果第一個引數不是物件,則永遠返回False;2、如果第二個引數不是由類物件組成的元組,會丟擲個TypeError異常。

hasattr() 函式

hasattr() 函式用於判斷物件是否包含對應的屬性

。語法:hasattr(object, name),object -- 物件。name -- 字串,屬性名。如果物件有該屬性返回 True,否則返回 False。

getattr() 函式

getattr()函式用於返回一個物件屬性值。語法:getattr(object, name[, default]) object -- 物件,name -- 字串,物件屬性,default -- 預設返回值,如果不提供該引數,在沒有對應屬性時,將觸發 AttributeError。返回物件屬性值。例子:

>>>class A(object):
...     bar = 1
... 
>>> a = A()
>>> getattr(a, 'bar')        # 獲取屬性 bar 值
1
>>> getattr(a, 'bar2')       # 屬性 bar2 不存在,觸發異常
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'A' object has no attribute 'bar2'
>>> getattr(a, 'bar2', 3)    # 屬性 bar2 不存在,但設定了預設值
3
>>>

setattr() 函式

setattr() 函式對應函式 getattr(),用於設定屬性值,該屬性不一定是存在的。語法:setattr(object, name, value)object -- 物件,name -- 字串,物件屬性,value -- 屬性值。無返回值。例子:

對已存在的屬性進行賦值:

>>>class A(object):
...     bar = 1
... 
>>> a = A()
>>> getattr(a, 'bar')          # 獲取屬性 bar 值
1
>>> setattr(a, 'bar', 5)       # 設定屬性 bar 值
>>> a.bar
5

如果屬性不存在會建立一個新的物件屬性,並對屬性賦值:

>>>class A():
...     name = "runoob"
... 
>>> a = A()
>>> setattr(a, "age", 28)
>>> print(a.age)
28
>>>

delattr() 函式

delattr 函式用於刪除屬性delattr(x, 'foobar') 相等於 del x.foobar。語法:delattr(object, name)object -- 物件,name -- 必須是物件的屬性。無返回值。例子:

#!/usr/bin/python
# -*- coding: UTF-8 -*-
 
class Coordinate:
    x = 10
    y = -5
    z = 0
 
point1 = Coordinate() 
 
print('x = ',point1.x)
print('y = ',point1.y)
print('z = ',point1.z)
 
delattr(Coordinate, 'z')
 
print('--刪除 z 屬性後--')
print('x = ',point1.x)
print('y = ',point1.y)
 
# 觸發錯誤
print('z = ',point1.z)

結果:

('x = ', 10)
('y = ', -5)
('z = ', 0)
--刪除 z 屬性後--
('x = ', 10)
('y = ', -5)
Traceback (most recent call last):
  File "test.py", line 22, in <module>
    print('z = ',point1.z)
AttributeError: Coordinate instance has no attribute 'z'

property() 函式

property() 函式的作用是在新式類中返回屬性值。用屬性來設定屬性。語法:class property([fget[, fset[, fdel[, doc]]]]) or property(fget = None, fset = None, fdel = None, doc = None)

引數:fget -- 獲取屬性值的函式;fset -- 設定屬性值的函式;fdel -- 刪除屬性值函式;doc -- 屬性描述資訊

返回新式類屬性。

property()這個函式的作用就是設定一個屬性,這個屬性的作用是去設定定義好的屬性。x如何設定屬性,你必須傳入寫好的方法,第一個屬性是獲取屬性的方法,第二個是設定屬性的方法,第三是刪除屬性的方法,第四個就文件。c1.x 就會執行第一個引數的內容,就是獲取方法了,c1.x = .. 等於什麼的時候就會呼叫第二個方法。小甲魚例子:

class C:
    def __init__(self, size = 10):
        self.size = size
    def getSize(self):
        return self.size
    def setSize(self, value):
        self.size = value
    def delSize(self):
        del self.size
    x = property(getSize, setSize, delSize)

    
>>> c1 = C()
>>> c1.getSize()
10
>>> c1.x
10
>>> c1.x = 18
>>> c1.x
18
>>> c1.size
18
>>> c1.getSize()
18
>>> del c1.x
>>> c1.size
Traceback (most recent call last):
  File "<pyshell#19>", line 1, in <module>
    c1.size
AttributeError: 'C' object has no attribute 'size'
>>> 

菜鳥教程例子:

定義一個可控屬性值 x:

class C(object):
    def __init__(self):
        self._x = None
 
    def getx(self):
        return self._x
 
    def setx(self, value):
        self._x = value
 
    def delx(self):
        del self._x
 
    x = property(getx, setx, delx, "I'm the 'x' property.")

如果 cC 的例項化, c.x 將觸發 getter 就是property的第一個引數,第一個引數的方法是getattr;而c.x = value 將觸發 setter 就是第二個方法,設定; del c.x 觸發 deleter。

如果給定 doc 引數,其將成為這個屬性值的 docstring,否則 property 函式就會複製 fget 函式的 docstring(如果有的話)

這段裝飾器的還不會!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

將 property 函式用作裝飾器可以很方便的建立只讀屬性

class Parrot(object):
    def __init__(self):
        self._voltage = 100000
 
    @property
    def voltage(self):
        """Get the current voltage."""
        return self._voltage

上面的程式碼將 voltage() 方法轉化成同名只讀屬性的 getter 方法。

property 的 getter,setter 和 deleter 方法同樣可以用作裝飾器:

class C(object):
    def __init__(self):
        self._x = None
 
    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x
 
    @x.setter
    def x(self, value):
        self._x = value
 
    @x.deleter
    def x(self):
        del self._x

這個程式碼和第一個例子完全相同,但要注意這些額外函式的名字和 property 下的一樣,例如這裡的 x。

課後測試題及答案

測試題:

0. 如何判斷一個類是否為另一個類的子類?

使用issubclass()函式,例如issubclass(A, B),A是否是B的子類呢

答:使用 issubclass(class, classinfo) 函式,如果第一個引數(class)是第二個引數(classinfo)的一個子類,則返回 True,否則返回 False。

另外以下這些常識你應該知道:

1、一個類被認為是其自身的子類

2、classinfo 可以是類物件組成的元祖,只要 class 與其中任何一個候選類的子類,則返回 True

3、在其他情況下,會丟擲一個 TypeError 異常

1. 如何判斷物件 a 是否為 類 A 的例項物件?

使用isinstance()函式

答:使用 isinstance(object, classinfo) 函式,如果第一個引數(object)是第二個引數(classinfo)的例項物件,則返回 True,否則返回 False。

另外以下這些常識你應該知道:

1、如果 objec t是 classinfo 的子類的一個例項,也符合條件

2、如果第一個引數不是物件,則永遠返回False

3、classinfo 可以是類物件組成的元祖,只要class與其中任何一個候選類的子類,則返回 True

4、如果第二個引數不是類或者由類物件組成的元祖,會丟擲一個 TypeError 異常

2. 如何優雅地避免訪問物件不存在的屬性(不產生異常)?

使用hasattr()

答:有兩種方法可以做到。第一種先使用 hasattr(object, name) 函式判斷屬性是否存在,如果存在再訪問(第一個引數(object)是物件,第二個引數(name)是屬性名的字串形式);第二種方法是直接使用 getattr(object, name[, default]) 函式並設定 default 引數(返回物件指定的屬性值,如果指定的屬性不存在,返回default(可選引數)的值)。

3. Python 的一些 BIF 很奇怪,但卻十分有用。請問 property() 函式的作用是什麼?

相當於建立了一個介面

答:property() 函式允許程式設計人員輕鬆、有效地管理屬性訪問。

4. 請補充以下程式碼,使程式可以正常執行:

class C:
    def __init__(self, size=10):
        self.size = size

    def getXSize(self):
        return self.size

    def setXSize(self, value):
        self.size = value

    def delXSize(self):
        del self.size

        # 此處應該補充一句程式碼,程式才能正常執行

>>> c.x
10
>>> c.x = 12
>>> c.x
12
4、補充程式碼
class C:
    def __init__(self, size=10):
        self.size = size

    def getXSize(self):
        return self.size

    def setXSize(self, value):
        self.size = value

    def delXSize(self):
        del self.size

        # 此處應該補充一句程式碼,程式才能正常執行
    x = property(getXSize, setXSize, delXSize)#property的引數是字串,方法名就ok了
# >>> c.x
# 10
# >>> c.x = 12
# >>> c.x
# 12

答:x = property(getXSize, setXSize, delXSize)

5. 通過自學【擴充套件閱讀】Python 函式修飾符(裝飾器)的使用,使用修飾符修改以下程式碼。

程式碼A:

class CodeA:
    def foo():
        print("呼叫靜態方法 foo()")

        # 將 foo() 方法設定為靜態方法
        foo = staticmethod(foo)
程式碼A

答案:

程式碼B:

class CodeB:
    def foo(cls):
        print("呼叫類方法 foo()")

        # 將 foo() 方法設定為類方法
        foo = classmethod(foo)
程式碼B

答:其實正是因為設定靜態方法和類方法過於討人吐槽,因此 Python 的作者才開發出了函式修飾符的形式替代

程式碼A:

class CodeA:
        @staticmethod
    def foo():
        print("呼叫靜態方法 foo()")

程式碼B:

class CodeB:
        @classmethod
    def foo(cls):
        print("呼叫類方法 foo()")

6. 你真的理解了修飾符的用法嗎?那請你寫出以下程式碼沒有用上修飾符的等同形式:

@something
def f():
    print("I love FishC.com!")

答:其實 Python 的修飾符就是一種優雅的封裝,但要注意的是只可以模組或類定義內對函式進行修飾,不允許修飾一個類。

一個修飾符就是一個函式,它將被修飾的函式做為引數,並返回修飾後的同名函式其它可呼叫的東西。

@something
def f():
    print("I love FishC.com!")

# 相當於

def f():
    print("I love FishC.com!")

f = something(f)

7. 通過自學【擴充套件閱讀】property 的詳細使用方法,將第 4 題的程式碼修改為“使用屬性修飾符建立描述符”的方式實現。

答:可能你還沒聽說過描述符(這個概念在你學完接下來的幾節課自然會了解),但這一點都影響聰明的你修改這個程式。

程式碼清單:

class C:
    def __init__(self, size=10):
        self.size = size
        
    @property
    def x(self):
        return self.size

    @x.setter
    def x(self, value):
        self.size = value

    @x.deleter
    def x(self):
        del self.size