第040講:類和物件:一些相關的BIF
目錄
0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!
3. Python 的一些 BIF 很奇怪,但卻十分有用。請問 property() 函式的作用是什麼?
5. 通過自學【Python擴充套件閱讀】Python 函式修飾符(裝飾器)的使用,使用修飾符修改以下程式碼。
6. 你真的理解了修飾符的用法嗎?那請你寫出以下程式碼沒有用上修飾符的等同形式:
7. 通過自學【Python擴充套件閱讀】property 的詳細使用方法,將第 4 題的程式碼修改為“使用屬性修飾符建立描述符”的方式實現。
0. 請寫下這一節課你學習到的內容:格式不限,回憶並複述是加強記憶的好方式!
今天我們談談和類與物件相關的BIF,也就是內建函式。
(一)issubclass(class, classinfo)
如果第一個引數 class 是第二個引數 classinfo 的子類,就返回 True,關於這個函式有幾點需要注意的:
- 一個類被認為是其自身的子類
- classinfo 可以是類物件組成的元組,只要 class 是其中一個候選類的子類,就返回 True
(二)isinstance(object, classinfo)
檢查一個例項物件 object 是否屬於一個類 classinfo,關於這個函式有幾點需要注意的:
- 如果第一個引數不是物件,則永遠返回 False
- 如果第二個引數不是類或者由類物件組成的元組,則丟擲一個 TypeError 異常
另外,Python 提供了幾個BIF讓我們訪問物件的屬性:
(三)hasattr(object, name) attr = attribute:屬性。
測試一個物件是否有指定的屬性。name 要用引號把屬性名引起來。
>>> class C:
def __init__(self, x = 0):
self.x = x
>>> c1 = C()
>>> hasattr(c1, "x")
True
>>> hasattr(c1, x)
Traceback (most recent call last):
File "<pyshell#71>", line 1, in <module>
hasattr(c1, x)
NameError: name 'x' is not defined
(四)getattr(object, name[ , default] )
返回物件指定的屬性值。如果指定的屬性不存在,如果你有設定 default,它會把這個default 引數打印出來,否則會丟擲一個AttributeError異常。
>>> class C:
def __init__(self, x = 0):
self.x = x
>>> c1 = C()
>>> getattr(c1, 'x')
0
>>> getattr(c1, 'y')
Traceback (most recent call last):
File "<pyshell#80>", line 1, in <module>
getattr(c1, 'y')
AttributeError: 'C' object has no attribute 'y'
>>> getattr(c1, 'y', '你所訪問的屬性不存在')
'你所訪問的屬性不存在'
(五)setattr(object, name, value)
設定物件中指定屬性的值,如果指定的屬性不存在,會新建一個新的屬性,並給其賦值。
>>> setattr(c1, 'y', '來自江南的你')
>>> getattr(c1, 'y', '你所訪問的屬性不存在')
'來自江南的你'
(六)delattr(object, name)
刪除物件中指定的屬性,如果屬性不存在,就丟擲一個AttributeError異常。
俗話說,條條大路通羅馬。Python 其實提供了好幾個方式供你選擇,property 是一個BIF,作用是通過屬性設定屬性,
property(fget = None, fset = None, fdel = None, doc = None)
property 函式的作用就是設定一個屬性,這個屬性就是去設定定義好的屬性,它的第一個引數 fget 是獲取屬性的方法,第一個引數 fset 是設定屬性的方法,第一個引數 fdel 是刪除屬性的方法。舉例說明:
>>> 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.x
10
>>> c1.getSize()
10
>>> c1.x = 18
>>> c1.getSize()
18
>>> c1.setSize(20)
>>> c1.x
20
>>> del c1.x
>>> c1.getSize()
Traceback (most recent call last):
File "<pyshell#104>", line 1, in <module>
c1.getSize()
File "<pyshell#94>", line 5, in getSize
return self.size
AttributeError: 'C' object has no attribute 'size'
property 的優勢:舉個例子,在上面這個例子中,這個程式慢慢寫的很複雜了,有一天,你想把這個程式進行大改,把函式名進行改寫,如果沒有 property,那你提供給使用者的呼叫介面就西藥修改,就會降低使用者體驗,但是有了property,問題就不存在了,因為提供給使用者的介面都是 x,程式裡面無論如何修改,property裡面的引數跟著改進行了,使用者還是隻用呼叫 x 來設定或者獲取 size 屬性就可以了。
測試題
0. 如何判斷一個類是否為另一個類的子類?
答:使用 issubclass(class, classinfo) 函式,如果第一個引數(class)是第二個引數(classinfo)的一個子類,則返回 True,否則返回 False。
另外以下這些常識你應該知道:
- 一個類被認為是其自身的子類
- classinfo 可以是類物件組成的元祖,只要 class 與其中任何一個候選類的子類,則返回 True
- 在其他情況下,會丟擲一個 TypeError 異常
1. 如何判斷物件 a 是否為 類 A 的例項物件?
答:使用 isinstance(object, classinfo) 函式,如果第一個引數(object)是第二個引數(classinfo)的例項物件,則返回 True,否則返回 False。
另外以下這些常識你應該知道:
- 如果 objec t是 classinfo 的子類的一個例項,也符合條件
- 如果第一個引數不是物件,則永遠返回False
- classinfo 可以是類物件組成的元祖,只要class與其中任何一個候選類的子類,則返回 True
- 如果第二個引數不是類或者由類物件組成的元祖,會丟擲一個 TypeError 異常
2. 如何優雅地避免訪問物件不存在的屬性(不產生異常)?
答:有兩種方法可以做到。
第一種先使用 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
答:x = property(getXSize, setXSize, delXSize)
5. 通過自學【Python擴充套件閱讀】Python 函式修飾符(裝飾器)的使用,使用修飾符修改以下程式碼。
程式碼A:
class CodeA:
def foo():
print("呼叫靜態方法 foo()")
# 將 foo() 方法設定為靜態方法
foo = staticmethod(foo)
程式碼B:
class CodeB:
def foo(cls):
print("呼叫類方法 foo()")
# 將 foo() 方法設定為類方法
foo = classmethod(foo)
答:其實正是因為設定靜態方法和類方法過於討人吐槽,因此 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. 通過自學【Python擴充套件閱讀】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