今天在同事的推荐下,看了下扇贝的sea 代码, 没细看,突然看到了cached_property的代码,这个让我突然想到了我们内部爬虫框架的cached_property,当时我在写这部分代码的时候主要目的有点类似如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 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下面的方法,基本流程为:
1 2 3 >>> b = B()>>> b.a.random()>>> b.a.other_method()
当时设计的时候,并没有想到通过property这个将方法变成属性的方法,当时想我只需要初始化两次就行,如下:
1 2 3 4 5 >>> b = B()>>> a = b.a()>>> a.random()>>> a.other_method()
但是后来我就意识到我这种设计有点low,因为其他同事调用的时候可能不会这么使用,另外因为B下面还会有其他类似A这种东西,例如:
1 2 3 4 5 6 7 8 9 class B (object) : @cached_property def a (self) : return A() @cached_property def c (self) : return C()
难道每次使用的时候都拿到c这个实例吗,有点傻,所以参考了Flask的cached_property实现,改成了如上了流程.那么进入主题,cached_property到底干了什么事情?
先看Flask的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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的实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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的实现
1 2 3 4 5 class B (object) : @cached_property def b (self) : pass
第一步
学过Python的应该蛮清楚关于装饰器这个概念,当将cached_property加在b上时,就已经完成了cached_property类的实例化(看最后一个类版本的计算时间装饰器),那怎么传进去的呢?
1 2 3 4 5 6 7 8 9 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__
第二步
调用的时候怎么个过程?看Python Document
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 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__)
1 2 3 4 5 6 7 8 9 10 11 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
总结
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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()
剩下自行理解吧..
1 2 3 4 5 6 7 8 9 10 11 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