Reputation: 1
I am currently trying to understand some basic concepts of Python.
Code Sample:
class BorgSingleton:
_shared_borg_state = {}
test = "toto"
def __init__(self):
self.__dict__ = self._shared_borg_state
print("borg created")
print(self.test)
class Point(BorgSingleton) :
def __init__(self, lat, long):
self.latitude = lat
self.longitude = long
super().__init__()
print (self.latitude)
def g(self) :
return BorgSingleton.test +" " +str(self.latitude) + " " + str(self.longitude)
def main():
point_1 = Point(5,2)
point_2 = Point(3,3)
print(point_1.g())
print(point_2.g())
point_1.test = "tata"
print(point_1.g())
print(point_2.g())
if (__name__ == "__main__"):
main()
Code Execution:
borg created
toto
Traceback (most recent call last):
File "file1.py", line 32, in
main()
File "file1.py", line 23, in main
point_1 = Point(5,2)
File "file1.py", line 16, in init
print (self.latitude)
AttributeError: 'Point' object has no attribute 'latitude'
I'm sure I'm making a basic mistake in python, but am not sure why my instance point_1
of the class Point
doesn't have the latitude
attribute.
I read about Inheritance and Borg conception, but something is missing. I have also tried the -tt option for indentation mistake.
Upvotes: 0
Views: 186
Reputation: 2503
Try to put super().__init__()
as first instruction in __init__()
method of class Point:
class BorgSingleton:
_shared_borg_state = {}
test = "toto"
def __init__(self):
self.__dict__ = self._shared_borg_state
print("borg created")
print(self.test)
class Point(BorgSingleton):
def __init__(self, lat, long):
super().__init__()
self.latitude = lat
self.longitude = long
#super().__init__()
print(self.latitude)
def g(self):
return BorgSingleton.test + " " + str(self.latitude) + " " + str(self.longitude)
def main():
point_1 = Point(5, 2)
point_2 = Point(3, 3)
print(point_1.g())
print(point_2.g())
point_1.test = "tata"
print(point_1.g())
print(point_2.g())
if (__name__ == "__main__"):
main()
With this modification the attributes latitute
and longitude
will be present in the instances of the class Point
.
Upvotes: 1
Reputation: 31
The code you've wrote should've worked if self.__dict__
had not been overridden in the parent BorgSingleton
class.
In Python, it is not a good practice to override magic methods (ones that start and end with double-unders) unless you know what you're doing.
To generalize what happened, lets see the below two classes:
class Parent:
some_attribute = {}
def __init__(self):
self.__dict__ = self.some_attribute
class Child(Parent):
def __init__(self, arg_1, arg_2):
self.arg_1 = arg_1
self.arg_2 = arg_2
# at this point, an instance of class Child has arg_1 and arg_2
# attributes, and self.__dict__ stores corresponding values
super().__init__()
# in the Parent class, self.__dict__ is overridden to an empty dict
# which makes arg_1 and arg_2 arguments unaccessible from the
# instance after a super call
test_instance = Child(1, 2)
Update based on additional question, asked in comments:
In order to change an attribute of several model instances, we have to iterate over a collection of these instances, setting the desired value to the corresponding attribute.
Having partially refactored your code, running this code:
class BorgSingleton:
def __init__(self):
"""
Assigning default value to attribute.
"""
self.test = 'toto'
class Point(BorgSingleton):
def __init__(self, lat, long):
self.latitude = lat
self.longitude = long
super().__init__()
def g(self):
return f'{self.test} {self.latitude} {self.longitude}'
class Points:
def __init__(self, collection_of_points):
"""
Before we instantiate Points, check that collection_of_points is a list
and all elements of a list are the instances of Point class.
"""
if (isinstance(collection_of_points, list) and
all([isinstance(x, Point) for x in collection_of_points])):
self.points = collection_of_points
else:
raise ValueError
def assign_new_value_for_test_attribute(self, value):
"""
Iterate over all Point class instances, stored in self.points,
and change values of test attribute.
"""
[setattr(x, 'test', value) for x in self.points]
def main():
point_1 = Point(5, 2)
point_2 = Point(3, 3)
print(point_1.g())
print(point_2.g())
points = Points([point_1, point_2])
points.assign_new_value_for_test_attribute('tata')
print(point_1.g())
print(point_2.g())
if __name__ == "__main__":
main()
results in this:
toto 5 2
toto 3 3
tata 5 2
tata 3 3
Here, we create an instance of Points
class, which stores point_1
and point_2
instances in Points.points
attribute. Then, calling Points.assign_new_value_for_test_attribute
method, list comprehension is used to update test
attribute of point_1
and point_2
.
Upvotes: 2