2012-07-08 87 views
3

这是一个有点棘手,但我想要的东西,在一个简单的方法,就是:前置函数调用其他功能在Python

我有一个函数foo和装饰process

@process 
def foo(x, y): 
    print x + y 

该装饰是创建一个Process对象,它交到foo功能和它的参数

# The process decorator 
def process(func=None, **options): 
    if func != None: 
     def _call(*args, **kwargs): 
      if len(options) > 0 and 'fail' in options: 
       return Process(func, fail=options['fail'], *args, **kwargs) 
      else: 
       return Process(func, fail=None, *args, **kwargs) 
     return _call 
    else: 
     def _func(func): 
      return process(func, **options) 
     return _func 

我要到c在运行时更改给process-decorator的foo函数,以包含对另一个函数的调用,例如一个get_locals()功能,这可能会覆盖局部变量,在foo之初:

def foo(x, y): 
    get_locals() 
    print x + y 

我试图做一些与inspect,得到函数的源代码,然后编译成一个代码对象事后,但是这导致了'code' object is not callable异常或类似的情况。

这甚至可能吗?有更简单/更好的解决方案吗?

+1

如果作为装饰者使用,func总是不会变成无? “不是无”是写这个的正确方法,因为“无”是一个单身。 – 2012-07-08 13:48:29

+1

这是因为'@ process'装饰器可以有可选的参数,这也是为什么我检查选项阵列中的'fail''。 – 2012-07-08 13:49:40

回答

1

有几种不同的方法,你想做的事, 在“cleaninness”变化(相对于附加特殊的hackish), easinness做什么(和理解,后来维护),Python的 依赖实施等等。

我看到使用哪种方法的限制因子取决于 如何/想要干涉装饰的 函数本身的声明。

所以,如果你在一个位置 改变所有的代码,会利用你的装饰 的(或有文件给其他人将使用它),最直接, 简单,我想我会建议,方法是在运行时重新分配 该函数的默认参数元组(foo.func_defaults)。

这种方法要求要在运行 代码进行重写声明为函数的参数使用默认值的变量:

(我将使用一个简单的装饰比你用于记录的目的)

new_args = (10,20) 

def process(func): 
    def new_func(*args, **kw): 
     func_defaults = func.func_defaults 
     func.func_defaults = new_args 
     result = func(*args, **kw) 
     func.func_defaults = func_defaults 
     return result 
    return new_func 

@process 
def foo(x=1, y =2): 
    print x + y 

而且 “只是工程”(TM)

>>> foo() 
30 

2.另一种方法,仍然要求装饰函数遵循特定模式, 是声明所有要覆盖为全局变量的变量,并且在调用时创建一个新的函数对象,其中所有东西都等于原始函数 但改变它的全局词典。因为你为每个调用创建一个新的函数对象,所以这将是线程安全的-0然而,你不能改变被声明为函数参数的变量(因为Python将thos标记为“本地”),并且它们不能被随后标记作为“全球性”)

from types import FunctionType 

new_args = {"x":10, "y":20} 

def process(func): 
    def new_func(*args, **kw): 
     temp_func = FunctionType(func.func_code, 
           new_args, 
           func.func_name, 
           func.func_defaults, 
           func.func_closure) 
     return temp_func(*args, **kw) 
    return new_func 


@process 
def foo(): 
    global x, y 
    print x + y 

按照同样的方法,对于要被覆盖的每一个局部变量, 你会在函数的顶部将其列为接收默认值 - 像 在

def foo(x=1,y=2): 
    z= 3 
    w = 4 

而在运行时你会重新建立din在每次调用时不仅仅是函数对象 而且它的代码对象 - Yu可以调用types.CodeType与原始func.func_code对象具有相同的参数,但对于“常量”参数 - 将替换原始对象co_consts属性。

我不会举这个方法做事,因为它落在 “不错的实验黑客,但实际生产代码非常可怕”的领域 - 因为它太依赖于实现。

4.您可以在你的装饰,设置代码跟踪上 - 这通常是由debuger事业做,让你得到一个回调到您指定的每个表达式的功能将目标代码计算 - 你可以使用它们,启用“settrace”,并在你的回调函数中调用目标函数的局部变量。
这是apratnly简单,一旦你看到sys.gettrace文档 - http://docs.python.org/library/sys.html#sys.settrace - 但它确实hvae的小麻烦根本 :-) 这种情况工作,因为虽然你们的确有正确的读写在框架本地语言字典上,语言优化并不总是在使用它们时从该字典获取值(如果有的话)。 所以这个简单的例子显示了它不工作:

>>> import sys 
>>> def a(): 
... b = 1 
... f = sys._getframe() 
... print f.f_locals 
... f.f_locals["b"] = 2 
... print b 
... 
>>> a() 
{'b': 1, 'f': <frame object at 0x1038cd0>} 
1 

5.最后,你说你能做到准确,并使用Python的“DIS”模块和编译器的功能注入你的函数代码中的变量属性 - 直接在字节码上。你必须:反编译函数的代码对象,成功地干预字节码,以正确加载你想要的变量和所需的值,重新编译字节码,重新创建代码对象(使用前面提到的types.CodeType调用),传递适当的“常量”值来正确加载你的变量,重新创建函数对象并进行你的函数调用。

我不会列举出为什么我发现这种方法比其他方法更有用的原因,既不尝试参考实现。但是,如果正确完成,它至少可以用于cpython 2.x。

相关问题