• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

cached_property的理解和使用

武飞扬头像
ox180x
帮助1

今天在同事的推荐下,看了下扇贝的sea代码,
没细看,突然看到了cached_property的代码,这个让我突然想到了我们内部爬虫框架cached_property,当时我在写这部分代码的时候主要目的有点类似如下:

12345678910111213
class A(object):    def __init__(self):        pass    def random(self):        return random.random()class B(object):    @cached_property    def a(self):        return A()

A为一个类,然后B有点类似A的一个超集,平常使用的使用为实例化B, 然后为了操作A下面的方法,基本流程为:

123
>>> b = B()>>> b.a.random()>>> b.a.other_method()

当时设计的时候,并没有想到通过property这个将方法变成属性的方法,当时想我只需要初始化两次就行,如下:

12345
>>> b = B()>>> a = b.a()>>> a.random()>>> a.other_method()

但是后来我就意识到我这种设计有点low,因为其他同事调用的时候可能不会这么使用,另外因为B下面还会有其他类似A这种东西,例如:

123456789
class B(object):    @cached_property    def a(self):        return A()    @cached_property    def c(self):        return C()

难道每次使用的时候都拿到c这个实例吗,有点傻,所以参考了Flaskcached_property实现,改成了如上了流程.那么进入主题,cached_property到底干了什么事情?

先看Flask的实现:

1234567891011121314151617181920212223242526272829303132
class _Missing(object):    def __repr__(self):        return 'no value'    def __reduce__(self):        return '_missing'_missing = _Missing()class cached_property(property):    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    def __set__(self, obj, value):        obj.__dict__[self.__name__] = value    def __get__(self, obj, type=None):        if obj is None:            return self        value = obj.__dict__.get(self.__name__, _missing)        if value is _missing:            value = self.func(obj)            obj.__dict__[self.__name__] = value        return value

再看sea的实现:

12345678910111213141516171819
class cached_property:    """ thread safe cached property """    def __init__(self, func, name=None):        self.func = func        self.__doc__ = getattr(func, '__doc__')        self.name = name or func.__name__        self.lock = Lock()    def __get__(self, instance, cls=None):        with self.lock:            if instance is None:                return self            try:                return instance.__dict__[self.name]            except KeyError:                res = instance.__dict__[self.name] = self.func(instance)                return res

唯一不同点应该就在于加了一个锁吧,那么抛去锁的部分,单纯讲cached_propery的实现

12345
class B(object):    @cached_property    def b(self):        pass

第一步

学过Python的应该蛮清楚关于装饰器这个概念,当将cached_property加在b上时,就已经完成了cached_property类的实例化(看最后一个类版本的计算时间装饰器),那怎么传进去的呢?

123456789
class cached_property:    """ thread safe cached property """    def __init__(self, func, name=None):        self.func = func # 这个func对应上面的就为b        self.__doc__ = getattr(func, '__doc__') # None        self.name = name or func.__name__ # func.__name__ 为b

第二步

调用的时候怎么个过程?看Python Document

123456789101112131415161718192021222324252627282930313233343536
class Property(object):    "Emulate PyProperty_Type() in Objects/descrobject.c"    def __init__(self, fget=None, fset=None, fdel=None, doc=None):        self.fget = fget        self.fset = fset        self.fdel = fdel        if doc is None and fget is not None:            doc = fget.__doc__        self.__doc__ = doc    def __get__(self, obj, objtype=None):        if obj is None:            return self        if self.fget is None:            raise AttributeError("unreadable attribute")        return self.fget(obj)    def __set__(self, obj, value):        if self.fset is None:            raise AttributeError("can't set attribute")        self.fset(obj, value)    def __delete__(self, obj):        if self.fdel is None:            raise AttributeError("can't delete attribute")        self.fdel(obj)    def getter(self, fget):        return type(self)(fget, self.fset, self.fdel, self.__doc__)    def setter(self, fset):        return type(self)(self.fget, fset, self.fdel, self.__doc__)    def deleter(self, fdel):        return type(self)(self.fget, self.fset, fdel, self.__doc__)
学新通
1234567891011
def __get__(self, instance, cls=None):        # instance代表实例化B        with self.lock:            if instance is None:                return self            try:                return instance.__dict__[self.name]            except KeyError:                 # 如果没有在B的__dict__找到self.name(即方法b), 那么self.func(即方法b)则会执行b(B), 并将其保存到实例化b的__dict__下                res = instance.__dict__[self.name] = self.func(instance)                return res

总结

123456789101112131415
class B(object):    @cached_property    def a(self):        return A()    @cached_property    def c(self):        return C()>>> b = B()>>> for i in range(3):        print(b.__dict__)        b.a.random()

剩下自行理解吧..

1234567891011
# 类版本的计算时间的装饰器class cal_time(object):    def __init__(self, func):        self.func = func    def __call__(self, *args, **kwargs):        s = time.time()        resp = self.func(*args, **kwargs)        e = time.time()        print("使用时长:{}".format(e - s))        return resp

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhggfajk
系列文章
更多 icon
同类精品
更多 icon
继续加载