2011-05-28 14 views
2

請考慮以下代碼片段。當使用具有可調用對象的裝飾器時返回用戶定義的函數名稱

def print_timing(func): 
    import time 
    def wrapper(*args, **kwargs): 
     t1 = time.time() 
     res = func(*args, **kwargs) 
     t2 = time.time() 
     print '%s took %0.3f s ~ %0.0f min and %0.1f sec' % (func.func_name, t2-t1, int(t2 - t1)/60, (t2-t1) % 60) 
     return res 
    return wrapper 

@print_timing                  | 
def foo():                   | 
    return 'foo' 

class name(object): 
     def __init__(self, name): 
       self.name = name 
     @print_timing 
     def __call__(self): 
       return self.name 

bar = name("bar") 
print bar() 

這將返回:

__call__ took 0.000 s ~ 0 min and 0.0 sec 
bar 

目的bar表現得像稱爲bar函數,但與裝飾print_timing使用時暴露的__call__內部實現細節。有沒有一種方法(也許傳遞合適的參數傳遞給__init__功能)來改變name對象,因此它返回,而不是

bar took 0.000 s ~ 0 min and 0.0 sec 

?我想要一個解決方案,讓print_timing修飾器繼續使用普通功能。運行 print foo()

foo took 0.000 s ~ 0 min and 0.0 sec 
foo 
+0

「'name'」類(原文與大寫字母)不是正確的解決方法,不應該使用,也不應該將修飾器放在特殊的位置。不幸的是,我們不能說正確的方法,因爲我們不知道你真的想做什麼。 – ninjagecko 2011-05-30 19:39:49

回答

1

只要您只使用方法上的裝飾,他們將通過self作爲第一個參數:

def print_timing(func): 
    import time 
    def wrapper(*args, **kwargs): 
     t1 = time.time() 
     res = func(*args, **kwargs) 
     t2 = time.time() 
     funcname = func.__name__ 
     # Special case; a "name" instance has a "name" attribute we want to use instead. 
     if len(args) >= 1 and isinstance(args[0], name): 
      funcname = args[0].name 
     print '%s took %0.3f s ~ %0.0f min and %0.1f sec' % (funcname, t2-t1, int(t2 - t1)/60, (t2-t1) % 60) 
     return res 
    return wrapper 

更新:包裝程序現在使用func.__name__默認,但如果你使用這個在name類(如你原來的問題),它將使用實例的name屬性。

我已使用isinstance測試來確定name屬性將存在,​​但您可以使用鴨式輸入(if hasattr(args[0], 'name')); name變量非常通用,但是在任意類方法上使用時,最有可能會得到意想不到的結果。

+0

@Martijn:對不起,我沒有遵循你的觀點。你能詳細說明嗎? – 2011-05-28 21:35:45

+0

@Faheem'wrapper'是包裝函數;它獲得了與原始方法相同的參數,包括'self'。請記住,'self'只是按照慣例稱爲'self',只要它是該方法的** first **參數,就可以任意命名。因此,包裝器將'self'作爲第一個參數傳遞(在'* args'中),您可以像包裝器一樣訪問它。 'self'是你的'name'類的實例,它具有'name'屬性,這就是你想要打印的屬性。 – 2011-05-28 21:39:31

+0

@Martijn:我可以得到像這樣的工作方式,但只能用於函數對象。正如我在問題中提到的那樣,我希望'print_timing'裝飾器也可以使用常規函數。而'self = args [0]'不適用於函數。 – 2011-05-29 06:11:41

1

建立時發生的裝飾和實例是建立在__init__()調用發生。您需要讓裝飾器將該函數轉換爲描述符並讓該描述符從實例中獲取名稱。

+0

>感謝您的回覆。你能描繪一下這種方法如何工作嗎? – 2011-05-28 21:01:27

+0

描述符的['__get__()'](http://docs.python.org/reference/datamodel.html#implementing-descriptors)將綁定該方法,從該對象獲取名稱並將其分配給新的綁定方法,然後返回該方法。 – 2011-05-28 21:06:29

+0

經過一番搗鼓之後,仍然不確定你想要的是什麼。您的方法是否可以同時使用函數和函數對象?你能提供一個說明性的例子嗎?謝謝。 – 2011-05-29 11:49:24

1

使用@print_timing爲一類裝飾:

@print_timing 
class name(object): 
    ... 

沒有變化是必要的;你的包裝對象現在是一個函數,當它被認爲是一個類時,但是我從你的問題(和它是一個可調用的類)推斷出它並不重要(如果是這樣,你可以修改裝飾器到使返回的包裝對象「漂亮」)。

+0

@ninjagecko:我不確定你在這裏想到什麼,但是將它用作類裝飾器只是意味着返回類名,在這種情況下,是'name',所以輸出消息是'名字花費了0.000 s〜0分鐘和0.0秒'。 – 2011-05-29 05:54:36

+0

@Fareem:你爲什麼要做這個'Name'類?(旁註:類名通常是大寫字母) – ninjagecko 2011-05-29 20:21:54

+0

@ninjagecko:不知道我理解你的問題,但如果你的意思是,爲什麼一個類而不是一個函數,這是因爲我有兩個非常相似的函數(很多常見的代碼),我抽象出一些類似於函數的功能,然後將它傳遞給類__init__。所以,一個類代替兩個函數,本質上是。 – 2011-05-30 08:04:44

相關問題