Python 2使用全局关键字和闭包注入模块范围

时间:2014-05-05 13:49:49

标签: python scope closures inject

tl; dr :是否可以将一个带有global关键字的函数注入到模块中,注入的global将关闭该模块? (怎么或为什么不?)


带示例的长版

Runtime injection of attributes into module namespace中建议注入模块的命名空间,可以使用setattr方法:

import foo

## before
try:
    foo.bar()
except AttributeError:
    print 'ok'

# expected to print 'ok'

## magic
def bar():
    pass

setattr(foo,'bar',bar)

## after
foo.bar()
# expected to do nothing (instead of raising an error)

然而,这似乎不适合人们对global关键字的期望。例如:

## foo.py
a = 4
def bar():
    global a
    a = 5
def check(expectation):
    global a
    assert a == expectation, "%s == %s"%(a,expectation)
    a = 4

## rab.py
import foo
foo.bar()
# expected to return and it does
foo.check(5)
print 'bar: ok'
def rab():
    global a
    a = 6
setattr(foo,'rab',rab)
foo.rab()
# may be expected to return but it raises AssertionError:
foo.check(6)
print 'rab: ok' # never printed

是否可以在rab运行foo.a6注入foo.check,同时保留原始foo.check

是否可以以注入的global将在该模块上关闭的方式向模块注入具有global关键字的函数? (怎么或为什么不?)

2 个答案:

答案 0 :(得分:3)

rab的全球词典已经出炉。您可以在rab.func_globals看到它。

该属性是只读的,因此您无法替换它

您可以在foo中创建新功能,然后将代码从rab推送到其中:)

type(rab)(rab.func_code, foo.__dict__)

例如

## rab.py
import foo
foo.bar()
# expected to return and it does
foo.check(5)
print 'bar: ok'
def rab():
    global a
    a = 6
foo.rab = type(rab)(rab.func_code, foo.__dict__)
foo.rab()
# may be expected to return but it raises AssertionError:
foo.check(6)
print 'rab: ok'

但不要这样做

答案 1 :(得分:0)

并非完全按照您考虑的方式,但您可以这样做:

def rab():
    foo.a = 6

foo.rab = rab  # No need to `setattr` in this simple example
foo.rab()
foo.check(6)

globals 总是引用函数定义的模块中的全局命名空间(没有办法改变这个)< SUP> 1 。

1 这实际上是它表现出来的唯一合理方式 - 毕竟,没有什么能阻止你将rab放入多个模块名称空间。功能