Python - 装饰器

Posted

技术标签:

【中文标题】Python - 装饰器【英文标题】:Python - Decorators 【发布时间】:2014-01-23 14:02:54 【问题描述】:

我正在努力学习 Decorators 。 我理解了它的概念,现在正在尝试实现它。

这是我写的代码 代码是不言自明的。它只是检查参数是否传入int

def wrapper(func):
    def inner():
        if issubclass(x,int): pass
        else: return 'invalid values'

    return inner()

@wrapper
def add(x,y):
    return x+y

print add('a',2)

global name 'x' is not defined 会抛出错误。我知道它没有在inner 下定义,但不知道如何纠正此代码?我哪里错了?

【问题讨论】:

【参考方案1】:

你的装饰器应该是这样的:

def wrapper(func):
    def inner(x, y): # inner function needs parameters
        if issubclass(type(x), int): # maybe you looked for isinstance?
            return func(x, y) # call the wrapped function
        else: 
            return 'invalid values'
    return inner # return the inner function (don't call it)

需要注意的几点:

issubclass 需要一个类作为第一个参数(你可以用一个简单的 try/except TypeError 替换它)。 包装器应该返回一个函数,而不是被调用函数的结果 您实际上应该在内部函数中调用包装函数 您的内部函数没有参数

你可以找到关于装饰器的很好的解释here。

【讨论】:

如果不使用isinstance(),我会使用type(x) 而不是x.__class__ 感谢您更正代码。我怀疑包装函数的概念。是否真的需要编写一个嵌套函数来检查值是否为 int。我的意思是如果不使用嵌套函数就无法完成。例如:-def wrapper(x,y): if issubclass(x.__class__, int): return func(x, y) else: return 'invalid values' @MartijnPieters 感谢您的评论;使用type(x) 优于x.__class__。修好了。 @python-coder 不,那行不通。当你使用wrapper 作为装饰器时,它会被包装的函数作为第一个参数调用。 为了更加向上兼容,您可以使用灵活的参数列表:def inner(x, *args, **kwargs):return func(x, *args, **kwargs)【参考方案2】:

我发现您当前的代码存在三个问题。

首先,您调用的是inner 函数,而不是返回对它的引用。

其次,您的 inner 函数不采用与您正在装饰的函数相同的参数。在这种情况下,您至少需要显式使用 x 参数(某些内部函数可以独占使用 *args**kwargs,但显然不是您的)。

最后,你永远不会调用被包装的函数。虽然这不是严格要求的(在开发过程中用装饰器替换方法可能很有用),但通常您希望在内部函数代码的某个时间点调用该函数。

所以,为了把整个事情包装在一起,我认为你希望你的代码是这样的:

def wrapper(func):
    def inner(x, y):
        if issubclass(x, int): # issue 2
            return func(x, y) # issue 3
        else:
            return "invalid values" # consider raising an exception here instead!

    return inner # issue 1

【讨论】:

感谢您更正代码。我怀疑包装函数的概念。是否真的需要编写一个嵌套函数来检查值是否为 int。我的意思是如果不使用嵌套函数就无法完成。例如:-def wrapper(x,y): if issubclass(x.__class__, int): return func(x, y) else: return 'invalid values' 嵌套函数是使其作为装饰器工作所必需的。如果您希望 yoru 代码的用户直接调用 wrapper,而不是通过其通常的名称调用 func(尽管您需要直接在 wrapper 中命名),那么您的替代版本可以工作。请注意,您对函数的命名有点不寻常。通常,外部函数以装饰器的用途命名(例如arg_type_check 或其他东西),wrapper 是装饰器返回的内部函数的名称,“包裹”在原始函数周围。【参考方案3】:

这个怎么样。

def wrapper(func):
    def inner():
        if isinstance(func,int):
                return func(x, y)
        else: return 'invalid values'

    return inner()

【讨论】:

这很糟糕。 wrapper 应该返回一个函数才能用作装饰器。您的解决方案返回调用inner结果,即。 e.您将返回调用 func 或字符串 ('invalue values') 的结果。也不是函数。【参考方案4】:

这可能有效。

def wrapper(func):
    def inner(*args,**kwargs):
        if ((args[0] is int) and (args[1] is int)): pass
        else: return 'invalid values'
    return inner
@wrapper
def add(x,y):
    return x+y
print add('a',2)

【讨论】:

【参考方案5】:

如果您想在类型检查失败时正确终止 add 方法,您也可以引发异常。像这样

def check_int_types(func):
    def type_checker(x, y):
        if issubclass(type(x), int) and issubclass(type(y), int):
            return func(x, y)
        raise Exception("Invalid types: , ".format(x, y))
    return type_checker

@check_int_types
def add(a, b):
    return a + b

def main():
    x = y = 15.0
    print add(x, y)

if __name__ == '__main__':
    main()

结果:

Traceback (most recent call last):
  File "deco_test.py", line 17, in <module>
    main()
  File "deco_test.py", line 14, in main
    print add(x, y)
  File "deco_test.py", line 5, in type_checker
    raise Exception("Invalid types: , ".format(x, y))
Exception: Invalid types: 15.0, 15.0

【讨论】:

以上是关于Python - 装饰器的主要内容,如果未能解决你的问题,请参考以下文章

python 装饰器:装饰器实例类装饰器(装饰函数)

python 装饰器:装饰器实例内置装饰器

python 装饰器:装饰器实例内置装饰器

Python 装饰器和装饰器模式有啥区别?

python 装饰器:装饰器基础装饰器形式,何时执行

理解Python装饰器