繫結的未繫結和靜態方法

在 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

現在假設 aA 的一個例項,那麼什麼是 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 的更多詳細資訊: