sha*_*kra 4 python python-3.x python-decorators
我正在处理以asyncio特定的相对时间间隔调用的调度方法。我决定将调度集中到我编写的类的一种方法中,以减少我的项目逻辑出错的机会。
每次计划方法完成时都应调用此类方法。我虽然在loop.call_soon每个方法的末尾添加,但我决定试一试装饰器。
我写了一个类装饰器,然后将它应用于我的主类的一些方法,编写其余的逻辑等等。但是当我尝试在我的项目上测试我的更改时,我得到了一个例外:
AttributeError: 'function' object has no attribute '__self__'
Run Code Online (Sandbox Code Playgroud)
不知何故,装饰我的方法使它成为一个功能。这是我无法理解的事情,为什么会发生这种情况?如何在不放弃装饰器的情况下解决这个问题?
这是我正在尝试做的一个最小、完整且可验证的示例:
import asyncio
from datetime import datetime
class thinkagain:
loop = asyncio.get_event_loop()
def __init__(self, f):
self.fun = f
self.class_ = f.__self__
def __call__(self):
self.fun(*args, **kwords)
# everything in Python is an object
setattr(self.fun, "called", datetime.utcnow())
self.loop.call_later(self.class_.think, 5 * 60)
class DoSomething:
loop = asyncio.get_event_loop()
@thinkagain
def think(self):
attr = getattr(self.dosomething, "called")
if attr:
elapsed = attr - datetime.utcnow()
seconds = elapsed.seconds
else:
seconds = 99999
if seconds >= 20 * 60:
self.loop.call_soon(self.dosomething)
@thinkagain
def dosomething(self):
print("I did something awesome!")
loop = asyncio.get_event_loop()
something = DoSomething()
loop.call_soon(something.think)
loop.run_forever()
Run Code Online (Sandbox Code Playgroud)
这是我得到的例外:
Python 3.5.1 (default, Dec 7 2015, 13:41:59)
[GCC 5.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/tmp/mcve.py", line 19, in <module>
class DoSomething:
File "/tmp/mcve.py", line 22, in DoSomething
@thinkagain
File "/tmp/mcve.py", line 10, in __init__
self.class_ = f.__self__
AttributeError: 'function' object has no attribute '__self__'
>>>
Run Code Online (Sandbox Code Playgroud)
关于装饰器,Graham Dumpleton发表了精彩的演讲 《创建装饰器的高级方法》,讨论了各种装饰风格和技术的内部实现。强烈推荐。
他最后介绍的相关模块:https : //github.com/GrahamDumpleton/wrapt
尽管如此,我用两个版本修改了您的示例。下面的版本按照您的意图将属性直接存储在方法中。
from datetime import datetime
class thinkagain:
def __init__(self, f):
# Plain function as argument to be decorated
self.func = f
def __get__(self, instance, owner):
self.instance_ = instance
return self.__call__
def __call__(self, *args, **kwargs):
"""Invoked on every call of any decorated method"""
# set attribute directly within bound method
bound_method = getattr(self.instance_, self.func.__name__)
bound_method.__dict__['called'] = datetime.utcnow()
# returning original function with class' instance as self
return self.func(self.instance_, *args, **kwargs)
class DoSomething_A:
@thinkagain
def think(self, *args, **kwargs):
print('\n%s' % locals())
print(self.think.called, args, kwargs)
self.dosomething()
@thinkagain
def dosomething(self):
print('%s\n' % ('-'*30), locals())
print("%s I did something awful" % self.dosomething.called)
Run Code Online (Sandbox Code Playgroud)
第二个版本看起来更简洁,它跳过在方法中存储属性并直接在实例中分配它们。
from datetime import datetime
class thinkagain:
def __init__(self, f):
# Plain function as argument to be decorated
self.func = f
def __get__(self, instance, owner):
self.instance_ = instance
return self.__call__
def __call__(self, *args, **kwargs):
"""Invoked on every call of decorated method"""
# set attribute on instance
name = '%s_called' % self.func.__name__
setattr(self.instance_, name, datetime.utcnow())
# returning original function with class' instance as self
return self.func(self.instance_, *args, **kwargs)
class DoSomething_B:
@thinkagain
def think(self, *args, **kwargs):
print('\n%s' % locals())
print(self.think_called)
self.dosomething()
@thinkagain
def dosomething(self):
print('%s\n' % ('-'*30), locals())
print(self.dosomething_called)
Run Code Online (Sandbox Code Playgroud)
两者都会产生相同的期望行为:
>>> something = DoSomething_A().think(1, 2)
{'args': (1, 2), 'kwargs': {}, 'self': <__main__.DoSomething_A object at 0x10209f128>}
2015-12-26 04:13:25.629887 (1, 2) {}
------------------------------
{'self': <__main__.DoSomething_A object at 0x10209f128>}
2015-12-26 04:13:25.647476 I did something awful
Run Code Online (Sandbox Code Playgroud)
和
>>> something = DoSomething_B().think('arg_a', 'arg_b')
{'args': ('arg_a', 'arg_b'), 'kwargs': {}, 'self': <__main__.DoSomething_B object at 0x10209f208>}
2015-12-26 04:13:25.648039
------------------------------
{'self': <__main__.DoSomething_B object at 0x10209f208>}
2015-12-26 04:13:25.648390
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
7874 次 |
| 最近记录: |