在装饰器中,我怎样才能将我正在装饰的函数作为参数传递?

时间:2015-02-22 17:06:30

标签: python decorator

我制作了这个简单的装饰器,基本上将装饰的功能放在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

我知道我可以把它放在函数中while的{​​{1}}循环中,但我这样做是为了学习练习。有没有办法让这件事发生?

2 个答案:

答案 0 :(得分:1)

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

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会返回get_int在通话时返回的内容(即运行时),因此会更改为get_int当前的内容。

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

答案 1 :(得分:1)

一种方法是定义并使用 sentinel 对象来表示&#34;调用现在正在执行的相同修饰函数。即,在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的方式 - 毫无疑问在Python 2中是raw_input)...:

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