全局语句似乎不适用于 ipython3 上的脚本

Global statement seems not to work with scripts on ipython3

我有以下脚本

test.py:

a=1

def f() :
    global a
    a=2

当我在 ipython3 中 运行 这个脚本时,我得到以下结果:

In [1]: a
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-3f786850e387> in <module>
----> 1 a

NameError: name 'a' is not defined

In [2]: run test

In [3]: a
Out[3]: 1

In [4]: f()

In [5]: a
Out[5]: 1

既然我把a声明为一个全局变量,为什么我运行函数f()时它的值没有变成2?

TLDR:run 模块的命名空间 copied 到 IPython 命名空间,而不是 shared.到 f() 为 运行 时,命名空间不再同步。


当 IPython 通过 run 执行模块时,它会在新的命名空间中执行;完成后,此命名空间的元素将复制到主 IPython 命名空间。

Built-in magic commands %run

[...]
The file is executed in a namespace initially consisting only of __name__=='__main__' and sys.argv constructed as indicated. It thus sees its environment as if it were being run as a stand-alone program (except for sharing global objects such as previously imported modules). But after execution, the IPython interactive namespace gets updated with all variables defined in the program (except for name and sys.argv). This allows for very convenient loading of code for interactive work, while giving each program a 'clean sheet' to run in.
[...]

在程序执行期间,a=1def f(): ...被执行。结果,a=1f 都被复制回主命名空间。

值得注意的是,f 未在完成前调用。手动调用f()时,其全局命名空间仍然是执行test的那个;只有 test 命名空间被 f().

修改

正如@MisterMiyagi 指出的那样,解释是在 run 命令的末尾复制了变量、函数和其他对象。因此,test命名空间中的变量amain命名空间中的变量a并不相同,尽管它们指向同一个对象。它是问题示例中的不可变变量;因此,在函数内部对其进行的任何更改都会导致 test 命名空间中的变量指向一个新对象。

但是,如果变量是可变的,并且如果此对象发生变化但变量未在函数内部重新定义,则两个变量的新值将相同 a:

test.py:

a=[1,2,3]

def f() :
    global a
    a+=[1]

ipython3 部分:

In [1]: run test

In [2]: a
Out[2]: [1, 2, 3]

In [3]: f()

In [4]: a
Out[4]: [1, 2, 3, 1]

如果我在函数定义中将 a+=[1] 替换为 a=a+[1],这会创建一个新对象 a+[1] 而不是更新 a 指向的对象,结果是

In [4]: a
Out[4]: [1, 2, 3]

因为在 运行 f() 之后,test 命名空间中的变量 a 将指向一个新对象,不会改变之前指向的对象。

In [2]: run test.py
In [3]: a
Out[3]: 1
In [4]: f()
In [5]: a
Out[5]: 1

使用“-i”参数:

In [6]: run -i test.py
In [7]: a
Out[7]: 1
In [8]: f()
In [9]: a
Out[9]: 2

来自 run?:

-i
  run the file in IPython's namespace instead of an empty one. This
  is useful if you are experimenting with code written in a text editor
  which depends on variables defined interactively.