Python绑定,未绑定和静态方法

示例

在Python 3中删除了绑定和非绑定方法的概念。在Python 3中,当您在类中声明方法时,您正在使用def关键字,从而创建了一个函数对象。这是一个常规函数,周围的类用作其命名空间。在下面的示例中,我们f在class中声明method A,它成为一个函数A.f:

Python 3.x 3.0
class A(object):
    def f(self, x):
        return 2 * x
A.f
# <functionA.fat ...>  (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)
# 真正
inspect.ismethod(A.f)
# 假
Python 2.x 2.3
import inspect

inspect.isfunction(A.f)
# 假
inspect.ismethod(A.f)
# 真正

在两个版本的Python中A.f,只要您将class的实例A作为第一个参数传递,就可以直接调用函数/方法。

A.f(1, 7)
# Python 2:TypeError:未绑定方法f()必须使用
#                      一个实例作为第一个参数(取而代之的是int实例) 
# 的Python 3:14   
a = A()
A.f(a, 20)
# Python 2 & 3: 40

现在假设a是class的实例A,那是a.f什么?好吧,从直觉上讲,这应该是f类的相同方法A,只是应该以某种方式“知道”它已应用于对象a–在Python中,这称为绑定到的方法a。

具体细节如下:编写会a.f调用的魔术__getattribute__方法a,该方法首先检查是否a有一个名为的属性f(没有),然后检查类A是否包含具有该名称的方法(有),并且创建一个新对象m类型的method,其具有的参考原始A.f中m.__func__,向对象的引用a中m.__self__。当将此对象作为函数调用时,它只需执行以下操作:m(...) => m.__func__(m.__self__, ...)。因此,将此对象称为绑定方法,因为在被调用时它知道提供绑定到的对象作为第一个参数。(这些内容在Python 2和3中的工作方式相同)。

a = A()
a.f
# <bound methodA.fof <__main__.A object at ...>>
a.f(2)
# 4

# 注意:绑定方法objecta.fis每次都会重新创建一次:
a.f isa.f # 假
# 作为性能优化,您可以将绑定方法存储在对象的
# __dict__,在这种情况下,方法对象将保持不变:
a.f = a.f
a.f isa.f # 真正

最后,Python有类方法静态方法–特殊方法。类方法的工作方式与常规方法相同,除了在对象上调用它们时,它们将绑定到对象的而不是对象。这样。当您调用这样的绑定方法时,它将类的作为第一个参数传递。静态方法甚至更简单:它们根本不绑定任何内容,而只是返回基础函数而无需任何转换。m.__self__ = type(a)a

class D(object):
    multiplier = 2

    @classmethod
    def f(cls, x):
        returncls.multiplier* x

    @staticmethod
    def g(name):
        print("Hello, %s" % name)

D.f
# <bound methodtype.fof <class '__main__.D'>>
D.f(12)
# 24
D.g
# <functionD.gat ...>
D.g("world")
# 你好,世界

请注意,即使在实例上访问,类方法也已绑定到该类:

d = D()
d.multiplier = 1337
(D.multiplier, d.multiplier)
# (2,1337)
d.f
# <bound methodD.fof <class '__main__.D'>>
d.f(10)
# 20

值得注意的是,在最低层次上,函数,方法,静态方法等实际上是调用__get__,__set__和可选的__del__特殊方法的描述符。有关类方法和静态方法的更多详细信息:

  • Python中的@staticmethod和@classmethod有什么区别?

  • @classmethod和@staticmethod对初学者的意义?