在Python中,我可以使用decorator来改变函数的局部作用域吗?
有没有任何方法可以编写一个装饰器,使下面的工作在Python中,我可以使用decorator来改变函数的局部作用域吗?,python,decorator,Python,Decorator,有没有任何方法可以编写一个装饰器,使下面的工作 assert 'z' not in globals() @my_decorator def func(x, y): print z 编辑:从anwser移动 回答hop的“为什么?”:语法为sugar/DRY 这不是关于缓存,而是基于x和y的值计算z(和z1,z2,z3,…) 我有很多函数做相关的事情,我不想写 z1, z2, z3=calculate_from(x, y) 在每个函数的开头,我都会在某个地方出错。如果这是c,我会使用
assert 'z' not in globals()
@my_decorator
def func(x, y):
print z
编辑:从anwser移动 回答hop的“为什么?”:语法为sugar/DRY 这不是关于缓存,而是基于x和y的值计算z(和z1,z2,z3,…) 我有很多函数做相关的事情,我不想写
z1, z2, z3=calculate_from(x, y)
在每个函数的开头,我都会在某个地方出错。如果这是c,我会使用cpp(如果这是lisp,我会使用宏…),但我想看看装饰程序是否也可以这样做
如果有帮助的话,我几乎肯定会将装饰器称为“precalculate_z”,它肯定不会成为任何公共API的一部分
使用类基础结构可能也会产生类似的效果,但我想看看使用原始函数是否可行。我不知道局部作用域,但您可以暂时提供一个可选的全局名称空间。比如:
import types
def my_decorator(fn):
def decorated(*args,**kw):
my_globals={}
my_globals.update(globals())
my_globals['z']='value of z'
call_fn=types.FunctionType(fn.func_code,my_globals)
return call_fn(*args,**kw)
return decorated
@my_decorator
def func(x, y):
print z
func(0,1)
它应该打印“z值”a)不要这样做
b) 说真的,你为什么要这么做
c) 您可以在decorator中将z声明为全局的,因此在第一次调用decorator之前,z不会在globals()中,因此断言不会发出声音
d) 为什么?我可能也可以从使用类基础结构中获得类似的效果,但我想看看它是否适用于原始函数
Python是一种面向对象的语言。在我看来,你应该在课堂上这样做。制作一个好的类接口肯定会简化您的问题。这不是装饰师的用武之地。我先重复一下“请不要”,但这是你的选择。这里有一个解决方案:
assert 'z' not in globals ()
class my_dec:
def __init__ (self, f):
self.f = f
def __call__ (self,x,y):
z = x+y
self.f(x,y,z)
@my_dec
def func (x,y,z):
print z
func (1,3)
它确实需要形式参数中的z
,但不需要实际参数。呼应Hop的答案
def添加了动态装饰器(f):
def更换(*arg,**karg):
#在全局中创建新的“z”绑定,保存以前的
如果globals()中的“z”:
oldZ=(globals()['z'],)
其他:
oldZ=无
尝试:
globals()['z']=无
#调用原始函数
res=f(*arg,**karg)
最后:
#恢复所有旧绑定
如果是oldZ:
globals()['z']=oldZ[0]
其他:
del(globals()['z'])
返回res
退换货
@添加_dynamic_z_decorator
定义函数(x,y):
打印z
def其他_递归(x):
全局z
打印“x=%s,z=%s%”(x,z)
递归(x+1)
打印“x=%s,z=%s%”(x,z)
@添加_dynamic_z_decorator
def递归(x=0):
全局z
z=x
如果x<3:
其他_递归(x)
打印“调用函数(1,2)”
func(1,2)
打印“调用递归()
递归()
我不保证上述代码的实用性或合理性。事实上,我保证它是疯狂的,除非你想让你的Python同龄人鞭笞你,否则你应该避免使用它
此代码类似于eduffy和John Montgomery的代码,但确保创建并正确还原“z”,就像局部变量一样——例如,请注意“other_recurse”如何能够看到“recurse”主体中指定的“z”的绑定 显式优于隐式 这够好吗
def provide_value(f):
f.foo = "Bar"
return f
@provide_value
def g(x):
print g.foo
(如果你真的想要邪恶,分配给f.func_globals似乎很有趣。)其他人给出了一些制作工作装饰器的方法,许多人建议不要这样做,因为它在风格上与普通python行为非常不同,这会让试图理解代码的人感到困惑
如果您需要重新计算很多东西,将它们组合在一个对象中是否有意义?在构造函数中计算z1…zN,然后使用这些值的函数可以访问作为实例一部分的预先计算的答案。+1:如果您想要一个具有“内存”或“缓存”的函数,请定义一个可调用对象,使用call_u方法。说真的,为什么要这样做?您需要CommonLisp及其扩展语言的能力。或许Ruby可以做到这一点。Python在很多方面都很好,但不是这样。如果在声明了全局z之后重新加载代码,断言将正确中断?这不会将my_globals的状态冻结为定义func时的全局状态吗?不,my_globals会在每次调用修饰时重新创建(修饰函数返回的实际函数)。这应该意味着你总是有最新的全球新闻。哦,对了,当然。看到但忽略了嵌套函数。不过,这说明了为什么应该避免这种情况。好吧,这只是一天结束时的一个相当标准的装饰,但是,是的,像这样与全局性的干扰通常应该避免。然而,我可以想象,在某些情况下,知道如何去做会很有用(即使你以后追求更好的方法)我不确定你想在这里完成什么-如果
z
既不是全局参数也不是函数参数,那么它从哪里来?或者你想让z
成为静态的?Python不支持C风格的静态变量,但通过将函数放入类中,您可以完成大致相同的事情。我相信他在问“my_decorator”是否可以将局部变量注入到它所修饰的函数体中。谢谢-这可以满足我的要求;但我肯定不会用它。但至少我的好奇心现在满足了!我还是想办法避开我的同事
def provide_value(f):
f.foo = "Bar"
return f
@provide_value
def g(x):
print g.foo