Updating Class Instance Attributes via SuperClass update() method - python

I'm in the midst of coding a simulation for studying UAV interaction in swarms and obstacle avoidance scenarios. The issue I'm having currently is in getting the vehicles to update their positions. Basically, I have a base class which contains the update() method which does the calculation to arrive at the new position and velocity. The actual objects in the sim code are instances of a subclass of this, and in the subclass's update() method, all I do is update the acceleration vector and call super().update(). However, the values are retained after the function call. I assume this is just a lack of knowledge of Python on my part, as I'm just starting with it (coming from C++ for many years). The searches I've done for pass by reference and such are giving me good information, but so far I can't get an answer to this specific problem. Here's the code:
[EDIT] Per jonrsharpe's request, I've written out a minimal example that encapsulates the problem I'm having. Here's the minimal code:
class UpdateTester:
x = [0,0]
def update(self):
for elem in self.x:
elem += 1
class SubClassTester(UpdateTester):
def update(self):
super(SubClassTester,self).update()
a = SubClassTester()
for i in range(1,5):
a.update()
print(a.x)
So basically, per my(admittedly limited) understanding, I should get an output which shows increments to the list a.x. However, my output from running this example shows repeated [0,0]'s as output.

Integers are immutable in Python therefore for elem in self.x: elem += 1 does nothing. It doesn't change self.x. If you want to change self.x:
for i, value in enumerate(self.x):
self.x[i] = value + 1
Also UpdateTester.x is a class variable. It is the same for all instances. Lists are mutable in Python therefore if you call .update() on any instance of UpdateTester class then you change the list for all of them. To create per instance list instead, put it in __init__():
class UpdateTester(object):
def __init__(self, **kwargs):
super(UpdateTester, self).__init__(**kwargs) # for multiple inheritence
self.x = []

Related

Python OOB: Not understanding subclasses

