繫結的未繫結和靜態方法
在 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 的更多詳細資訊: