我知道在使用Groovy闭包时,我可以更改闭包上的delegate
,因此可以在外部定义闭包内的函数调用。
我可以在Python中做类似的事吗?
具体来说,如果您使用以下代码:
def configure():
build()
def wrap(function):
def build():
print 'build'
function()
wrap(configure)
我希望打印'build'(仅对wrap()
进行更改。)
一些注意事项:
我不想将函数传递给configure()
,因为configure()
可能会调用大量函数。
我也不想全局定义它们,因为,configure()
可能会调用大量函数,我不想污染全局命名空间。 / p>
答案 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)
找到了它
答案 1 :(得分:3)
如果没有元编程,这是可行的。让configure
将build
函数作为参数:
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()