[英]Python - ambiguity with decorators receiving a single arg
I am trying to write a decorator that gets a single arg, ie 我正在尝试编写一个装饰器,它得到一个arg,即
@Printer(1)
def f():
print 3
So, naively, I tried: 因此,我天真地尝试了:
class Printer:
def __init__(self,num):
self.__num=num
def __call__(self,func):
def wrapped(*args,**kargs):
print self.__num
return func(*args,**kargs**)
return wrapped
This is ok, but it also works as a decorator receiving no args, ie 可以,但是它也可以用作不接受arg的装饰器,即
@Printer
def a():
print 3
How can I prevent that? 我该如何预防?
Well, it's already effectively prevented, in the sense that calling a()
doesn't work. 好吧,从某种意义上来说,已经有效地防止了调用a()
工作。
But to stop it as the function is defined, I suppose you'd have to change __init__
to check the type of num
: 但是要在定义函数时停止它,我想您必须更改__init__
来检查num
的类型:
def __init__(self,num):
if callable(num):
raise TypeError('Printer decorator takes an argument')
self.__num=num
I don't know if this is really worth the bother, though. 不过,我不知道这是否值得。 It already doesn't work as-is; 它已经不能按原样工作; you're really asking to enforce the types of arguments in a duck-typed language. 您实际上是在要求使用鸭子式语言来强制实参的类型。
Are you sure it works without arguments? 您确定它没有参数就可以工作吗? If I leave them out I get this error message: 如果我将它们保留在外面,则会收到以下错误消息:
Traceback (most recent call last): File "/tmp/blah.py", line 28, in ? a() TypeError: __call__() takes exactly 2 arguments (1 given)
You could try this alternative definition, though, if the class-based one doesn't work for you. 但是,如果基于类的定义不适合您,则可以尝试使用该替代定义。
def Printer(num):
def wrapper(func):
def wrapped(*args, **kwargs):
print num
return func(*args, **kwargs)
return wrapped
return wrapper
The decorator is whatever the expression after @
evaluates to. 装饰器是@
运算后的表达式。 In the first case, that's an instance of Printer
, so what happens is (pretty much) equivalent to 在第一种情况下,这是Printer
的一个实例,所以发生的事情(几乎)等同于
decorator = Printer(1) # an instance of Printer, the "1" is given to __init__
def f():
print 3
f = decorator(f) # == dec.__call__(f) , so in the end f is "wrapped"
In the second case, that's the class Printer, so you have 在第二种情况下,这是打印机类,因此您有
decorator = Printer # the class
def a():
print 3
a = decorator(a) # == Printer(a), so a is an instance of Printer
So, even though it works (because the constructor of Printer
takes one extra argument, just like __call__
), it's a totally different thing. 因此,即使它可以工作(因为Printer
的构造函数需要一个额外的参数,就像__call__
一样),却是完全不同的事情。
The python way of preventing this usually is: Don't do it. 防止这种情况的python方法通常是:不要这样做。 Make it clear (eg in the docstring) how the decorator works, and then trust that people do the right thing. 弄清楚装饰器的工作原理(例如在文档字符串中),然后相信人们在做正确的事情。
If you really want the check, Eevee's answer provides a way to catch this mistake (at runtime, of course---it's Python). 如果您真的想要检查, Eevee的答案提供了一种捕获此错误的方法(当然,在运行时-这是Python)。
I can't think of an ideal answer, but if you force the Printer class to be instantiated with a keyword argument, it can never try to instantiate via the decorator itself, since that only deals with non-keyword arguments: 我想不出理想的答案,但是如果您强制使用关键字参数实例化Printer类,则它永远不会尝试通过装饰器本身实例化,因为它仅处理非关键字参数:
def __init__(self,**kwargs):
self.__num=kwargs["num"]
... ...
@Printer(num=1)
def a():
print 3
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.