class memoized_property(object):
"""A read-only @property that is only evaluated once."""
def __init__(self, fget, doc=None):
self.fget = fget
self.__doc__ = doc or fget.__doc__
self.__name__ = fget.__name__
# 这个方法应该是这个缓存装饰器的关键
# 因此, 我组织关键字如下
# * python __get__
# * how python __get__ works
# # python descript tools
def __get__(self, obj, cls):
if obj is None:
return self
obj.__dict__[self.__name__] = result = self.fget(obj)
return result
def _reset(self, obj):
memoized_property.reset(obj, self.__name__)
@classmethod
def reset(cls, obj, name):
obj.__dict__.pop(name, None)
According to the implementation method of memoized_property, the following answers all have a premise, that is, it is assumed that it is used as a decorator for class functions. At this time, this class can be regarded as a modified version of the property decorator. The caching effect can be achieved because Python has priority when accessing attributes.
For
a.val
, Python performs the following processing:Access the object’s
__dict__
,即a.__dict__['val']
first;If there is no access to the class again,
A.__dict__['val']
will be searched along the inheritance relationship;If
method of the descriptor will be called;A.__dict__['val']
,返回的是值的话,那么就获得该值;如果返回的是一个描述器,则会调用描述器的__get__
is found and a value is returned, then the value will be obtained; if a descriptor is returned, thememoized_property
For theval
的时候,根据上面的查找顺序:对象里面没有,跳到第二步;在类的字典里发现了,但发现是描述器,因此会进入到描述器中的__get__
方法中。在这里,使用self.fget(obj)
调用装饰的val
函数并计算结果后,在返回结果的同时,将结果也存储在obj.__dict__['val']
中。下次再访问a.val
的时候,由于对象的__dict__
中有val
了,就会先查找obj.__dict__['val']
,而不会大动干戈的去找__get__
。这样就实现缓存一个属性的效果。而一般的__get__
是不会设置obj.__dict__['xxx']
When accessingval
for the first time, according to the above search sequence: it is not in the object, jump to the second step; it is found in the class dictionary, but it is found to be a descriptor, so it will be entered to the method in the descriptor. Here, useself.fget(obj)
to call the decoratedval
function and calculate the result. While returning the result, the result is also stored inobj. __dict__['val']
. The next time you visit, since there is
val
in the object's__dict__
, it will first search forobj.__dict__['val']
, and will not go to great lengths to find itreset
就很清楚了,只不过把上一个优先级的途径去掉。然后Python就不得不沿着优先级一步步找下去,发现__get__
可用,于是又在其中调用a.val
. This achieves the effect of caching an attribute. Generally,obj.__dict__['xxx']
is not set, so it is recalculated every time.After understanding this,
method in it and recalculated it again. 🎜 🎜And the interior of 🎜 can be said to be much better. . . . 🎜reset
is very clear, it just removes the previous priority path. Then Python had to search step by step along the priority and found that__get__
was available, so it called theA class method is a method that you can call directly without instantiating the class