2012-03-20 43 views
2

是否可以檢查一個函數/方法,看它是否可以用作裝飾器?在那裏它遵循裝飾器包裝其他功能並返回一個可調用的常用方法?具體來說,我期待驗證第三方代碼。如何查看方法是否爲裝飾器?

+3

你的意思是*可以用作裝飾器*或*用作裝飾器的某處*?雖然第一個可能非常困難,但如果不是不可能一般解決,第二個可能實際上是可行的。 – 2012-03-20 21:19:54

+1

或者你問函數是否被修飾? – forivall 2012-03-20 21:36:08

+0

@NiklasB。對不起 - 澄清了這個問題。 – 2012-03-20 22:04:42

回答

2

通過應用疑似裝飾,捕獲異常,然後測試結果是否包含__call__方法,可以產生一個猜測,一個給定的調用是一個裝飾或沒有。但這只是一個猜測,而不是一個保證。由於Python語言的動態類型化特性以及CPython解釋器中內置函數的特殊處理,除此之外,我不相信你所希望的一般可能。無法通過編程來判斷可調用對象是否將另一個可調用對象接受爲參數,或者返回值的類型是什麼類型。另外,在CPython中,對於在C中實現的函數,甚至不能檢查可調用函數以查看它接受的參數數量。

「裝飾者」一詞可以理解爲不同的意思。定義它的一種方法是,裝飾器是可接受單個(可調用)參數並返回可調用對象的任何可調用對象。

請注意,我甚至沒有在這個定義中使用「功能」一詞;這樣做實際上是不正確的。事實上,一些常用的裝飾有奇怪的特性:

  • 內置classmethodstaticmethod裝飾返回描述對象,而不是功能。
  • 由於語言版本2.6你可以裝飾類,而不僅僅是函數和方法。
  • 任何包含__init__(self, somecallable)方法和__call__(self, *args, **kwargs)方法的類都可以用作裝飾器。
+0

謝謝 - 你的聲明,一個裝飾器可以「可調用,接受一個可調參數並返回一個可調參數」將是我測試的基礎,應該足以進行驗證,因爲這就是他們應該在第三方代碼中創建。然而,從複雜性來看,我只是試着提供一些不錯的錯誤信息。 :d – 2012-03-22 12:17:26

0

我從來沒有做過這樣的事情,但一般來說python依賴於「鴨子打字」在這種情況下。所以你可以試着裝飾一個虛擬函數,看看是否可以返回一個可調用函數。

2

由於在Python中沒有標準化的裝飾器,所以沒有真正的方法來判斷一個函數是否是裝飾器,除非你知道你正在尋找的裝飾器的一些東西。

如果裝飾器在你的控制之下,你可以添加一個標記來表明它是一個裝飾的功能。否則,沒有真正的統一方式來做到這一點。藉此示例如:

def decorator(func): 
    return g 

@decorator 
def f() 
    pass 

def g(): 
    pass 

在上述例子中,在運行時,f和g將是相同的,並且沒有告訴兩個分開的方式。

0

不,這是不可能的。可能不是檢查f是否是裝飾器,你應該考慮爲什麼你需要檢查?

如果你期待一些具體的裝飾,可以直接查詢,如果你想要一些特定的行爲/方法/屬性,你可以檢查

如果你想檢查是否存在贖回f可以作爲裝飾,您可以通過傳遞一些虛函數來測試裝飾器行爲,但通常它可能無法正常工作,或者對於不同的輸入具有不同的行爲。

這裏是一個這樣的幼稚檢查:

def decorator1(func): 
    def _wrapper(*args, **kwargs): 
     print "before" 
     func(*args, **kwargs) 
     print "after" 

    return _wrapper 

def dummy_func(): pass 

out_func = decorator1(dummy_func) 

if callable(out_func) and dummy_func != out_func: 
    print "aha decorated!" 
+0

'你可以通過傳遞一些虛擬函數直接檢查'??不是真的......你不能說出它會用它的論點來做什麼! – katrielalex 2012-03-20 21:41:32

+0

@katrielalex是的,爲什麼一般答案是否定的,但如果你知道裝飾者應該做什麼,你可以通過使用它來檢查行爲 – 2012-03-20 21:44:19

1

任何可調用用的參數的權數可被用作裝飾。請記住,

@foo 
def bar(...): 

正是一樣

def bar(...): 
    ... 
bar = foo(bar) 

當然,因爲foo可以返回什麼,你有沒有檢查功能是否已裝修完畢與否的方式。雖然foo可能很好,並留下印記,但它沒有義務這樣做。


如果你給出一些Python代碼,並要找出所有裝飾的東西,你可以通過分析代碼轉換成抽象語法樹,然後遍歷樹尋找裝飾功能,這樣做。下面是一個例子,存儲裝飾器的.id。顯然,如果你願意,你可以存儲ast對象。

>>> class DecoratorFinder(ast.NodeVisitor): 
...  def __init__(self, *args, **kwargs): 
...   super(DecoratorFinder, self).__init__(*args, **kwargs) 
...   self.decorators = set() 
...  
...  def visit_FunctionDef(self, node): 
...   self.decorators.update(dec.id for dec in node.decorator_list) 
...   self.generic_visit(node) 
... 
>>> finder = DecoratorFinder() 
>>> x = ast.parse(""" 
... @dec 
... def foo(): 
...  pass 
... """) 
>>> finder.visit(x) 
>>> finder.decorators 
set(['dec']) 
+0

其實只有可能至少需要一個位置參數的可調用... – 2012-03-20 22:19:16

+0

@Niklas你當然是正確的=) – katrielalex 2012-03-20 22:20:47