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: %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]
(I hope the code is self-explanatory)
I ran the cell and tested the code and it work fine:
In [24] : m = MyClass()
m.show()
Out [25] : Name: Class 0
Id: 0
The Twist:
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 [24] : m = MyClass()
m.show()
MyClass.instance_at(0).show()
Out [25] : 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.
CodePudding user response:
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[0].__class__
y = Old_Example()