jupyter notebook 中 python 的奇怪行为;这是一个错误还是我应该接受它?

Posted

技术标签:

【中文标题】jupyter notebook 中 python 的奇怪行为;这是一个错误还是我应该接受它?【英文标题】:Weird behaviour with python in jupyter notebook; is this a bug or should I just accept it as it is? 【发布时间】:2021-11-11 21:09:48 【问题描述】:

当我偶然发现一些奇怪的行为时,我正在对 jupyter notbook 中的一个小项目想法进行修补...... 以下代码是从原文中抽象出来的。

class MyClass:
    Instances = []
    
    def __init__(self,name=None):
        self.id = len(MyClass.Instances)
        MyClass.Instances.append(self)
        
        if name is None:
            self.name = 'Class %s' % self.id
        else:
            self.name = name
    
    def show(self):
        print('Name: %s\nId: %s' % (self.name, self.id))

    def instance_at(i : int):
        if i >= len(MyClass.Instances):
            raise ValueError("Instance does not exist")
        return MyClass.Instances[i]

(我希望代码是不言自明的)

我运行了单元并测试了代码,它工作正常:

In [24] :   m = MyClass()
            m.show()

Out [25] :  Name: Class 0
            Id: 0

转折:

我不喜欢第一个实例,没有给出名字,被称为'Class 0',所以我想:'为什么不添加一个对象作为索引 0 的占位符'(Don '不要问我为什么这样做,'这是一个脑残)。

所以我将第 2 行更改为 Instances = [MyClass(name='id')。这也有效,但是当我尝试在索引 0 处接收实例时,它的 id 值与我的预期不同。

In [24] :   m = MyClass()
            m.show()
            MyClass.instance_at(0).show()

Out [25] :  Name: Class 0
            Id: 1
            Name: id
            Id: 1

这就是我决定在另一个笔记本中编写更通用版本的代码(此处显示的版本)的原因。我在运行单元之前写了完整的,包括Instances = [MyClass(name='id')]

这次我得到了这个:

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-1-60020e1975a4> in <module>
----> 1 class MyClass:
      2     Instances = [MyClass(name='id')]
      3     #Instances = []
      4 
      5     def instance_at(i : int):

<ipython-input-1-60020e1975a4> in MyClass()
      1 class MyClass:
----> 2     Instances = [MyClass(name='id')]
      3     #Instances = []
      4 
      5     def instance_at(i : int):

NameError: name 'MyClass' is not defined

所以现在我有一段代码可以在一个笔记本上运行,但不能在另一个笔记本上运行。至少当您简单地复制和粘贴它时。在 jupyter notebook 中,这可以通过将第 2 行更改为 Instances = []、运行单元并将其改回来解决。

我相当肯定这是因为在我创建模棱两可的代码行之前,类和类变量 Instances 已经存在。

事后看来,这确实是有道理的,我怀疑意外的 id 值来自创建实例 m 时在列表中发现的构造函数调用。

我错了吗?谁能详细说明一下?

如果这样的帖子在这里不合适,请告诉我。

【问题讨论】:

“所以我将第 2 行更改为 Instances = [MyClass(name='id')。这也有效”这是不可重现的。这样做将使用您当前正在编写的MyClass 的定义。它将使用 previous MyClass,如果它在您的会话中仍然可用;如果您重新启动并尝试使用这样的类(或在新的源文件中从头开始),则会产生错误。 "这次我得到了这个:...所以现在我有一段代码可以在一个笔记本上运行,但不能在另一个笔记本上运行。"那是因为它实际上并没有像你所期望的那样“工作”,而是“在一个笔记本中”你还没有开始新鲜。 也就是说:试图“自动注册”您为类创建的每个实例几乎肯定是一个设计错误。只需让调用代码列出部分代码关心的实例。根据整体代码结构,这可能是也可能不是全部。 【参考方案1】:

一个更简单的例子:

class Example:
    def __init__(self):
        print("Creating an instance of the OLD class")

class Example: # redefining like this does **not** cause an error
    Instances = [Example()]
    def __init__(self):
        print("Creating an instance of the NEW class")
    
# the OLD message is printed immediately
# because `Instances = [Example()]` uses the previous definition
# because it **cannot** use the current one; it hasn't been created yet.

# You **do** get an error **without** the old definition, because then
# there isn't a definition at all.

x = Example() # the NEW message is printed
# the OLD class **still exists**, but cannot easily be accessed.
# As long as we can think of a way to get at an instance,
# we can use the `__class__` of the instance to create more;
# and we can rename that to make it easily usable:
Old_Example = Example.Instances[0].__class__
y = Old_Example()

【讨论】:

以上是关于jupyter notebook 中 python 的奇怪行为;这是一个错误还是我应该接受它?的主要内容,如果未能解决你的问题,请参考以下文章

如何修改jupyter notebook的默认工作路径

在Jupyter notebook中使用特定虚拟环境中的python的kernel

Jupyter Notebook安装新python kernel

python 在Jupyter Notebook中更改浮动格式

Python之Idel利器jupyter notebook

mac 配置 jupyter notebook