在装饰器中,如何将我正在装饰的函数作为参数传递?

In a decorator how can I pass the function I'm decorating as an argument?

我制作了这个简单的装饰器,它基本上将装饰函数放在 try...except

from functools import wraps
def try_except(on_exception=None, exception=Exception, *args, **kwargs):
    from sys import stderr
    def decorator(func):
        @wraps(func)
        def wrapper(*args1, **kwargs1):
            try:
                return func(*args1, **kwargs1)
            except exception as e:
                print(repr(e), file=stderr)
            if on_exception is not None:
                return on_exception(*args, **kwargs)
        return wrapper
    return decorator

然后,我尝试修饰下面的函数,所以它在引发 ValueError:

时调用自身
@try_except(get_int, ValueError, "Please enter a valid integer!\n>>> ")
def get_int(prompt=">>> "):
    return int(input(prompt))

但我收到以下错误:

Traceback (most recent call last):
  File "C:\Python\decorator_test.py", line 9348234, in <module>
    @try_except(get_int, ValueError, "Please enter a valid integer!\n>>> ")
NameError: name 'get_int' is not defined

我知道我可以将它与函数中的 try...except 放在 while 循环中,但我这样做是为了学习练习。有什么办法可以避免这种情况发生吗?

解决此问题的最佳方法是使用调用该函数的函数。

def _get_int(prompt):
    get_int(prompt)
@try_except(_get_int, ValueError, "Please enter a valid integer!\n>>> ")
def get_int(prompt=">>> "):
    return int(input(prompt))
del _get_int

# Or even a lambda

@try_except(lambda p: get_int(p), ValueError, "Please enter a valid integer!\n>>> ")
def get_int(prompt=">>> "):
    return int(input(prompt))

因为_get_int returns what get_int returns 在调用的时候(也就是运行时),会变成现在的get_int .

你可能认为没有 @ 语法糖你也能做到,但那只会调用前面的(未修饰的)函数,所以它不会递归。

一种方法是定义和使用 sentinel 对象来表示“调用正在执行的相同 decorated 函数。即,在def try_except,添加:

same = object()

并且在包装器的主体中,在 try/except 之后:

        if on_exception is not None:
            if on_exception is same:
                return decorator(func)(*args, **kwargs)
            else:
                return on_exception(*args, **kwargs)

然后,装饰函数将是例如 (Python 3,我假设,考虑到您使用 input 的方式 - 毫无疑问是 raw_input in Python 2)...:[=​​18=]

@try_except(same, ValueError, "Please enter a valid integer!\n>>> ")
def get_int(prompt=">>> "):
    return int(input(prompt))