I have an infuriating problem with the code below. I have some objects which each hold internally a list of other objects. The code creates a new instance of a 'two' object, adds it into the 'three' object collection, and then creates a random number of 'one' objects and adds them into the newly created 2 object. When I iterate through three's collection of two objects, I expect to see that each two object has a random number of one objects, but in fact I see that all two objects have the same number of one objects.
What on earth am I doing wrong, or not understanding?
from typing import List
from random import randrange
class one:
__name: str = None
def __init__(self, name: str):
self.__name = name
def get_name(self):
return self.__name
class two:
__name:str = None
__ones: List[one] = []
def __init__(self, name: str):
self.__name = name
def get_name(self):
return self.__name
def num_ones(self) -> int:
if not self.__ones:
return 0
return len(self.__ones)
def __eq__(self,other):
if not other:
return False
if not isinstance(other, self.__class__):
return False
if self.get_name() == other.get_name():
return True
return False
def __str__(self):
ret = self.get_name()
ret += " ones:"
ret += str(self.num_ones())
return ret
def add_one(self, name: str):
for o in self.__ones:
if o.get_name() == name:
return o
foo = one(name)
self.__ones.append(foo)
class three:
__twos: List[two] = []
def __init__(self):
pass
def add_two(self, t:two):
for i in self.__twos:
if i == t:
return o
self.__twos.append(t)
def __str__(self):
ret = "twos:"
ret += str(len(self.__twos))
return ret
athree = three()
for i in range(1,60):
#create a new two object
atwo = two("two0" + str(i))
#print the two object, it should contains zero one objects
#because it's only just been created
#but instead it contains the same as all other instances of two objects
print(str(atwo))
#add newly created object into a collection of two objects
athree.add_two(atwo)
#now create a random number of one objects and
#add them into our newly created two object
#so we expect three to contains 60 two objects each
#with a random number of one objects in them.
rand = randrange(20)
for o in range(1,rand):
aone = atwo.add_one("one0" + str(o))
example output
..
two044 ones:17
two045 ones:17
two046 ones:17
..
here we expected to see that the number of 'ones' in each 'two' is random, but all sixty instances of two contains 17 ones!?
EDIT:
following the answer, which is that in Python properties declared in the body of the class are static until assigned by a method (usually the constructor), here is the working code :
from typing import List
from random import randrange
class one:
__name: str = None
def __init__(self, name: str):
self.__name = name
def get_name(self):
return self.__name
class two:
__name = None
__ones: List[one] = None
def __init__(self, name: str):
self.__name = name
self.__ones: List[one] = []
def get_name(self):
return self.__name
def num_ones(self) -> int:
if not self.__ones:
return 0
return len(self.__ones)
def __eq__(self,other):
if not other:
return False
if not isinstance(other, self.__class__):
return False
if self.get_name() == other.get_name():
return True
return False
def __str__(self):
ret = self.get_name()
ret += " ones:"
ret += str(self.num_ones())
return ret
def add_one(self, name: str):
for o in self.__ones:
if o.get_name() == name:
return o
foo = one(name)
self.__ones.append(foo)
class three:
__twos: List[two] = None
def __init__(self):
self.__twos: List[two] = []
def add_two(self, t:two):
for i in self.__twos:
if i == t:
return o
self.__twos.append(t)
def __str__(self):
ret = "twos:"
ret += str(len(self.__twos))
return ret
athree = three()
for i in range(1,60):
#create a new two object
atwo = two("two0" + str(i))
#add newly created object into a collection of two objects
athree.add_two(atwo)
#now create a random number of one objects and
#add them into our newly created two object
#so we expect three to contains 60 two objects each
#with a random number of one objects in them.
rand = randrange(20)
for o in range(1,rand):
aone = atwo.add_one("one0" + str(o))
print(str(atwo))