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 能够经过对应的底层函数,访问函数的全部特殊属性。

相关文章
相关标签/搜索