And*_*rts 6 python decorator chaining
今天早上我有一个有趣的问题.我有一个基类看起来像这样:
# base.py
class Base(object):
@classmethod
def exists(cls, **kwargs):
# do some work
pass
Run Code Online (Sandbox Code Playgroud)
还有一个看起来像这样的装饰模块:
# caching.py
# actual caching decorator
def cached(ttl):
# complicated
def cached_model(ttl=300):
def closure(model_class):
# ...
# eventually:
exists_decorator = cached(ttl=ttl)
model_class.exists = exists_decorator(model_class.exists))
return model_class
return closure
Run Code Online (Sandbox Code Playgroud)
这是我的子类模型:
@cached_model(ttl=300)
class Model(Base):
pass
Run Code Online (Sandbox Code Playgroud)
事实上,当我实际上调用Model.exists时,我得到关于错误数量的参数的抱怨!检查装饰器中的参数显示没有任何奇怪的事情 - 参数正是我所期望的,并且它们与方法签名匹配.如何将其他装饰器添加到已经装饰的方法中classmethod?
并非所有模型都被缓存,但exists()方法作为类方法存在于每个模型上,因此重新排序装饰器不是一个选项:cached_model可以将类方法添加到exists(),但是什么使exists()成为类方法在未缓存的模型?
在Python中,当声明一个方法时,在一个函数体中,它就像一个函数 - 一旦解析并存在该类,就通过"."检索该方法.运算符将该函数(即时)转换为方法.此转换确实将第一个参数添加到方法中(如果它不是静态方法) -
所以:
>>> class A(object):
... def b(self):
... pass
...
>>> A.b is A.b
False
Run Code Online (Sandbox Code Playgroud)
因此,每次检索"A"的"b"属性都会产生"方法对象b"的不同实例.
>>> A.b
<unbound method A.b>
Run Code Online (Sandbox Code Playgroud)
如果有的话,可以在没有任何转换的情况下检索原始函数"b"
>>> A.__dict__["b"]
<function b at 0xe36230>
Run Code Online (Sandbox Code Playgroud)
对于以@classmethod相同的方式装饰的函数,当从A中检索时,值"class"被添加到参数列表中.
的@classmethod和@staticmethod装饰将包装在不同的描述符比正常instancemethod底层函数.一个类方法对象 - 一个函数在被包装时变成的classmethod是一个描述符对象,它有一个'__get__'方法,它将返回一个包装底层函数的函数 - 并在所有其他函数之前添加"cls"参数.
任何进一步的装饰器必须@classmethod"知道"它实际上是处理描述符对象,而不是函数.-
>>> class A(object):
... @classmethod
... def b(cls):
... print b
...
>>> A.__dict__["b"]
<classmethod object at 0xd97a28>
Run Code Online (Sandbox Code Playgroud)
因此,让@classmethod装饰器成为应用于方法的最后一个(堆栈中的第一个)要容易得多 - 这样其他装饰器就可以处理一个简单的函数(知道"cls"参数将会作为第一个插入).
据我所知,除了将方法绑定到类之外,在某些情况下,装饰器实际上还会在方法调用之前添加一个参数classmethod。class解决方案是编辑我的类装饰闭包:
def cached_model(ttl=300):
def closure(model_class):
# ...
# eventually:
exists_decorator = cached(ttl=ttl, cache_key=exists_cache_key)
model_class.exists = classmethod(exists_decorator(model_class.exists.im_func))
return model_class
return closure
Run Code Online (Sandbox Code Playgroud)
该im_func属性似乎获得了对原始函数的引用,这允许我使用缓存装饰器进入并装饰原始函数,然后将整个混乱包装在调用中classmethod。总之,classmethod装饰是不可堆叠的,因为参数似乎是被注入的。
| 归档时间: |
|
| 查看次数: |
1897 次 |
| 最近记录: |