我可以使用装饰器来改变Python中函数的局部范围吗?

时间:2009-02-26 16:03:09

标签: python decorator

有没有办法编写装饰器,以便以下方法有效?

assert 'z' not in globals()

@my_decorator
def func(x, y):
   print z

编辑:从anwser移动

回答hop的“为什么?”:语法糖/ DRY。

这不是关于缓存,而是基于x&的值计算z(和z1,z2,z3,...)。收率

我有很多相关的功能,我不想写

z1, z2, z3=calculate_from(x, y)

在每个单一功能的开头 - 我会在某处弄错。如果这是c我会用cpp做这个(如果这是lisp,我会用宏做这个...),但我想看看装饰者是否可以做同样的事情。

如果它有帮助,我几乎肯定会将装饰器称为“precalculate_z”,它肯定不会成为任何公共API的一部分。

我可能也可以通过使用类基础结构获得类似的效果,但我想看看它是否可以用于原始函数。

7 个答案:

答案 0 :(得分:11)

回应Hop的回答

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

    http://codepad.org/6vAY8Leh

    def adds_dynamic_z_decorator(f):
      def replacement(*arg,**karg):
        # create a new 'z' binding in globals, saving previous
        if 'z' in globals():
          oldZ = (globals()['z'],)
        else:
          oldZ = None
        try:
          globals()['z'] = None
          #invoke the original function
          res = f(*arg, **karg)
        finally:
          #restore any old bindings
          if oldZ:
            globals()['z'] = oldZ[0]
          else:
            del(globals()['z'])
        return res
      return replacement
    
    @adds_dynamic_z_decorator
    def func(x,y):
      print z
    
    def other_recurse(x):
      global z
      print 'x=%s, z=%s' %(x,z)
      recurse(x+1)
      print 'x=%s, z=%s' %(x,z)
    
    @adds_dynamic_z_decorator
    def recurse(x=0):
      global z
      z = x
      if x < 3:
        other_recurse(x)
    
    print 'calling func(1,2)'
    func(1,2)
    
    print 'calling recurse()'
    recurse()
    

    我对上述代码的实用性或完整性不作任何保证。实际上,我保证 是疯狂的,你应该避免使用它,除非你想从你的Python对手那里鞭打。

    此代码类似于eduffy和John Montgomery的代码,但确保'z'被创建并正确恢复“就像”局部变量一样 - 例如,注意'other_recurse'如何能够看到绑定对于'递归'正文中指定的'z'。

答案 1 :(得分:8)

我不知道本地范围,但您可以暂时提供替代的全局名称空间。类似的东西:



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的值”

答案 2 :(得分:7)

a)不要这样做。

b)说真的,你为什么要这样做?

c)你可以在你的装饰器中声明z为全局,所以z在第一次调用装饰器之后才会出现在globals()中,所以断言不会吠叫。

d)为什么???

答案 3 :(得分:2)

我首先回应“请不要”,但这是你的选择。这是一个适合您的解决方案:

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,而不是实际参数。

答案 4 :(得分:1)

我也许可以通过使用类基础结构获得类似的效果,但我想看看它是否可以用于原始函数。

嗯,Python是一种面向对象的语言。在我看来,你应该在课堂上这样做。制作一个好的类接口肯定会简化你的问题。 不是是为什么装饰者制作的。

答案 5 :(得分:1)

明确比隐含更好。

这还不错吗?

def provide_value(f):
    f.foo = "Bar"
    return f

@provide_value
def g(x):
    print g.foo

(如果你真的想要邪恶,分配给f.func_globals似乎很有趣。)

答案 6 :(得分:0)

其他人已经提供了一些制作工作装饰器的方法,许多人建议不要这样做,因为它在风格上与普通的python行为不同,它会让任何试图理解代码的人感到困惑。

如果你需要重新计算一些东西,将它们组合在一个对象中是否有意义?在构造函数中计算z1 ... zN,然后使用这些值的函数可以作为实例的一部分访问预先计算的答案。