我正在尝试从给定的输入变量创建一个字典。
我遇到了一个非常奇怪的行为,所以我开始调查。
我最终得出的结论是该函数有时引用局部变量,并且有时会搜索同名的全局变量。
更具体地说:
- 当我在循环中创建字典时,它指的是 全局 变量
- 它指的是 本地|| |当我逐一创建字典时的变量 当我在循环中创建元组列表时它指的是
- 本地 变量 当我在循环中创建元组列表时它指的是
- 本地 变量逐个创建元组列表 这里有一小段代码说明了这个问题:
打印输出:
x = 1
y = 2
z = 3
def func(x,y,z):
print {var:eval(var) for var in ['x','y','z']} # Uses globals
print {'x':eval('x'),'y':eval('y'),'z':eval('z')} # Uses locals
print [(var,eval(var)) for var in ['x','y','z']] # Uses locals
print [('x',eval('x')),('y',eval('y')),('z',eval('z'))] # Uses locals
func(4,5,6)
我在这里缺少什么?
{'y': 2, 'x': 1, 'z': 3}
{'y': 5, 'x': 4, 'z': 6}
[('x', 4), ('y', 5), ('z', 6)]
[('x', 4), ('y', 5), ('z', 6)]
What am I missing here?
观察到的行为与列表推导式 (list comprehension) 和字典推导式 (dictionary comprehension) 中的作用域规则有关。在 Python 中,列表推导式和字典推导式会创建自己的作用域。这意味着在推导式内部定义的变量在推导式外部是不可见的。
让我们来逐行分析的代码:
-
print {var:eval(var) for var in ['x','y','z']}
- 这里使用字典推导式来创建一个字典。
-
eval(var)
在没有提供全局或本地命名空间的情况下被调用,它会默认使用调用它的作用域。 -
在这种情况下,循环在
func
函数的作用域内运行,所以eval
会在func
函数的作用域中查找变量x
、y
和z
。 -
由于在
func
函数中定义了同名的局部变量,所以eval
会使用这些局部变量的值。
-
print {'x':eval('x'),'y':eval('y'),'z':eval('z')}
- 这里没有使用推导式,而是直接创建了一个字典。
-
和上面一样,
eval
函数会默认使用调用它的作用域。 -
所以,
eval('x')
、eval('y')
和eval('z')
会分别使用func
函数中定义的局部变量x
、y
和z
的值。
-
print [(var,eval(var)) for var in ['x','y','z']]
- 这里使用了列表推导式来创建一个列表。
- 和字典推导式一样,列表推导式也会创建自己的作用域。
-
所以在列表推导式内部,
eval(var)
会在列表推导式的作用域内查找变量。 -
由于在列表推导式的作用域内没有定义变量
x
、y
和z
,所以eval
会去外层作用域,也就是func
函数的作用域中查找,并使用func
函数中定义的局部变量的值。
-
print [('x',eval('x')),('y',eval('y')),('z',eval('z'))]
- 这里没有使用推导式,而是直接创建了一个列表。
-
和第 2 点一样,
eval
函数会默认使用调用它的作用域,也就是func
函数的作用域。 -
所以
eval('x')
、eval('y')
和eval('z')
会分别使用func
函数中定义的局部变量x
、y
和z
的值。
总的来说,观察到的行为是符合 Python 的作用域规则的。
重要提示:
使用
eval
函数通常是不安全的,因为它可以执行任意代码。如果需要从字符串中获取变量的值,建议使用字典或其他更安全的方法。