绑定的未绑定和静态方法
在 Python 3 中删除了绑定和未绑定方法的想法。在 Python 3 中,当你在类中声明方法时,你正在使用 def
关键字,从而创建一个函数对象。这是一个常规函数,周围的类作为其命名空间。在下面的例子中,我们在类 A
中声明方法 f
,它变成了一个函数 A.f
:
Python 3.x >= 3.0
class A(object):
def f(self, x):
return 2 * x
A.f
# <function A.f at ...> (in Python 3.x)
在 Python 2 中,行为是不同的:类中的函数对象被 instancemethod
类型的对象隐式替换,这些对象被称为*未绑定方法,*因为它们未绑定到任何特定的类实例。可以使用 .__func__
属性访问基础函数。
Python 2.x >= 2.3
A.f
# <unbound method A.f> (in Python 2.x)
A.f.__class__
# <type 'instancemethod'>
A.f.__func__
# <function f at ...>
后面的行为通过检查得到确认 - 方法被认为是 Python 3 中的函数,而在 Python 2 中则支持这种区别。
Python 3.x >= 3.0
import inspect
inspect.isfunction(A.f)
# True
inspect.ismethod(A.f)
# False
Python 2.x >= 2.3
import inspect
inspect.isfunction(A.f)
# False
inspect.ismethod(A.f)
# True
在 Python 函数/方法的两个版本中,只要你将类 A
的实例作为第一个参数传递,就可以直接调用 A.f
。
A.f(1, 7)
# Python 2: TypeError: unbound method f() must be called with
# A instance as first argument (got int instance instead)
# Python 3: 14
a = A()
A.f(a, 20)
# Python 2 & 3: 40
现在假设 a
是 A
的一个实例,那么什么是 a.f
呢?嗯,直觉上这应该是类 A
的相同方法 f
,只有它应该以某种方式知道它被应用于对象 a
- 在 Python 中这被称为绑定到 a
的方法。
细节如下:写 a.f
调用 a
的魔法 __getattribute__
方法,首先检查 a
是否有一个名为 f
的属性(它没有),然后检查类 A
是否包含具有这样名字的方法(确实如此),并创建了一个 method
类型的新对象 m
,它引用了 m.__func__
中的原始 A.f
,以及对 m.__self__
中对象 a
的引用。当此对象作为函数调用时,它只执行以下操作:m(...) => m.__func__(m.__self__, ...)
。因此,此对象称为**绑定方法,**因为在调用时,它知道将其绑定的对象作为第一个参数提供。 (这些东西在 Python 2 和 3 中的工作方式相同)。
a = A()
a.f
# <bound method A.f of <__main__.A object at ...>>
a.f(2)
# 4
# Note: the bound method object a.f is recreated *every time* you call it:
a.f is a.f # False
# As a performance optimization you can store the bound method in the object's
# __dict__, in which case the method object will remain fixed:
a.f = a.f
a.f is a.f # True
最后,Python 有类方法和静态方法 - 特殊类型的方法。类方法的工作方式与常规方法相同,只是在对象上调用它们时,它们绑定到对象的类而不是对象的类。因此 m.__self__ = type(a)
。当你调用这样的绑定方法时,它会传递 a
的类作为第一个参数。静态方法甚至更简单:它们根本不绑定任何东西,只是返回底层函数而不进行任何转换。
class D(object):
multiplier = 2
@classmethod
def f(cls, x):
return cls.multiplier * x
@staticmethod
def g(name):
print("Hello, %s" % name)
D.f
# <bound method type.f of <class '__main__.D'>>
D.f(12)
# 24
D.g
# <function D.g at ...>
D.g("world")
# Hello, world
请注意,即使在实例上访问,类方法也绑定到类:
d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2, 1337)
d.f
# <bound method D.f of <class '__main__.D'>>
d.f(10)
# 20
值得注意的是,在最低级别,函数,方法,静态方法等实际上是调用 __get__
,__set
__和可选的 __del__
特殊方法的描述符 。有关 classmethods 和 staticmethods 的更多详细信息: