更改类时Jupyter自动重新加载失败

Sta*_*ean 10 python jupyter-notebook

在大多数情况下,使用jupyter实验室/笔记本时,我将这两行放在笔记本的第一个单元格中:

%reload_ext autoreload
%autoreload 2
Run Code Online (Sandbox Code Playgroud)

这些通常使我可以修改导入的脚本并使用它们,而不必重新导入它们或重新启动内核。昨天,我遇到了一个问题:我修改了脚本,在笔记本上执行一个单元格给了我一个错误。重新启动内核并重做导入可修复它。我调查了此问题,并尝试创建一个最小的示例。

假设您有以下工作目录:

+-- nb.ipynb
+-- scripts
|   +-- __init__.py
|   +-- script1.py
Run Code Online (Sandbox Code Playgroud)

笔记本由三个单元组成:

%reload_ext autoreload
%autoreload 2
Run Code Online (Sandbox Code Playgroud)

\

from scripts.script1 import Foo
Run Code Online (Sandbox Code Playgroud)

\

a = Foo(42)
Run Code Online (Sandbox Code Playgroud)

在实验开始时,script1包含以下内容:

class Foo():
    def __init__(self, x):
        self.x = x
Run Code Online (Sandbox Code Playgroud)

现在,我们执行笔记本的3个单元,一切正常。然后,我们转到script1.py并将其代码替换为:

class Bar():
    def __init__(self, x):
        self.x = x

class Foo(Bar):
    def __init__(self, x):
        super().__init__(x)
Run Code Online (Sandbox Code Playgroud)

我们保存文件,回到笔记本,然后执行包含单元格,a = Foo(42) 这将产生以下错误:

[autoreload of script.script failed: Traceback (most recent call last):
  File "/home/user/miniconda3/lib/python3.6/site-packages/IPython/extensions/autoreload.py", line 245, in check
    superreload(m, reload, self.old_objects)
  File "/home/user/miniconda3/lib/python3.6/site-packages/IPython/extensions/autoreload.py", line 384, in superreload
    update_generic(old_obj, new_obj)
  File "/home/user/miniconda3/lib/python3.6/site-packages/IPython/extensions/autoreload.py", line 323, in update_generic
    update(a, b)
  File "/home/user/miniconda3/lib/python3.6/site-packages/IPython/extensions/autoreload.py", line 288, in update_class
    if update_generic(old_obj, new_obj): continue
  File "/home/user/miniconda3/lib/python3.6/site-packages/IPython/extensions/autoreload.py", line 323, in update_generic
    update(a, b)
  File "/home/user/miniconda3/lib/python3.6/site-packages/IPython/extensions/autoreload.py", line 266, in update_function
    setattr(old, name, getattr(new, name))
ValueError: __init__() requires a code object with 0 free vars, not 1
]
Run Code Online (Sandbox Code Playgroud)

重新启动内核或再次执行导入行可解决此问题。为什么autoreload在这种情况下不起作用?

PS:这是在python 3.6中完成的,而我最初的问题是在python 3.7中

log*_*ind 7

一、关于问题

为什么在这种情况下 autoreload 不起作用?

正如错误日志所述:

setattr(old, name, getattr(new, name))
ValueError: __init__() requires a code object with 0 free vars, not 1
Run Code Online (Sandbox Code Playgroud)

autoreload在这里做什么:

它尝试__init__()用新__init__()函数代码对象替换旧函数(代码更改之前的函数)的代码对象。当我检查__init__()代码更改前后函数的自由变量时,得到如下结果:

检查代码:

Foo.__init__.__code__.co_freevars
Run Code Online (Sandbox Code Playgroud)

结果:

在代码更改之前:(),没有自由变量。

代码更改后:('__class__',),这里有一个空闲变量。这就是错误发生的原因。它不能用新的目标代码替换旧的目标代码。

2.如何解决问题

在这种情况下,因为autoreload仍然是旧的函数对象,所以我们不能做任何事情,只能重新启动内核。

希望这有帮助。