这篇通过Django源码中的cached_property来看下Python中一个很重要的概念——Descriptor(描述器)的使用。想必通过实际代码来看能让人对其用法更有体会。
什么是Descriptor?
Descriptor是Python中定义的一个协议,协议的内容是只要你定义的这个类(对象)具有: __get__, __set__, __delete__ 方法中的任意一个你这个类(对象)就叫做Descriptor。那么Descriptor是做什么用的呢?简单来说它是用来拦截属性访问的。简单说法你不能理解的话,下面这句话应该能理解:
Descriptors are a powerful, general purpose protocol. They are the mechanism behind properties, methods, static methods, class methods, and super()。 翻译:Descriptor是强大且通用的协议。它是Python中的属性,方法,静态访问,类方法和super关键字的实现机理。
你可以打开你的shell,随便定义一个方法func,然后查看它有哪些属性: dir(func) ,会发现上面说的那个 __get__ 。
更多的协议的细节可以看文章最后给出的链接。下面来看下这个Descriptor在Django中是怎么被使用的。
Django中的cached_property
在Django项目的utils/functional.py中这么一个类:cached_property。从名字上可以看出,它的作用是属性缓存。在看这个之前,先来看下在python中property这个关键字的应用场景:
import datetime class User(object): birth_year = 1988 @property def age(self): return datetime.date.today().year - self.birth_year
import datetime class my_property(object): def __init__(self, func): self.func = func # 保存原来的age方法 def __get__(self, instance, klass=None): print instance, klass return self.func(instance) # 调用原方法 class User(object): birth_year = 1988 @my_property def age(self): return datetime.date.today().year - self.birth_year user = User() print user.age
cached_property代码
理解了上面的例子在来看Django中的这个cached_property代码就容易多了。上面的property虽然是成功了添加了一个age的属性,但是每次调用这个属性都得再次计算,如果方法中的计算量比较大或者执行操作比较复杂的话,那效率岂不是很慢。因此就需要有cached这样的东西了。
这个东西的原理就是,既然你已经计算完了,那么就把它的结果直接塞到你的实例对象中去吧。它的代码是这样的:
class cached_property(object): """ Decorator that converts a method with a single self argument into a property cached on the instance. """ def __init__(self, func): self.func = func def __get__(self, instance, type=None): print instance if instance is None: return self # 关键点,直接通过内置的__dict__把属性塞回去。 res = instance.__dict__[self.func.__name__] = self.func(instance) return res
import datetime class User(object): birth_year = 1988 @cached_property def age(self): return datetime.date.today().year - self.birth_year user = User() print user.age print user.age print user.age
总结
上面仅仅是对__get__的简单应用。关于这个Descriptor更详细的介绍推荐大家看看官方文档或者翻译的文档。
参考:
http://pyzh.readthedocs.org/en/latest/Descriptor-HOW-TO-Guide.htmlhttps://docs.python.org/2/howto/descriptor.html
转载请注明:爱开源 » Python的Descriptor在Django中的使用