2013-02-25 67 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