通过上面的讲解,咱们已经知道如何定义描述符,且明白了描述符是如何工做的。python
正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最多见的应用之一,或许你还不知道,其实有不少 Python 的特性的底层实现机制都是基于 描述符协议
的,好比咱们熟悉的@property
、@classmethod
、@staticmethod
和 super
等。微信
先来讲说 property
吧。函数
有了第一篇的基础,咱们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。code
class Student: def __init__(self, name): self.name = name @property def math(self): return self._math @math.setter def math(self, value): if 0 <= value <= 100: self._math = value else: raise ValueError("Valid value must be in [0, 100]")
不防再简单回顾一下它的用法,经过property装饰的函数,如例子中的 math 会变成 Student 实例的属性。而对 math 属性赋值会进入 使用 math.setter
装饰函数的逻辑代码块。blog
为何说 property 底层是基于描述符协议的呢?经过 PyCharm 点击进入 property 的源码,很惋惜,只是一份相似文档同样的伪源码,并无其具体的实现逻辑。文档
不过,从这份伪源码的魔法函数结构组成,能够大致知道其实现逻辑。get
这里我本身经过模仿其函数结构,结合「描述符协议」来本身实现类 property
特性。源码
代码以下:it
class TestProperty(object): def __init__(self, fget=None, fset=None, fdel=None, doc=None): self.fget = fget self.fset = fset self.fdel = fdel self.__doc__ = doc def __get__(self, obj, objtype=None): print("in __get__") if obj is None: return self if self.fget is None: raise AttributeError return self.fget(obj) def __set__(self, obj, value): print("in __set__") if self.fset is None: raise AttributeError self.fset(obj, value) def __delete__(self, obj): print("in __delete__") if self.fdel is None: raise AttributeError self.fdel(obj) def getter(self, fget): print("in getter") return type(self)(fget, self.fset, self.fdel, self.__doc__) def setter(self, fset): print("in setter") return type(self)(self.fget, fset, self.fdel, self.__doc__) def deleter(self, fdel): print("in deleter") return type(self)(self.fget, self.fset, fdel, self.__doc__)
而后 Student 类,咱们也相应改为以下class
class Student: def __init__(self, name): self.name = name # 其实只有这里改变 @TestProperty def math(self): return self._math @math.setter def math(self, value): if 0 <= value <= 100: self._math = value else: raise ValueError("Valid value must be in [0, 100]")
为了尽可能让你少产生一点疑惑,我这里作两点说明:
使用TestProperty
装饰后,math
再也不是一个函数,而是TestProperty
类的一个实例。因此第二个math函数可使用 math.setter
来装饰,本质是调用TestProperty.setter
来产生一个新的 TestProperty
实例赋值给第二个math
。
第一个 math
和第二个 math
是两个不一样 TestProperty
实例。但他们都属于同一个描述符类(TestProperty),当对 math 对于赋值时,就会进入 TestProperty.__set__
,当对math 进行取值里,就会进入 TestProperty.__get__
。仔细一看,其实最终访问的仍是Student实例的 _math
属性。
说了这么多,仍是运行一下,更加直观一点。
# 运行后,会直接打印这一行,这是在实例化 TestProperty 并赋值给第二个math in setter >>> >>> s1.math = 90 in __set__ >>> s1.math in __get__ 90
对于以上理解 property
的运行原理有困难的同窗,请务必参照我上面写的两点说明。若有其余疑问,能够加微信与我进行探讨。
说完了 property
,这里再来说讲 @classmethod
和 @staticmethod
的实现原理。
我这里定义了一个类,用了两种方式来实现静态方法。
class Test: @staticmethod def myfunc(): print("hello") # 上下两种写法等价 class Test: def myfunc(): print("hello") # 重点:这就是描述符的体现 myfunc = staticmethod(myfunc)
这两种写法是等价的,就好像在 property
同样,其实如下两种写法也是等价的。
@TestProperty def math(self): return self._math math = TestProperty(fget=math)
话题仍是转回到 staticmethod
这边来吧。
由上面的注释,能够看出 staticmethod
其实就至关于一个描述符类,而myfunc
在此刻变成了一个描述符。关于 staticmethod
的实现,你能够参照下面这段我本身写的代码,加以理解。
调用这个方法能够知道,每调用一次,它都会通过描述符类的 __get__
。
>>> Test.myfunc() in staticmethod __get__ hello >>> Test().myfunc() in staticmethod __get__ hello
一样的 classmethod
也是同样。
class classmethod(object): def __init__(self, f): self.f = f def __get__(self, instance, owner=None): print("in classmethod __get__") def newfunc(*args): return self.f(owner, *args) return newfunc class Test: def myfunc(cls): print("hello") # 重点:这就是描述符的体现 myfunc = classmethod(myfunc)
验证结果以下
>>> Test.myfunc() in classmethod __get__ hello >>> Test().myfunc() in classmethod __get__ hello
讲完了 property
、staticmethod
和classmethod
与 描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 super 的实现原理,就交由你来本身完成。