自定義函式的特殊屬性已經專門整理過一篇(Python 自定義函式的特殊屬性),方法的特殊屬性與其稍有不同,我們通過下面這個例子展開介紹:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
class A(): def foo(self): '''a method''' print('hellow world!') bar = foo @classmethod def clsmtd(cls, arg): print(str(arg)) a = A() |
例項方法的只讀屬性
與自定義函式的特殊屬性相比,例項方法具有 __self__
,__func__
這兩個函式所不具有的只讀屬性;此外,方法的 __doc__
,__name__
,__module__
也是隻讀。對於例項方法而言,其 __self__
屬性為例項本身:
1 2 3 4 |
print(a.foo.__self__) # <__main__.A object at 0x00000233DF6DE2E8> print(a) # <__main__.A object at 0x00000233DF6DE2E8> |
而 __func__
屬性則返回方法所對應的底層函式:
1 2 3 4 |
print(a.foo) # <bound method A.foo of <__main__.A object at 0x00000233DF6DE2E8>> print(a.foo.__func__) #注意與 a.foo 的區別 # <function A.foo at 0x00000233DF6C3F28> |
至於 __doc__
,__name__
,__module__
屬性則與函式相應屬性的值一致,所不同的是方法的這些屬性均為只讀,不可改寫:
1 2 3 4 5 6 7 8 |
print(a.foo.__doc__) # a method print(a.foo.__name__) # foo print(a.foo.__module__) # __main__ |
例項方法可通過底層函式訪問函式屬性
不過,例項方法也可以通過其底層的 function
物件(通過 __func__
屬性獲得)訪問函式所具有的特殊屬性,如:
1 2 |
print(a.foo.__func__.__code__) # <code object foo at 0x00000233DF6B5930, file "<ipython-input-43-c5636bcc492a>", line 3> |
因此,諸如 __doc__
,__name__
,__module__
等屬性的值就可以通過底層函式相應的特殊屬性進行改寫:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
a.foo.__doc__ = 'raise error' # AttributeError: attribute '__doc__' of 'method' objects is not writable print(a.foo.__func__.__doc__) # a method a.foo.__func__.__doc__ = 'can be changed through func doc' print(a.foo.__doc__) # can be changed through func doc a.foo.__name__ = 'dobi' # AttributeError: 'method' object has no attribute '__name__' print(a.foo.__func__.__name__) # foo a.foo.__func__.__name__ = 'dobi' print(a.foo.__name__) # dobi |
底層函式的唯一性
需要注意的是:當一個類的例項方法是通過其他例項方法建立,則其他例項方法所對應的底層函式並非其所建立的例項方法,而是其所建立的例項方法所對應的底層函式:
1 2 3 4 |
print(a.bar) #注意這裡 a.bar 是個例項方法 <bound method A.foo of <__main__.A object at 0x00000233DF6DE2E8>> print(a.bar.__func__) # <function A.foo at 0x00000233DF6C3F28> |
上例中,通過其他例項方法 a.bar
建立了例項方法 a.foo
,但a.bar.__func__
卻是 a.foo.__func__
而非 a.foo
:
1 2 3 4 |
print(a.foo.__func__) # <function A.foo at 0x00000233DF6C3F28> print(a.foo) # <bound method A.foo of <__main__.A object at 0x00000233DF6DE2E8>> |
例項方法的首位引數為例項本身
例項方法執行時,其底層函式的首位引數為例項本身,下面兩行程式碼執行結果是一致的:
1 2 3 4 |
a.foo() # hellow world! a.foo.__func__(a) # hellow world! |
類例項方法的首位引數是類本身
當一個例項方法(嚴格來說是類例項方法)是由類方法建立,則其 __self__
屬性是其類本身:
1 2 |
print(a.clsmtd.__self__) # <class '__main__.A'> |
事實上,通過類方法建立的(類)例項方法,在呼叫底層函式時(下例是 A.clsmtd.__func__
),其首位引數(也即 __self__
)是類本身,這一點與例項方法執行時有所區別。
1 2 3 4 5 6 |
a.clsmtd('dog') # dog A.clsmtd('dog') # dog A.clsmtd.__func__(A, 'dog') # dog |
類例項方法,本身也是 bound method
,這與例項方法一致:
1 2 3 4 5 6 7 8 |
print(a.clsmtd) # <bound method A.clsmtd of <class '__main__.A'>> print(a.foo) # <bound method A.foo of <__main__.A object at 0x00000233DF6DE2E8>> print(A.clsmtd) # <bound method A.clsmtd of <class '__main__.A'>> print(A.foo) # <function A.foo at 0x00000233DF6C3F28> |
只不過,一個是繫結類(類例項),另一個是繫結例項。
總結
- Python 3 有兩種
bound method
, 一種是instance method
,一種是class method
(class instance method
),兩種都可以被例項訪問; - 對於
instance method
其__self__
屬性值為instance
本身,而class method
其屬性值則為class
本身; - 不管是
instance method
還是class method
,執行時,都需要呼叫與之對應的底層函式(underlying function
,通過__func__
訪問),底層函式的首位引數通過__self__
獲得, 對於instance method
其為該例項本身,對於class method
則為該類本身; bound method
可以通過對應的底層函式,訪問函式的所有特殊屬性。