I was tinkering on a little project idea in jupyter notbook when I stumbled across some weird behaviour… The following code is abstracted from the original.
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: %snId: %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]
(I hope the code is self-explanatory)
I ran the cell and tested the code and it work fine:
In  : m = MyClass() m.show() Out  : Name: Class 0 Id: 0
I didn’t like that the first instance, with no name given, would be called ‘Class 0’ so I thought to myself: ‘Why not add an object which will act as a placeholder for the index 0′ (Don’t ask why I did this, ’twas a brainfart).
So I changed line 2 to be
Instances = [MyClass(name='id'). This also worked but when I tried to receive the instance at index 0, it’s id value was different to what I expected.
In  : m = MyClass() m.show() MyClass.instance_at(0).show() Out  : Name: Class 0 Id: 1 Name: id Id: 1
This was the point where I decided to write a more generalized version of my code (the one shown here) in another notebook. I wrote it whole before running the cell, including
Instances = [MyClass(name='id')].
This time I got this:
--------------------------------------------------------------------------- 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
So now I have a piece of code which runs in one notebook but not in another. At least when you simply copy and paste it. In jupyter notebook this is fixable by changing line 2 to
Instances = , running the cell and changing it back.
I am fairly certain that this is because the class and the class variable
Instances already existed before I created the ambiguous line of code.
In hindsight, this does make sense and I suspect the unexpected id value comes from a constructor call being discovered in the list when the instance
m is being created.
Am I wrong? Can anybody elaborate?
Please let me know if a post like this is inappropriate here.
A simpler example:
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.__class__ y = Old_Example()