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)
PHP中文网2017-04-18 10:35:42
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 A.__dict__['val']
,返回的是值的话,那么就获得该值;如果返回的是一个描述器,则会调用描述器的__get__
is found and a value is returned, then the value will be obtained; if a descriptor is returned, the
memoized_property
For the
For example, this class encapsulates the val function of class A:
class A(object):
...
@memoized_property
def val(self):
...
a = A()
a.val
val
的时候,根据上面的查找顺序:对象里面没有,跳到第二步;在类的字典里发现了,但发现是描述器,因此会进入到描述器中的__get__
方法中。在这里,使用self.fget(obj)
调用装饰的val
函数并计算结果后,在返回结果的同时,将结果也存储在obj.__dict__['val']
中。下次再访问a.val
的时候,由于对象的__dict__
中有val
了,就会先查找obj.__dict__['val']
,而不会大动干戈的去找__get__
。这样就实现缓存一个属性的效果。而一般的__get__
是不会设置obj.__dict__['xxx']
When accessing val
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, use self.fget(obj)
to call the decorated val
function and calculate the result. While returning the result, the result is also stored in obj. __dict__['val']
. The next time you visit , since there is val
in the object's __dict__
, it will first search for obj.__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, 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 the
PHP中文网2017-04-18 10:35:42
A class method is a method that you can call directly without instantiating the class