I am currently working on the 3.2.1.10 A short journey from procedural to object approachlab from edube.org, course (Python Essentials 2 (Intermediate, v.2.0).
The task is about programming a stack in object oriented style. We have a push and a pop method so far and a simple stack we can fill and take away the last item. Now it should be extended to be able to display the sum of the values in the stack. The complete code given in the lab is as follows:
class Stack:
def __init__(self):
self.__stack_list = []
def push(self, val):
self.__stack_list.append(val)
def pop(self):
val = self.__stack_list[-1]
del self.__stack_list[-1]
return val
class AddingStack(Stack):
def __init__(self):
Stack.__init__(self)
self.__sum = 0
def get_sum(self):
return self.__sum
def push(self, val):
self.__sum += val
Stack.push(self, val)
def pop(self):
val = Stack.pop(self)
self.__sum -= val
return val
stack_object = AddingStack()
for i in range(5):
stack_object.push(i)
print(stack_object.get_sum())
for i in range(5):
print(stack_object.pop())
The code works. As an explanation for using class AddingStack(Stack) it says:
We don't want to modify the previously defined stack. It's already
good enough in its applications, and we don't want it changed in any
way. We want a new stack with new capabilities. In other words, we
want to construct a subclass of the already existing Stack class.
The first step is easy: just define a new subclass pointing to the
class which will be used as the superclass.
This is what it looks like: class AddingStack(Stack):
pass
The class doesn't define any new component yet, but that doesn't mean
that it's empty. It gets all the components defined by its superclass
However, when I run the same code, but just modify the line to:
class AddingStack():
it still works. I don't understand what the benefit of class AddingStack(Stack) is?
However, when I run the same code, but just modify the line to:
class AddingStack():
it still works. I don't understand what the benefit of class AddingStack(Stack) is?
It still works because the methods in AddingStack explicitly call other methods in Stack.
You aren't actually using any inherited methods, which defeats the entire point of inheritance.
Usually the benefit from inheritance in OOP is the ability to crate a class from an existing class, and modify it a bit with ease.
If you really just override every single function in the super-class, then no, don’t use inheritance, it won’t benefit you nothing.
It is very useful in cases when you have a sub-class that only change some of the functions and the things from the super-class, and the rest, will be using the super-class functions.
It works because you are calculating the sum without actually using the elements of the stack, instead accumulating the result in the __sum variable.
You are also not using inheritance, instead delegating to the pop() and push() methods of class Stack.
The objective of the exercise seems to be for you to add up the elements of the stack (which is already implemented in the superclass) and to implement get_sum() such that you iterate through the list of values on the stack and add them up.

How to iteratively use a method of an iteratively created object in Python

I'm trying to create object iteratively by using a class method inside the class that the objects belong to. So everytime I call that class method it creates an object, and ads it to a dictionary with its proper idex (both are class variables). My problem comes when I want to call the same method of every object, but iteratively and with a random attribute each time. My code is large so here I coded a another program with exactly what I'm looking for so it's easier to understand.
class new_class:
objects = {} #this dictionary stores all objects of this class
i = 0 #used to iterate the dictionary and define every object separately
def __init__(self):
pass
def method(self, random): #<-- here goes the random elements that the method should be called with
return random #sample usage of the random value
#classmethod
def object_creator(cls):
cls.i += 1
cls.objects[cls.i] = cls() <-- this creates a new object of its own class and adds it to the dictionary with the key of the also iterated "i" variable
while True:
new_class.object_creator()
#Here I want to call for the method of evey existing object with random attributes
Calling the object this way, with the dictionary and its index doesn't work because it just calls the last created object, since the current index belongs to him.
while True:
new_class.object_creator()
new_class.objects[new_class.i].method()
I'm not sure if this is even possible because I would have to essentialy "create new code" for each created object. The only pseudo-solution I've found is to make another loop and make iterate through the length of the dictionary, and call the method of the object whose index is the loop's one, but that calls each method at a time and not all of them at the same time.
By default, your code is executed sequentially by a single thread, so the calls to the method will be done one after another. But it may be very quick to call all your objects' method because computers are fast. And from the point of view of the programming language, calling call_my_method_for_all_my_objects is no different than calling int("14").
If you really really (really) want to have code executed in parallel, you can have a look at multi-threading and multi-processing, but these are not easy topics. Don't bother with them if you don't actually want your program to execute faster or really need to have multiple code execution at the same time.
Using a dict instead of a list is not a real issue.
The problem with
while True:
new_class.object_creator()
new_class.objects[new_class.i].method()
is that at each iteration of the loop, it will create a new object (which increments i), then call the i-th object (newly created) method. It means that each object will have its method called only once, and in the creation order which is also i-ascending.
As for a solution, I recommand you to create a function or a method that will call for each of your objects. I decided to implement it as a static method of the class :
class new_class:
objects = {}
i = 0
def __init__(self):
pass
def method(self, random):
return random
#classmethod
def object_creator(cls):
cls.i += 1
cls.objects[cls.i] = cls()
#staticmethod # static
def call_each():
for i, obj in new_class.objects.items(): # iterate over the objects
print(obj.method(i)) # call each one's method, for example with its index
I used it like that :
# let's create 3 items for demonstration purposes
new_class.object_creator(); new_class.object_creator(); new_class.object_creator()
print(new_class.objects) # {1: <__main__.new_class object at 0x0000022B26285470>,
# 2: <__main__.new_class object at 0x0000022B262855C0>,
# 3: <__main__.new_class object at 0x0000022B262854A8>}
new_class.call_each() # prints 1 2 3
If you want to provide a random value for each call, add import random to your script and change the call_each method to :
#staticmethod
def call_each():
for obj in new_class.objects.values():
print(obj.method(random.random()))
so that
new_class.call_each() # prints 0.35280749626847374
# 0.22163283338299222
# 0.7368657784332368
If this does not answer your question, please please try to be extra clear in what you ask.

Python: Same name for class method parameters and class attribute

I have an assignment on classes. One of my tasks is as follows:
a. Augment the Tribute class by adding a new property, hunger, which will describe
the level of hunger for the Tribute. The initial value for hunger should be 0, as all
the Tributes will start the game with full stomach.
b. Create a method, get_hunger(), which return the current hunger level of the tribute.
c. Create a method, add_hunger(hunger), which will add a hunger value to the Tribute’s
hunger. When the hunger of a Tribute is equal or more than 100, he/she will
go_to_heaven(). (FYI go_to_heaven() is defined previously by other parent classes)
1)I wrote the following code, and when I tried running it I keep getting syntax error highlighted on the indentation right before self.get_hunger()+=hunger. May I know the reason for the syntax error since .get_hunger() is essentially self.hunger. self.get_hunger()=0 will work for other codes following this task but I don’t understand why self.get_hunger()+=hunger wont work. My lecturer stresses on not breaking the underlying layer of abstraction, which is why I would use the method .get_hunger() over attribute hunger, especially if I needed to get hunger value from instances of future child classes of Tribute, not sure if this concept is also embraced in practical situations.
class Tribute(Person):
def __init__(self, name, health):
super().__init__(name, health, -1)
self.hunger=0
def get_hunger(self):
return self.hunger
def add_hunger(self,hunger):
self.get_hunger()+=hunger #dk why can't assign to function call
if self.get_hunger()>=100:
self.go_to_heaven()
2)I also tried writing self.hunger+=hungerinstead of self.get_hunger()+=hunger to get past the syntax error and it works.However, I don’t find it intuitive why when defining a class method, and when I face a scenario where the name of the method parameter and the name of the class attribute is the same, the parameter will not overwrite the attribute in the form of hunger. Can anyone reason with me?
Assignments are performed on variables. That's just how Python works. Variables are references to objects in memory.
Function calls return objects, and you can't assign to an object.
I recommend using a setter method to handle the other side of the abstraction.
class Tribute(Person):
...
def get_hunger(self):
return self.hunger
def set_hunger(self, hunger):
self.hunger = hunger
def add_hunger(self,hunger):
self.set_hunger(self.get_hunger() + hunger)
if self.get_hunger() >= 100:
self.go_to_heaven()
Looks like you have abstraction already, since you're using a method to increase class field add_hunger() with health checking inside. Not using class field directly inside it's own method doesn't seem to have much sense.
You can't access class field self.hunger by using its method self.get_hunger().
Your method self.get_hunger() returns value of self.hunger (its copy), but not the variable itself. So you can add any number to that value, but you need to write it somewhere to keep its value. So, when you run self.get_hunger()+=hunger your method returns a copy of self.hunger, adds hunger from parameters to it and then this copy is lost, but self.hunger is the same.
So, if you want to increase self.hunger - you just need to use self.hunger+=hunger, which you checked already.
It would actually work if you would use the type of variable, that is passed by reference, not by value. Like list in this example, but I'd say it's kind of a perverted way to do so. ;)
class Tribute(Person):
def __init__(self, name, health):
super().__init__(name, health, -1)
self.hunger=[0]
def get_hunger(self):
return self.hunger
def add_hunger(self,hunger):
self.get_hunger()[0]+=hunger # dk why can't assign to function call
if self.get_hunger()[0]>=100:
self.go_to_heaven()
Using the same names for different things is not good. It can cause some errors. Even if one of them is variable and another one is method. If you try to pass that method to some thread later - it will never know which one you're passing there. If all names are different - it's safe and more readable.

