如何使用exec()将值传递给python函数中的变量



一个简单的问题:

exec("a=3")
print(a)
# This will print 3

如果我使用这个:

def func():
exec("a=3")
print(a)
func()
# NameError: name 'a' is not defined.

发生了什么?如何使用exec()在函数中为其赋值?

编辑:我发现一个问题有同样的问题,但仍然没有解决。

为什么要这样做?

我知道使用exec()是不好的,也是不安全的。但最近我试图解决一个OP的问题。我见过它。

Python知道几种作用域:模块global、函数local、nonlocal闭包、类主体。值得注意的是,范围解析是在字节码编译时静态定义的——最重要的是,名称是指本地/非本地还是全局范围都不能更改。

在这些作用域中,只有全局作用域可以保证其行为类似于dict,因此是可写的。本地/非本地作用域通常是不可写的,并且不能向其中添加新的变量

如果locals未传入,exec将写入全局作用域全局必须显式设置为其默认值globals()

def func():
exec("a='exec'", globals())  # access only global scope
print(a)
a = 'global'
func()  # prints exec

但是,一旦一个名称是函数的本地名称,exec就不能修改它

def func():
a = 'local'  # assignment makes name local
exec("a='exec global'", globals())
exec("a='exec locals'", globals(), locals())
print(a)
a = 'global'
func()  # prints local

虽然存在类似dict的本地/非本地作用域表示,但解释器不需要接受对其的更改。

locals((

更新并返回表示当前本地符号表的字典。当在函数块中调用locals((时,它会返回自由变量,但在类块中不会。请注意,在模块级别,locals((和globals((是同一个字典。

注意:此字典的内容不应修改更改可能不会影响解释器使用的局部变量和自由变量的值

尽管exec确实将局部作为dict,但它们并不像函数局部/非局部那样被处理。未定义修改默认局部变量的尝试(locals()的结果(。

exec((

如果给定全局变量和局部变量,则它们分别用于全局变量和本地变量。如果提供,局部可以是任何映射对象。请记住,在模块级别,全局变量和本地变量是同一个字典如果exec获得两个单独的对象作为全局对象和局部对象,则代码将像嵌入在类定义中一样执行

注意:默认的locals按照下面函数locals()的描述进行操作:不应尝试对默认locals字典进行修改。。。

在您的python3 REPL中执行help(exec),您将获得以下文档:

Help on built-in function exec in module builtins:
exec(source, globals=None, locals=None, /)
Execute the given source in the context of globals and locals.
The source may be a string representing one or more Python statements
or a code object as returned by compile().
The globals must be a dictionary and locals can be any mapping,
defaulting to the current globals and locals.
If only globals is given, locals defaults to it.

因此至少有两个选项可以提供参数"a"的值:

  1. 在模块的全局作用域中为变量"a"赋值:
a = 1
def func():
exec("global a;a=3")
print(a)
  1. 将自定义的全局或本地上下文传递给exec:
def func():
my_context = {'a': 1}
exec("a=3", None, my_context)
print(my_context['a'])

注意:除非你知道自己在做什么,否则不要在你的严重代码中使用evalexec


编辑注释

以下解决方案(评论中提到的第2个解决方案(不起作用:

def func():
a = 1
exec("a=3")
print(a) # still get 1 here

最新更新