第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() 函式用於判斷物件是否包含對應的屬性
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.")
如果 c 是 C 的例項化, 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 124、補充程式碼
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