在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的答案

  • 不要这样做
  • 说真的,别这样。Lisp和Ruby是更适合编写自定义语法的语言。使用其中一个。或者找到一个更干净的方法
  • 如果必须这样做,您需要的是动态范围变量,而不是词汇范围变量
  • Python没有动态作用域变量,但您可以模拟它。下面的示例通过创建全局绑定来模拟它,但在退出时恢复以前的值:

    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