在类方法中嵌套函数调用locals()
这里有一段代码:
class Foo(object):
def bar(self, x):
def baz(y):
print locals()['self']
..
return baz(..)
foo = Foo()
foo.bar(..)
我有两个盒子。在Windows Vista上,用Python 2.5调用foo.bar(..)
是可以正常工作的。但在Ubuntu上,用Python 2.7调用foo.bar(..)
时却出现了KeyError
错误,因为在本地的字典中找不到self
。
有没有什么想法?
补充说明:我需要道歉;在尝试描述问题时,我似乎误导了你们。实际上,嵌套函数中的代码是在处理来自DSL的字符串:
r = re.compile(r'\$(\w+)')
eval_string = r.sub(r'self.player.stats["\1"]', s)
result = eval(eval_string)
在Vista/Python 2.5上运行正常,但在Ubuntu/Python 2.7上失败。
4 个回答
局部变量总是在当前调用的函数内部定义的变量,而全局变量总是在程序最上面定义的变量。在bar
函数中,self是一个局部变量。在baz
函数中,self的情况有点复杂,它介于局部变量和全局变量之间。你仍然可以引用它,因为Python会在更高的作用域中查找这个变量,因为它在局部作用域中没有定义,但你无法访问这些作用域的字典。
我在使用2.6.6版本时,遇到了和你在2.7中报告的相同情况。不过,我在2.5.2和2.4.6版本中也遇到了这个问题。(都是在Linux系统上。)
有趣的是:
>>> class Foo(object):
def bar(self, x):
def baz(y):
print self.__class__.__name__
print locals()['self']
return baz(4)
...
>>> Foo().bar(3)
Foo
<__main__.Foo object at 0x9801f4c>
>>>
可以推测,可能对计算本地符号表的过程进行了某种优化或修改,这样只有在特定函数范围内实际使用的名称才会被添加到符号表中。即使某个符号在被明确提及时是可以访问的(并且不会是全局的),它也不会被加入。
如果把bar()的最后一行改成return baz
(而不是return baz(4)
),我们可以看到self
并不是一个局部变量;它是一个自由变量:
>>> baz = Foo().bar(4)
>>> baz.func_code.co_nlocals, f.func_code.co_varnames, f.func_code.co_freevars
(1, ('y',), ('self',))
不过,即使在这里,x
也没有被算作自由变量,因为当名称没有在内部函数中使用时,它不会被算作自由变量。
不过,这个解释让你在Vista上使用2.5时描述的行为听起来像是一个bug。
如果你承认在运行坏代码之前可以计算出变量字典,那你可以使用这个:
class Foo(object):
def bar(self, x):
outer_locals = locals()
def baz(y):
print outer_locals['self']
..
return baz(..)
但是,如果你必须在运行时(也就是在bad
函数里面)计算这个字典,那就应该这样做:
import inspect
def outer_locals(depth=0):
"""
With depth=0 behaves like locals(), depth=1 are the locals of the
containing frame, depth=2 the locals of the frame containing the containing
frame and so on...
"""
return inspect.getouterframes(inspect.currentframe())[depth+1][0].f_locals
class Foo(object):
def bar(self, x):
def baz(y):
print outer_locals(1)
return baz(...)
注意,如果在baz
函数中没有覆盖的话,bar
函数里的所有局部变量在bad
函数中都是可以用的:
class Foo(object):
def bar(self, x):
def baz(y):
print self
..
return baz(..)