Python数据模型文档:一个无绑定的用户定义方法对象和一个类方法对象

3 投票
1 回答
1197 浏览
提问于 2025-04-17 12:05

在参考资料的数据模型部分,作者花了很多精力来解释用户定义的方法是如何创建和运作的:(可以查看http://docs.python.org/reference/datamodel.html#the-standard-type-hierarchy,然后往下滚动)

当你获取一个类的属性时,可能会创建用户定义的方法对象(可能是通过这个类的一个实例)。如果这个属性是一个用户定义的函数对象、一个未绑定的用户定义方法对象,或者一个类方法对象,就会发生这种情况。当这个属性是一个用户定义的方法对象时,只有在从中获取的方法对象的类与原始方法对象存储的类相同,或者是原始类的派生类时,才会创建一个新的方法对象;否则,就会直接使用原始的方法对象。

那么未绑定的用户定义方法对象类方法对象之间有什么区别呢?

1 个回答

6

从“用户”的角度来看,Python中的类方法是一种特殊的方法,它的第一个参数是类本身,而不是像普通方法那样接收类的实例,普通方法的第一个参数通常叫做self

如果你从一个类中获取一个普通方法,而不是从这个类的实例中获取,你得到的是一个“未绑定的方法”。这意味着它只是一个函数的包装,但在调用时不会自动添加类或任何实例作为第一个参数。因此,如果你想调用这个“未绑定的方法”,你需要手动传入一个类的实例作为第一个参数。

另一方面,如果你手动调用一个类方法,类会自动作为第一个参数传入:

>>> class A(object):
...   def b(self):
...      pass
...   @classmethod
...   def c(cls):
...      pass
... 
>>> A.b
<unbound method A.b>
>>> A.c
<bound method type.c of <class '__main__.A'>>
>>> A.c()
>>> A.b()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unbound method b() must be called with A instance as first argument (got nothing instead)
>>> 

在底层,事情大致是这样的,使用“新风格类”时:

当你定义一个类的主体时,方法其实就是普通的函数。当类的主体结束后,Python会调用这个类的元类(通常是内置的type),并将类名、基类和类主体字典作为参数传递给它。这个调用会生成一个类——在Python中,类也是一个对象,因为一切都是对象。

现在,Python有一些很酷的方式来定制属性访问,这就是所谓的“描述符”。描述符是任何定义了__get__方法的对象(还有__set____del__,但我们这里不讨论这些)。当你在Python中访问一个类或对象的属性时,通常会返回该属性所指向的对象——但如果它是类属性,并且这个对象是一个描述符,那么Python就不会直接返回这个对象,而是会调用这个对象的__get__方法,并返回其结果。例如,内置的property就是一个类,它实现了__set____get____del__等方法。

当属性被获取时,任何函数(或类方法或未绑定的方法,如数据模型所述)都有一个__get__方法,这使得它成为一个描述符。基本上,描述符在每次访问属性时,会根据函数主体中定义的函数名创建一个新的对象——这个对象在调用时会自动填充第一个参数,也就是说,它变成了一个method

例如:

>>> class B(object):
...    def c(self):
...      pass
...    print c
... 
<function c at 0x1927398>
>>> print B.c
<unbound method B.c>
>>> b = B()
>>> b.c
<bound method B.c of <__main__.B object at 0x1930a10>

如果你想获取函数对象,而不转换为方法对象,可以通过类的__dict__属性来实现,这样不会触发描述符:

>>> B.__dict__["c"]
<function c at 0x1927398>
>>> B.__dict__["c"].__get__
<method-wrapper '__get__' of function object at 0x1927398>
>>> B.__dict__["c"].__get__(b, B)
<bound method B.c of <__main__.B object at 0x1930a10>>
>>> B.__dict__["c"].__get__(None, B)
<unbound method B.c>

至于“类方法”,它们只是不同类型的对象,显式地用内置的classmethod装饰。调用它的__get__时返回的对象是一个包装器,它会在调用时将cls作为第一个参数填入。

撰写回答