Python闭包和替换周围的范围

时间:2012-01-11 20:31:27

标签: python closures

我知道在使用Groovy闭包时,我可以更改闭包上的delegate,因此可以在外部定义闭包内的函数调用。

我可以在Python中做类似的事吗?

具体来说,如果您使用以下代码:

def configure():
  build()

def wrap(function):
  def build():
    print 'build'

  function()

wrap(configure)

我希望打印'build'(仅对wrap()进行更改。)

一些注意事项:

我不想将函数传递给configure(),因为configure()可能会调用大量函数。

我也不想全局定义它们,因为,configure()可能会调用大量函数,我不想污染全局命名空间。 / p>

5 个答案:

答案 0 :(得分:5)

是否是一个很好的方法是有争议的,但这是一个不修改全局命名空间的解决方案。

def configure():
  build()

def wrap(f):
  import new
  def build():
    print 'build'

  new.function(f.func_code, locals(), f.func_name, f.func_defaults, f.func_closure)()

wrap(configure)

我在How to modify the local namespace in python

找到了它

答案 1 :(得分:3)

如果没有元编程,这是可行的。让configurebuild函数作为参数:

def default_build():
    print "default build"

def configure(build_func=None):
    build_func = build_func or default_build
    build_func()

def wrap(func):
    def build():
        print "wrap build"

    func(build)

wrap(configure)

这种方式明确表明configure函数的行为可以改变。

您可以使用configure所见的命名空间来做一些更像我理解的Groovy所做的事情:

def build():
    print "default build"

def configure():
    build()

def wrap(func):
    def _build():
        print "wrap build"

    old_build = func.func_globals['build']
    func.func_globals['build'] = _build
    func()
    func.func_globals['build'] = old_build

答案 2 :(得分:2)

如果您感到疯狂和精彩,请查看this article about dynamic scoping

基本上,我们的想法是修改函数的字节码(使用byteplay模块)并将所有非严格局部范围的引用替换为。为了说明基本概念(在Python-pseudocode中):

code = byteplay.extractcode(function)
newbytecode = []

for opcode, arg in code.code:
    if opcode in (NONLOCAL_CODES):
        opcode = LOCAL_EQUIVALENT

    newbytecode.append((opcode, arg))

code.code = newbytecode

return code.to_code()

它稍微复杂一点,但文章提供了一些很好的信息。

他还建议不要在生产中使用它。 :d

答案 3 :(得分:0)

一种方法是在build中将wrap声明为全球:

def configure():
  build()

def wrap(function):
  global build
  def build():
    print 'build'

  function()

wrap(configure)

但是,我不建议这样做,因为它会污染命名空间。

答案 4 :(得分:0)

您需要使用global语句,以便在全局范围内定义build(),请尝试以下操作:

def wrap(function):
  global build
  def build():
    print 'build'

  function()