2013-02-25 69 views
4

在PEP 3107和this SO answer中暗示Python3K函數註釋和裝飾器適合手和手套 - 我應該能夠編寫一個可以使用函數屬性的裝飾器。有功能註釋的裝飾器

我不知道如何讓它們按照我的預期工作。

考慮:

def verbose(lcls): 
    def wrap(f): 
     print('inside wrap') 
     def wf(*args): 
      print('inside wf') 
      print('lcls in wf:',lcls) 
      print('locals in wf:',locals()) 
      print('wf annotations:',wf.__annotations__) 
      print('xYx annotations:',xXy.__annotations__) 
      r=f(*args) 
      print('after f(*args)') 
      return r 
     return wf 
    return wrap   

@verbose(locals())  
def xXy(x: 'x in x\'s', y: 'y in Y\'s') -> ('x times y','in x and y units'): 
    print('locals in xXy:',locals()) 
    return x*y 

xy=xXy(10,3)  
print(xy) 

打印:

inside wrap 
inside wf 
lcls in wf: {'xXy': <function verbose.<locals>.wrap.<locals>.wf at 0x109767ef0>, '__doc__': None, 'verbose': <function verbose at 0x109767050>, '__cached__': None, '__builtins__': <module 'builtins'>, '__package__': None, '__file__': '/private/var/folders/gx/gqtmx9mn7b75pk1gfy0m9w3w0000gp/T/Cleanup At Startup/test-383453350.857.txt', '__loader__': <_frozen_importlib.SourceFileLoader object at 0x10959ac10>, '__name__': '__main__'} 
locals in wf: {'f': <function xXy at 0x109767e60>, 'args': (10, 3), 'lcls': {'xXy': <function verbose.<locals>.wrap.<locals>.wf at 0x109767ef0>, '__doc__': None, 'verbose': <function verbose at 0x109767050>, '__cached__': None, '__builtins__': <module 'builtins'>, '__package__': None, '__file__': '/private/var/folders/gx/gqtmx9mn7b75pk1gfy0m9w3w0000gp/T/Cleanup At Startup/test-383453350.857.txt', '__loader__': <_frozen_importlib.SourceFileLoader object at 0x10959ac10>, '__name__': '__main__'}, 'wf': <function verbose.<locals>.wrap.<locals>.wf at 0x109767ef0>} 
wf annotations: {} 
xYx annotations: {} 
locals in xXy: {'y': 3, 'x': 10} 
after f(*args) 
30 

什麼該組線顯示的是,我不能看到如何訪問x和y的值XXY的裝飾或xXy的函數屬性。

我會要做的是1)有一個功能與註釋在PEP 3107指定,2)能夠有一個裝飾器,可以訪問函數的註釋和函數調用的值,而不只是是xXy函數簽名的克隆。

回答

4

版本3.3中的新功能,inspect.signature()將允許您在函數裝飾器中獲取所需的信息。下面是使用它打印在每次調用傳遞給裝飾功能以及訪問的參數名稱和值相關的註釋的一個例子:

import functools 
import inspect 

def verbose(wrapped): 
    @functools.wraps(wrapped) # optional - make wrapper look like wrapped 
    def wrapper(*args): 
     print('inside wrapper:') 
     fsig = inspect.signature(wrapped) 
     parameters = ', '.join('{}={}'.format(*pair) 
           for pair in zip(fsig.parameters, args)) 
     print(' wrapped call to {}({})'.format(wrapped.__name__, parameters)) 
     for parameter in fsig.parameters.values(): 
      print(" {} param's annotation: {!r}".format(parameter.name, 
                 parameter.annotation)) 
     result = wrapped(*args) 
     print(' returning {!r} with annotation: {!r}'.format(result, 
                 fsig.return_annotation)) 
     return result 
    return wrapper 

@verbose 
def xXy(x: 'x in X\'s', y: 'y in Y\'s') -> ('x times y','in X and Y units'): 
    return x*y 

xy = xXy(10, 3) 
print('xXy(10, 3) -> {!r}'.format(xy)) 

輸出:

inside wrapper: 
    wrapped call to xXy(x=10, y=3) 
    x param's annotation: "x in X's" 
    y param's annotation: "y in Y's" 
    returning 30 with annotation: ('x times y', 'in X and Y units') 
xXy(10, 3) -> 30 
+0

優秀!我正在玩檢查,但我還沒有弄明白。 – 2013-02-25 04:53:12

+0

只是在Py3.3中添加了'inspect.signature',所以對提問者很有用。它在Py 3.0-3.2中不可用。 – cfi 2013-02-25 16:52:04

+0

我正在使用3.3,所以這個工程太棒了!謝謝!! – 2013-02-25 18:16:42

4

我相信你正在尋找functools.wraps()

def verbose(lcls): 
    def wrap(f): 
     print('inside wrap') 
     @functools.wraps(f) 
     def wf(*args): 
      print('inside wf') 
      print('lcls in wf:',lcls) 
      print('locals in wf:',locals()) 
      print('wf annotations:',wf.__annotations__) 
      print('xYx annotations:',xXy.__annotations__) 
      r=f(*args) 
      print('after f(*args)') 
      return r 
     return wf 
    return wrap  

這是一個簡單的裝飾,以確保該包裝函數拿它換行功能的屬性。

+0

這解決了缺少的屬性(謝謝),但'wf'中'x'和'y'的值是可用的嗎?我可以在'wf'x = 10和y = 3之間打印嗎? – 2013-02-25 03:05:56

+0

@thewolf是的,只需從'args'中提取這些值或更改簽名即可接受這些特定的參數。 – 2013-02-25 03:07:59