Python (類)實例方法的特殊屬性

自定義函數的特殊屬性已經專門整理過一篇(Python 自定義函數的特殊屬性),方法的特殊屬性與其稍有不一樣,咱們經過下面這個例子展開介紹:python

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__ 屬性爲實例自己:segmentfault

print(a.foo.__self__)
# <__main__.A object at 0x00000233DF6DE2E8>
print(a)
# <__main__.A object at 0x00000233DF6DE2E8>

__func__ 屬性則返回方法所對應的底層函數:函數

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__ 屬性則與函數相應屬性的值一致,所不一樣的是方法的這些屬性均爲只讀,不可改寫:code

print(a.foo.__doc__)
# a method

print(a.foo.__name__)
# foo

print(a.foo.__module__)
# __main__

實例方法可經過底層函數訪問函數屬性

不過,實例方法也能夠經過其底層的 function 對象(經過 __func__ 屬性得到)訪問函數所具備的特殊屬性,如:對象

print(a.foo.__func__.__code__)
# <code object foo at 0x00000233DF6B5930, file "<ipython-input-43-c5636bcc492a>", line 3>

所以,諸如 __doc__,__name__,__module__等屬性的值就能夠經過底層函數相應的特殊屬性進行改寫:ip

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

底層函數的惟一性

須要注意的是:當一個類的實例方法是經過其餘實例方法建立,則其餘實例方法所對應的底層函數並不是其所建立的實例方法,而是其所建立的實例方法所對應的底層函數:get

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.fooinput

print(a.foo.__func__)
# <function A.foo at 0x00000233DF6C3F28>
print(a.foo)
# <bound method A.foo of <__main__.A object at 0x00000233DF6DE2E8>>

實例方法的首位參數爲實例自己

實例方法執行時,其底層函數的首位參數爲實例自己,下面兩行代碼執行結果是一致的:it

a.foo()
# hellow world!
a.foo.__func__(a)
# hellow world!

類實例方法的首位參數是類自己

當一個實例方法(嚴格來講是類實例方法)是由類方法建立,則其 __self__ 屬性是其類自己:io

print(a.clsmtd.__self__)
# <class '__main__.A'>

事實上,經過類方法創建的(類)實例方法,在調用底層函數時(下例是 A.clsmtd.__func__),其首位參數(也即 __self__)是類自己,這一點與實例方法執行時有所區別。

a.clsmtd('dog')
# dog
A.clsmtd('dog')
# dog
A.clsmtd.__func__(A, 'dog')
# dog

類實例方法,自己也是 bound method,這與實例方法一致:

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 methodclass instance method),兩種均可以被實例訪問;

  • 對於 instance method__self__ 屬性值爲 instance 自己,而 class method 其屬性值則爲 class 自己;

  • 無論是 instance method 仍是 class method ,執行時,都須要調用與之對應的底層函數(underlying function,經過 __func__ 訪問),底層函數的首位參數經過 __self__ 得到, 對於 instance method 其爲該實例自己,對於 class method 則爲該類自己;

  • bound method 能夠經過對應的底層函數,訪問函數的全部特殊屬性。

相關文章
相關標籤/搜索