我在python中创建了一个简单的事件系统,我发现每次触发事件的方式几乎都是相同的:无论是在通话结束时,还是在通话结束之前。感觉这对装饰工作者来说是件好事。这是我正在使用的代码:
from functools import wraps
def fires(event):
"""
Returns a decorater that causes an `Event` to fire immediately before the
decorated function is called
"""
def beforeDecorator(f):
"""Fires the event before the function executes"""
@wraps(f)
def wrapped(*args, **kargs):
event.fire(*args, **kargs)
return f(*args, **kargs)
return wrapped
def afterDecorator(f):
"""Fires the event after the function executes"""
@wraps(f)
def wrapped(*args, **kargs):
result = f(*args, **kargs)
event.fire(*args, **kargs)
return result
return wrapped
# Should allow more explicit `@fires(event).uponCompletion` and
# `@fires(event).whenCalled`
afterDecorator.onceComplete = afterDecorator
afterDecorator.whenCalled = afterDecorator
return afterDecorator
使用此代码,我可以成功写出:
@fires(myEvent)
def foo(y):
return y*y
print func(2)
一切正常。当我尝试写这个问题时,问题出现了:
@fires(myEvent).onceComplete
def foo(y):
return y*y
print func(2)
这给了我一个语法错误。复杂装饰器有一些特殊的语法吗?解析器在第一组括号后停止吗?
答案 0 :(得分:3)
根据grammar specification,不可能:
funcdef ::= [decorators] "def" funcname "(" [parameter_list] ")" ["->" expression] ":" suite decorators ::= decorator+ decorator ::= "@" dotted_name ["(" [argument_list [","]] ")"] NEWLINE dotted_name ::= identifier ("." identifier)* parameter_list ::= (defparameter ",")* ( "*" [parameter] ("," defparameter)* [, "**" parameter] | "**" parameter | defparameter [","] ) parameter ::= identifier [":" expression] defparameter ::= parameter ["=" expression] funcname ::= identifier
装饰者必须在末尾加上括号
答案 1 :(得分:2)
我添加了前后变量的预计算(由于调用技巧,所有闭包都是在导入时创建的,只是在应用装饰器时使用),使选择依赖于可选参数。 meta-decorator,并放入一个try / finally块,以确保您的事后总是触发。通过这种方法,功能属性的问题变得没有实际意义。
invoke = lambda f: f() # trick used in JavaScript frameworks all the time
@invoke # closure becomes fires
def fires():
def beforeDecorator(f, event):
"""Fires the event before the function executes"""
@wraps(f)
def wrapped(*args, **kargs):
event.fire(*args, **kargs)
return f(*args, **kargs)
return wrapped
def afterDecorator(f, event):
"""Fires the event after the function executes"""
@wraps(f)
def wrapped(*args, **kargs):
try:
result = f(*args, **kargs)
finally:
event.fire(*args, **kargs)
return result
return wrapped
def closure(event, after=False): # becomes fires
def decorator(function):
if after:
return afterDecorator(function, event)
else:
return beforeDecorator(function, event)
return decorator
return closure
答案 2 :(得分:1)
我不确定是否有办法获得你想要的语法,但这里有另一种选择。
只需在fires()
装饰器中添加一个额外的参数,以确定它是否应该在之前或之后发生:
def fires(event, before=True):
"""
Returns a decorater that causes an `Event` to fire immediately before or
after the decorated function is called
"""
if before:
def decorator(f):
"""Fires the event before the function executes"""
@wraps(f)
def wrapped(*args, **kargs):
event.fire(*args, **kargs)
return f(*args, **kargs)
return wrapped
else:
def decorator(f):
"""Fires the event after the function executes"""
@wraps(f)
def wrapped(*args, **kargs):
result = f(*args, **kargs)
event.fire(*args, **kargs)
return result
return wrapped
return decorator
然后像这样使用它:
@fires(myEvent, before=False) # or before=True, defaults to True
def foo(y):
return y*y