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 - 装饰器的主要内容,如果未能解决你的问题,请参考以下文章