文章 《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 工做的原理并更加体会到其设计的优雅性。