前置函数调用其他功能在Python
这是一个有点棘手,但我想要的东西,在一个简单的方法,就是:前置函数调用其他功能在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
异常或类似的情况。
这甚至可能吗?有更简单/更好的解决方案吗?
有几种不同的方法,你想做的事, 在“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。
如果作为装饰者使用,func总是不会变成无? “不是无”是写这个的正确方法,因为“无”是一个单身。 – 2012-07-08 13:48:29
这是因为'@ process'装饰器可以有可选的参数,这也是为什么我检查选项阵列中的'fail''。 – 2012-07-08 13:49:40