好吧,我认可我标题党了。可是这篇文章的知识点,你有极大的可能并不知道。python
前段时间,我写了一篇描述符的入门级文章,从那些文章里你知道了如何定义描述符,且明白了描述符是如何工做的。微信
若是你还未学习,能够点击这里进行阅读:Python为何要使用描述符函数
正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最多见的应用之一,或许你还不知道,其实有不少 Python 的特性的底层实现机制都是基于 描述符协议
的,好比咱们熟悉的@property
、@classmethod
、@staticmethod
和 super
等。post
这些装饰器方法,你绝对熟悉得不得了,可是今天并非要讲他们的用法,而是要讲是如何本身经过 纯Python 实现这些特性。学习
先来讲说 property
吧。spa
有了第一篇的基础,咱们知道了 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
装饰函数的逻辑代码块。cdn
为何说 property 底层是基于描述符协议的呢?经过 PyCharm 点击进入 property 的源码,很惋惜,只是一份相似文档同样的伪源码,并无其具体的实现逻辑。blog
不过,从这份伪源码的魔法函数结构组成,能够大致知道其实现逻辑。文档
这里我本身经过模仿其函数结构,结合「描述符协议」来本身实现类 property
特性。
代码以下:
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 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 的实现原理,就交由你来本身完成。