Index of an object subclassing list into a parent list

I've written a script that solves sudoku problems.
To model each slot of a grid, I have in a first time defined Slot and Grid classes like this (complete code elipsed for the sake of simplicity :
class Slot():
def __init__(self,grid):
self.grid = grid
self.values = list(range(9))
def pos(self):
return self.grid.index(self)
class Grid(list):
def __init__(self):
for i in range(9*9):
self.append(Slot(self))
Like this, I can define method for my Slot class using self.pos() and self.values(). For example :
g = Grid()
g[5].pos() -> returns 5, OK !
Now that my full script works just fine, I want to refactor it, and, as a Slot is basically a list belonging to a Grid, I decided it would be great for my Slot to subclass list, like this :
class Slot(list):
def __init__(self,grid):
self.grid = grid
self.append(list(range(9)))
def pos(self):
return self.grid.index(self)
class Grid(list):
def __init__(self):
for i in range(9*9):
self.append(Slot(self))
g = Grid()
g.index(g[5]) -> returns 0, KO !
I've tried to init the list first ie: super().init(list(range(9)), and also a few variations, but nothing seems to work.
What am I missing ?
PS : the g.index(g[5]) is just to describe, I'm aware it's pointless. I'm using this logic in methods inside my objects (self.pos(), etc.)
By making Slot a subclass of list you also make the comparison between Slot instances use the logic defined for lists (since you haven't overridden that).
Since all Slots contain the same value:
self.append(list(range(9)))
g.index() will simply match the first entry the grid yielding 0 as the result.
When you inherited from object (as Slot did in your first example) all instances compared unequal to themselves as is defined in the Python Reference (unless logic is implemented that dictates otherwise).
In short, you'll need to redefine the comparison methods if you need the slots with similar items to be treated differently when compared. In addition to that, you might want to reconsider sub classing from list and, instead, opt for UserList from collections.

Something seems to be stuck in memory

I have a program that loops over a list and then performs a function on the list. The result that is getting returned from the function is different depending on whether I loop over several observations versus just one. For example when I put in the 10th observation by itself, I get one result but when I put in 9 and 10 and loop over them I get a different answer for 10. The only thing I can come up with is that there is some variable in storage that is leftover from performing the function on 9 that is leading to something different for 10. Here's the code for the loop:
for i, k in enumerate(Compobs):
print i+1, ' of ', len(Compobs)
print Compobs[i]
Compobs[i] = Filing(k[0],k[1])
Compobs is just a list like this:
[['355300', '19990531'],[...],...]
The function Filing is from another .py file that I import. It defines a new class, Filing() and performs a bunch of functions on each observation and ultimately returns some output. I'm fairly new to python so I'm at a bit of a loss here. I could post the Filing.py code, but that's over 1,000 lines of code.
Here's the Filing class and the init.
class Filing(object):
cik =''
datadate=''
potentialpaths=[]
potential_files=[]
filingPath =''
filingType=''
reportPeriod=''
filingText=''
current_folder=''
compData=pd.Series()
potentialtablenumbers=[]
tables=[]
statementOfCashFlows=''
parsedstatementOfCashFlows=[]
denomination=''
cashFlowDictionary ={}
CFdataDictionary=OrderedDict()
CFsectionindex=pd.Series()
cfDataSeries=pd.Series()
cfMapping=pd.DataFrame()
compCFSeries=pd.Series()
cftablenumber=''
CompleteCF=pd.DataFrame()
def __init__(self,cik,datadate):
self.cik=cik
self.datadate=datadate
self.pydate=date(int(datadate[0:4]),int(datadate[4:6]),int(datadate[6:8]))
self.findpathstofiling()
self.selectfiling()
self.extractFilingType()
self.extractFilingText()
self.getCompData()
self.findPotentialStatementOfCashFlows()
self.findStatementOfCashFlows()
self.cleanUpCashFlowTable()
self.createCashFlowDictionary()
self.extractCFdataDictionary()
self.createCFdataSeries()
self.identifySections()
self.createMapping()
self.findOthers()
Shouldn't all the variables in the Filing.py get cleared out of memory each time it is called? Is there something I'm missing?
All of the lists, dicts, and other objects defined at the top level of Filing have only one copy. Even if you explicitly assign them to an instance, that copy is shared (and if you don't explicitly assign them, they're inherited). The point is that if you modify them in one instance, you modify them in all instances.
If you want each instance to have its own copy, then get rid of the top-level assignments altogether, and instead assign new instances of the objects in __init__.
In other words, don't do this:
class Foo(object):
x = []
def __init__(self):
self.x = x
Instead, do this:
class Foo(object):
def __init__(self):
self.x = []
Then each instance will have its own, unshared copy of x.
You are defining your class data members as class attributes, not object attributes. They are like static data member of a C++ or Java class.
To fix this, you need to not define them above the __init__ method, but instead, define them in the __init__ method. For example, instead of
tables = []
above __init__ you should have:
self.tables = []
in __init__

Categories

Resources