经过上面的讲解,我们已经知道如何定义描述符,且明白了描述符是如何工作的。

正常人所见过的描述符的用法就是上篇文章提到的那些,我想说的是那只是描述符协议最常见的应用之一,或许你还不知道,其实有很多 Python 的特性的底层实现机制都是基于 描述符协议 的,比如我们熟悉的@property@classmethod@staticmethodsuper 等。

如何实现property

先来说说 property 吧。

有了第一篇的基础,我们知道了 property 的基本用法。这里我直接切入主题,从第一篇的例子里精简了一下。

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 装饰函数的逻辑代码块。

为什么说 property 底层是基于描述符协议的呢?通过 PyCharm 点击进入 property 的源码,很可惜,只是一份类似文档一样的伪源码,并没有其具体的实现逻辑。

不过,从这份伪源码的魔法函数结构组成,可以大体知道其实现逻辑。

这里我自己通过模仿其函数结构,结合「描述符协议」来自己实现类 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]")

为了尽量让你少产生一点疑惑,我这里做两点说明:

  1. 使用TestProperty装饰后,math 不再是一个函数,而是TestProperty 类的一个实例。所以第二个math函数可以使用 math.setter 来装饰,本质是调用TestProperty.setter 来产生一个新的 TestProperty 实例赋值给第二个math

  2. 第一个 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 的运行原理有困难的同学,请务必参照我上面写的两点说明。如有其他疑问,可以加微信与我进行探讨。

如何实现staticmethod

说完了 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

同样的 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

讲完了 propertystaticmethodclassmethod 与 描述符的关系。我想你应该对描述符在 Python 中的应用有了更深的理解。对于 super 的实现原理,就交由你来自己完成。


最新文章

  1. 通过微软的cors类库,让ASP.NET Web API 支持 CORS
  2. OpenStack 的防火墙规则流程
  3. 简单的LR核心项集和Goto表填充演示程序
  4. hdu 2079 选课时间_母函数
  5. iOS开发——UITableView(未完,待续...)
  6. PHP 数组处理
  7. Centos搭建Docker环境
  8. APIO2010特别行动队
  9. Ubuntu安装Anaconda
  10. 关于如何在Visual Studio上仿真调试安卓的U3D应用
  11. Go接口interface
  12. docker系列(1)- 配置
  13. .Net Core---- 通过EPPlus批量导出
  14. Vusial Studio连接不到源代码管理器Vss
  15. 几种always块的形态
  16. Visual Studio Code配置JavaScript环境
  17. JS (function (window, document, undefined) {})(window, document)的真正含义
  18. js 倒计时 (时分秒版本)
  19. 如何安装Zend Studio 以及汉化和基本准备工作
  20. 定时检测Memcached进程是否存在,若不存在自动启动它

热门文章

  1. 主外键 设置 on update cascade 和on delete cascade 的区别
  2. git commit之后,撤销 commit
  3. 网格布局 GridLayout
  4. Dart函数方法
  5. Influxdb修改数据保留策略
  6. linux中导入sql文件
  7. 【PHP】 解决array_filter会过滤0 false的问题
  8. python中多层循环的一键退出
  9. Swift 4.0 Xib实现Button调整
  10. css代码陷阱