# sentinel
_missing = object()
class locked_cached_property(object):
"""A decorator that converts a function into a lazy property. The
function wrapped is called the first time to retrieve the result
and then that calculated result is used the next time you access
the value. Works like the one in Werkzeug but has a lock for
thread safety.
"""
def __init__(self, func, name=None, doc=None):
self.__name__ = name or func.__name__
self.__module__ = func.__module__
self.__doc__ = doc or func.__doc__
self.func = func
self.lock = RLock()
def __get__(self, obj, type=None):
if obj is None:
return self
with self.lock: # 保证线程安全
value = obj.__dict__.get(self.__name__, _missing)
if value is _missing:
value = self.func(obj)
obj.__dict__[self.__name__] = value
return value
class _PackageBoundObject(object):
......
@locked_cached_property
def jinja_loader(self):
"""The Jinja loader for this package bound object.
.. versionadded:: 0.5
"""
if self.template_folder is not None:
return FileSystemLoader(os.path.join(self.root_path,
self.template_folder))
locked_cached_property
是一个非数据属性描述符类,在_PackageBoundObject
类中,使用locked_cached_property
对jinja_loader
方法进行了装饰,使得jinjia_loader
方法变成了属性描述符,这样就实现了通过obj.jinja_loader
就能间接调用jinja_loader
方法。- 在
locked_cached_property
类的__get__
方法中,利用了可重入锁(RLock)保证线程安全,首次调用获取到函数返回值后,将函数的返回值缓存到了所属对象的__dict__
中,这样下次调用obj.jinja_loader
时,因为locked_cached_property(jinja_loader)
是非数据属性描述符,所以会直接返回obj.__dict__['jinja_loader']
,减少开销。
为何要要使用obj.__dict__.get(self.__name__, _missing)
而不使用obj.__dict__.get(self.__name__, None)
?
避免被装饰函数返回值为None的情况