簡體   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