我可以使用函数结果的属性作为修饰符吗?

2024-10-02 10:21:22 发布

您现在位置:Python中文网/ 问答频道 /正文

我用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)

这给了我一个语法错误。对于复杂的装饰器有一些特殊的语法吗?解析器在第一组括号之后停止吗?你知道吗


Tags: the代码eventreturndef事件argsfunction
3条回答

我不确定是否有一种方法可以得到你想要的语法,但这里有一个替代方法。你知道吗

只需向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

我添加了before和after变量的预计算(由于invoke技巧,所有闭包都是在导入时创建的,只要应用decorator就可以使用),使选择依赖于metadecorator的可选参数,并放入try/finally块以确保after事件始终触发。通过这种方法,函数属性的问题变得毫无意义。你知道吗

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

不,根据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

装饰者必须在结尾加上括号

相关问题 更多 >

    热门问题