类的方法之间的差异,它们是"函数"还是"绑定方法"?

Tim*_*Tim 1 python python-3.x

我做了一点实验.通过检查__dict__类或实例,我可以看到某些方法有类型function和一些bound method.实验很乱,我无法解决以下问题.

在Python 3中,类或实例的方法之间有什么区别,它们是"函数",哪些是"绑定方法"?

它们是如何分别创建的?

它们可以在类和实例上调用吗?它们是否会被隐含地作为第一个参数的实例?

"绑定方法"是类的属性还是类的实例?

谢谢.

MSe*_*ert 5

这个答案将是技术性的,我希望它仍然可以理解.问题是需要知道描述符协议才能理解Python中的方法是如何工作的.

Python 3中的所有函数都是描述符,准确地说它们是非数据描述符.这意味着他们实现了一种__get__方法 - 但没有__set__方法.

这很有趣,因为如果在类或实例上查找,描述符可以(几乎)执行任何操作.

通过Pythons数据模型中__get__方法的定义:

object.__get__(self, instance, owner)

调用以获取所有者类(类属性访问)或该类的实例(实例属性访问)的属性.owner永远是老板级,同时instance是该属性被访问过,或实例None时,属性可以通过访问owner.此方法应返回(计算)属性值或引发AttributeError异常.

那么,这与之间的差异做functionbound_method

它很容易,一个函数通过__get__一个instance=None将返回自己的函数:

>>> def func(x): return x
>>> func.__get__(None, object)
<function __main__.func>
>>> func.__get__(None, object) is func
True
Run Code Online (Sandbox Code Playgroud)

虽然bound_method使用not-None实例访问它是一个if:

>>> func.__get__(object())
<bound method func of <object object at 0x00000155614A0610>>
Run Code Online (Sandbox Code Playgroud)

它基本上只是func存储实例的包装器:

>>> m = func.__get__(object())
>>> m.__self__   # stored instance
<object at 0x155614a0650>

>>> m.__func__  # stored function
<function __main__.func>
Run Code Online (Sandbox Code Playgroud)

但是,在调用时,它会将实例作为第一个参数传递给包装函数:

>>> m()
<object at 0x155614a0650>
Run Code Online (Sandbox Code Playgroud)

因此,bound methods将实例作为第一个参数传递,而functions则不传递(它们需要所有属性).


所以,当你在一个类中的所有正常方法将显示为功能,而所有正常的一个实例的方法会bound methods.

为什么我提到常规方法?因为您可以定义任意描述符.例如,Python内置函数已经包含几个例外:

  • classmethod
  • staticmethod
  • property (这实际上是一个数据描述符,因此我将在下面的讨论中忽略它)

classmethodbound method即使在课堂上查询,s也会显示.这是因为它们在类上是可调用的,并将类传递给函数,无论它们是在类还是实例上调用:

class Test(object):
    @classmethod
    def func(x):
        return x

>>> Test.func
<bound method Test.func of <class '__main__.Test'>>
>>> Test().func
<bound method Test.func of <class '__main__.Test'>>
Run Code Online (Sandbox Code Playgroud)

并且staticmethods总是显示为函数,因为它们从不传递函数的任何附加内容:

class Test(object):
    @staticmethod
    def func(x):
        return x

>>> Test().func
<function __main__.Test.func>
>>> Test.func
<function __main__.Test.func>
Run Code Online (Sandbox Code Playgroud)

所以很容易看到bound method类上的s(例如classmethods),同样也可以function在实例上找到正常的s(例如staticmethods).