简体   繁体   English

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

[英]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.

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