2017-04-19 48 views
4

我想打印为my_func,并将源代码,由my_decorator包裹,如何获取由装饰器包装的函数的源代码?

import inspect 
from functools import wraps 

def my_decorator(some_function): 
    @wraps(some_function) 
    def wrapper(): 
     some_function() 

    return wrapper 

@my_decorator 
def my_func(): 
    print "supposed to return this instead!" 
    return 

print inspect.getsource(my_func) 

然而,对于包装返回源来代替:

@wraps(some_function) 
def wrapper(): 
    some_function() 

是否有它打印的一种方式取而代之?

def my_func(): 
    print "supposed to return this instead!" 
    return 

请注意,上述内容是从更大的程序中抽象出来的。当然,我们可以在这个例子中摆脱装饰器,但这不是我正在寻找的。

回答

6

在Python 2,@functools.wraps()装饰不设置方便__wrapped__属性,所述Python 3 version增加(新的Python 3.2)。

这意味着你将不得不采取从关闭提取原始功能。究竟在什么位置将取决于具体装饰的实现,但采摘的第一个函数对象应该是一个很好的概括:

from types import FunctionType 

def extract_wrapped(decorated): 
    closure = (c.cell_contents for c in decorated.__closure__) 
    return next((c for c in closure if isinstance(c, FunctionType)), None) 

使用方法:使用您的样品

print inspect.getsource(extract_wrapped(my_func)) 

演示:

>>> print inspect.getsource(extract_wrapped(my_func)) 
@my_decorator 
def my_func(): 
    print "supposed to return this instead!" 
    return 

另一种选择是更新functools库以为您添加__wrapped__属性,这与Python 3的相同方式如下:

import functools 

def add_wrapped(uw): 
    @functools.wraps(uw) 
    def update_wrapper(wrapper, wrapped, **kwargs): 
     wrapper = uw(wrapper, wrapped, **kwargs) 
     wrapper.__wrapped__ = wrapped 
     return wrapper 

functools.update_wrapper = add_wrapped(functools.update_wrapper) 

运行该代码输入你想要的装饰,看影响(所以他们最终使用的functools.update_wrapper()新版本)之前。 您必须手动解包静态(Python 2 inspect模块不会查找该属性);这是一个简单的帮手功能:

def unwrap(func): 
    while hasattr(func, '__wrapped__'): 
     func = func.__wrapped__ 
    return func 

这将打开任何级别的装饰环绕。或者使用inspect.unwrap() implementation from Python 3的副本,其中包括检查意外循环参考。

+0

对于Python 2,岂不也比较容易编写自己的'包装()'装饰者,d定义了“便利”属性?看起来这样的代码可能在Python 3中工作,即使没有必要(即它可以移植)? – martineau

+0

@martineau:提供您控制装饰器的源代码,当然可以轻松地更换'wrapps()'实现。你可能也可以修补'functools.update_wrapper'。 –

1

正如马亭皮特斯在他的回答中指出,Python的2 @functool.wraps()装饰没有定义__wrapped__属性,这会使得做你想做的事很容易的。根据我读到的documentation,虽然它是在Python 3.2中添加的,但是它在有些情况下可以处理,直到版本3.4被发布 - 因此下面的代码使用v3.4作为定义自定义的截断点wraps()装饰者。

因为从它的名字听起来像你有超过my_decorator()控制,您可以通过定义解决该问题,你自己wraps般的功能,而不是从封闭提取原有的功能,如在他的回答。以下是如何做到这一点(这在Python 2工程和3):

(如马亭也指出,可以通过覆盖functools.wraps模块属性,这将使改变猴子补丁的变化也影响到其他使用functools,而不是只在它定义的一个模块)

import functools 
import inspect 
import sys 

if sys.version_info[0:2] >= (3, 4): # Python v3.4+? 
    wraps = functools.wraps # built-in has __wrapped__ attribute 
else: 
    def wraps(wrapped, assigned=functools.WRAPPER_ASSIGNMENTS, 
       updated=functools.WRAPPER_UPDATES): 
     def wrapper(f): 
      f = functools.wraps(wrapped, assigned, updated)(f) 
      f.__wrapped__ = wrapped # set attribute missing in earlier versions 
      return f 
     return wrapper 

def my_decorator(some_function): 
    @wraps(some_function) 
    def wrapper(): 
     some_function() 

    return wrapper 

@my_decorator 
def my_func(): 
    print("supposed to return this instead!") 
    return 

print(inspect.getsource(my_func.__wrapped__)) 

输出:

@my_decorator 
def my_func(): 
    print("supposed to return this instead!") 
    return 
相关问题