文章 《Python中 property 的實現原理及實現純 Python 版》 中探究了 property
的實現原理。若是能理解那邊描述符的使用方式,那也能很快理解本篇中的 staticmethod
和 classmethod
。php
對於類中定義的方法來講,經過類來調用與實例調用是不同的:python
class C: def f(self): pass print(C.f) # <function C.f at > print(C().f) # <bound method C.f of >
一個返回的是 function
類型,一個返回的是 method
類型。他們的主要區別在於,函數的 傳參都是顯式傳遞的
而方法則方法中 傳參每每都會有隱式傳遞的,具體根據於調用方
。例如示例中的 C().f
經過實例調用的方式會隱式傳遞 self
數據。閉包
staticmethod
的效果是讓 C.f
與 c.f
都返回函數,等價於 object.__getattribute__(c, "f")
或 object.__getattribute__(C, "f")
,運行代碼以下:函數
class C: @staticmethod def sf(): pass c = C() print(C.sf) # <function C.sf at 0x000001AEDDA64040> print(c.sf) # <function C.sf at 0x000001AEDDA64040> print(C.sf is c.sf) # True
要實現這樣的方式也能夠依託於描述符的機制,在 __get__
中返回原始的函數,所以它的 Python 實現版本異常的簡單:工具
class staticmethod(object): def __init__(self, f): self.f = f def __get__(self, obj, objtype=None): return self.f
這麼簡單的代碼也已是 C 實現版本對應的Python完整代碼了。學習
classmethod
則是要讓 C.f
和 c.f
都返回方法,而且傳遞隱式參數 cls
, 運行代碼以下:設計
class C: @classmethod def cf(cls): pass c = C() print(C.cf) # <bound method C.cf> print(c.cf) # <bound method C.cf> print(C.cf is c.cf) # False
classmethod
不只要隱式傳遞參數,還須要每次建立新的 <bound method>
對象。所以它的實現上須要用閉包,將閉包函數做爲返回值以便獲得新的對象:code
class classmethod(object): def __init__(self, f): self.f = f def __get__(self, obj, klass=None): if klass is None: klass = type(obj) def newfunc(*args): return self.f(klass, *args) return newfunc
這裏的技巧就在於閉包將隱式的 cls
經過閉包空間進行綁定。這個純python實現版本在功能上沒什麼問題,僅有個小缺陷:對象
c = C() print(C.cf) # <function classmethod.__get__.<locals>.newfunc at 0x000001EDF2527EE0> print(c.cf) # <function classmethod.__get__.<locals>.newfunc at 0x000001EDF2527EE0> print(C.cf is c.cf) # False
儘管咱們用閉包綁定了個隱式參數,但經過 c.cf
獲取的依然是 function
對象。我沒有找到能夠在Python代碼中建立 <bound method>
實例的方式。ip
staticmethod
和 classmethod
都運用了描述符的機制,學習描述符不只能提供接觸到更多工具集的方法,還能更深地理解 Python 工做的原理並更加體會到其設計的優雅性。