繁体   English   中英

Python-装饰器收到单个arg的模棱两可

[英]Python - ambiguity with decorators receiving a single arg

我正在尝试编写一个装饰器,它得到一个arg,即

@Printer(1)
def f():
    print 3

因此,我天真地尝试了:

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

可以,但是它也可以用作不接受arg的装饰器,即

@Printer
def a():
   print 3

我该如何预防?

好吧,从某种意义上来说,已经有效地防止了调用a()工作。

但是要在定义函数时停止它,我想您必须更改__init__来检查num的类型:

def __init__(self,num):
    if callable(num):
        raise TypeError('Printer decorator takes an argument')
    self.__num=num

不过,我不知道这是否值得。 它已经不能按原样工作; 您实际上是在要求使用鸭子式语言来强制实参的类型。

您确定它没有参数就可以工作吗? 如果我将它们保留在外面,则会收到以下错误消息:

Traceback (most recent call last):
  File "/tmp/blah.py", line 28, in ?
    a()
TypeError: __call__() takes exactly 2 arguments (1 given)

但是,如果基于类的定义不适合您,则可以尝试使用该替代定义。

def Printer(num):
    def wrapper(func):
        def wrapped(*args, **kwargs):
            print num
            return func(*args, **kwargs)
        return wrapped

    return wrapper

装饰器是@运算后的表达式。 在第一种情况下,这是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"

在第二种情况下,这是打印机类,因此您有

decorator = Printer # the class

def a():
   print 3
a = decorator(a) # == Printer(a), so a is an instance of Printer

因此,即使它可以工作(因为Printer的构造函数需要一个额外的参数,就像__call__一样),却是完全不同的事情。

防止这种情况的python方法通常是:不要这样做。 弄清楚装饰器的工作原理(例如在文档字符串中),然后相信人们在做正确的事情。

如果您真的想要检查, Eevee的答案提供了一种捕获此错误的方法(当然,在运行时-这是Python)。

我想不出理想的答案,但是如果您强制使用关键字参数实例化Printer类,则它永远不会尝试通过装饰器本身实例化,因为它仅处理非关键字参数:

def __init__(self,**kwargs):
     self.__num=kwargs["num"]

...

@Printer(num=1)
def a():
    print 3

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM