Using a dictionary to control the flow of methods - python

I have a class Population that contains several method.
According to an input I want the run the method on an instance of the class Population in a given order.
To be a bit more accurate in what I am trying to achieve is quite the same than using is something like that:
stuff = input(" enter stuff ")
dico = {'stuff1':functionA, 'stuff2':functionC, 'stuff3':functionB, 'stuff4':functionD}
dico[stuff]()
Except that the functionA, functionB etc... are methods and not functions:
order_type = 'a'
class Population (object):
def __init__(self,a):
self.a = a
def method1 (self):
self.a = self.a*2
return self
def method2 (self):
self.a += 2
return self
def method3 (self,b):
self.a = self.a + b
return self
if order_type=='a':
order = {1:method1, 2:method2, 3:method3}
elif order_type=='b':
order = {1:method2, 2:method1, 3:method3}
else :
order = {1:method3, 2:method2, 3:method1}
my_pop = Population(3)
while iteration < 100:
iteration +=1
for i in range(len(order)):
method_to_use = order[i]
my_pop.method_to_use() # But obviously it doesn't work!
Hope I've made my question clear enough!
Note that one of my method need two arguments

Pass the instance explicitly as first argument:
method_to_use = order[i]
method_to_use(my_pop)
Full working code:
order_type = 'a'
class Population (object):
def __init__(self,a):
self.a = a
def method1 (self):
self.a = self.a*2
return self
def method2 (self):
self.a += 2
return self
def method3 (self):
self.a = 0
return self
if order_type=='a':
order = [Population.method1, Population.method2, Population.method3]
elif order_type=='b':
order = [Population.method2, Population.method1, Population.method3]
else :
order = [Population.method3, Population.method2, Population.method1]
my_pop = Population(3)
while iteration < 100:
iteration +=1
for method_to_use in order:
method_to_use(my_pop)
If you want to pass more than one argument, simply use the *args syntax:
if order_type=='a':
order = [Population.method1, Population.method2, Population.method3]
arguments = [(), (), (the_argument,)]
elif order_type=='b':
order = [Population.method2, Population.method1, Population.method3]
arguments = [(), (), (the_argument,)]
else :
order = [Population.method3, Population.method2, Population.method1]
arguments = [(the_argument, ), (), ()]
my_pop = Population(3)
while iteration < 100:
iteration +=1
for method_to_use, args in zip(order, arguments):
method_to_use(my_pop, *args)
The () is an empty tuple, hence *args will expand to no additional arguments, while (the_argument,) is a 1-element tuple that will pass the argument to the method.

Use getattr:
order = {1:'method1', 2:'method2', 3:'method3'} #values are strings
...
method_to_use = order[i]
getattr(mypop, method_to_use)()

You can use operator.methodcaller:
from operator import methodcaller
method_to_use = methodcaller('method' + str(i))
method_to_use(my_pop)

Related

How can I modify my __repr__ to respresent correctly?

My __repr__ method works fine using objects created in it's class, but with objects that were created with the help of importing a library and using methods from it, it only represented the memory address...
from roster import student_roster #I only got the list if students from here
import itertools as it
class ClassroomOrganizer:
def __init__(self):
self.sorted_names = self._sort_alphabetically(student_roster)
def __repr__(self):
return f'{self.get_combinations(2)}'
def __iter__(self):
self.c = 0
return self
def __next__(self):
if self.c < len(self.sorted_names):
x = self.sorted_names[self.c]
self.c += 1
return x
else:
raise StopIteration
def _sort_alphabetically(self,students):
names = []
for student_info in students:
name = student_info['name']
names.append(name)
return sorted(`your text`names)
def get_students_with_subject(self, subject):
selected_students = []
for student in student_roster:
if student['favorite_subject'] == subject:
selected_students.append((student['name'], subject))
return selected_students
def get_combinations(self, r):
return it.combinations(self.sorted_names, r)
a = ClassroomOrganizer()
# for i in a:
# print(i)
print(repr(a))
I tried displaying objects that don't rely on anther library, and they dispayed properly.
The issue I was facing was linked to me not understanding the nature of the object. itertools.combinations is an iterable, and in order to represent the values stored I needed to either:
unpack it inside a variable like:
def get_combinations(self, r):
*res, = it.combinations(self.sorted_names, r)
return res
Iter through it inside a loop and leave the original code intact like
for i in a.get_combinations(2):
print(i)
I prefer the second solution

How to update a jitclass variable with its string name by passing a setter function to the jitclass itself?

I'm seeking a way to modify a jitclass variable with its name as a string. I tried to write a setter and getter function (get_A and set_A), and I got the method outside the jitclass. I would like then to pass that method to a jitclass method to update the value of the variable. However I get and error saying:
numba.errors.TypingError: Failed in nopython mode pipeline (step: nopython frontend)
non-precise type pyobject
[1] During: typing of argument at <string> (3)
File "<string>", line 3:
<source missing, REPL/exec in use?>
This error may have been caused by the following argument(s):
- argument 1: cannot determine Numba type of <class 'tuple'>
Here the minimal example:
from numba import float64
from numba.experimental import jitclass
spec = [('A', float64), ('B', float64), ('C', float64)]
#jitclass(spec)
class maClass:
def __init__(self):
self.A = 0
def get_A(self):
return self.A
def set_A(self, x):
self.A = x
def update(self, func, val):
func(val)
C1 = maClass()
print('A', C1.A)
Names= ['A']
vals= [1.]
func = getattr(C1, 'set_' + Names[0])
print(func)
C1.update(func, vals[0])
print('A', C1.A)
Here is a solution I came up with.
I first wrote a function that return a list of the variable names I can update
def get_Variable_Names():
return ['A','B','C']
then, in the jitclass, I wrote a function that can update variable according to an index based on the order of get_Variable_Names.
def update_val(self, i, var):
if i == 0:
self.A = var
elif i == 1:
self.B = var
elif i == 2:
self.C = var
To use this function, I search for the index of a variable name in the list returned by get_Variable_Names and then call the update_val function of the jitclass.
listnames = get_Variable_Names()
val = 1.
name = 'A'
index = listnames.index(name)
C1.update_val(index,val)
here's all the code.
from numba import jitclass, int32, float64
def get_Variable_Names():
return ['A','B','C']
spec = [('A' ,float64),('B' ,float64),('C' ,float64)]
#jitclass(spec)
class maClass():
def __init__(self,):
self.A = 0.
self.B = 0.
self.C = 0.
def update_val(self, i, var):
if i == 0:
self.A = var
elif i == 1:
self.B = var
elif i == 2:
self.C = var
C1 = maClass()
listnames = get_Variable_Names()
val = 1.
name = 'A'
index = listnames.index(name)
C1.update_val(index,val)
val = 2.
name = 'B'
index = listnames.index(name)
C1.update_val(index,val)
val = 3.
name = 'C'
index = listnames.index(name)
C1.update_val(index,val)
print('A',C1.A)
print('B',C1.B)
print('C',C1.C)
It is not was I was looking for, but it is find for now.
I'm still waiting for something better.
I even wrote a script to write the if else update_val function, because It can be tedious when we have a large amount of variables to modify
listnames= ['A','B','C']
for i, n in enumerate(listnames):
if i == 0:
print('if i == ' + str(i)+ ' :\n\tself.' + n + ' = var')
else:
print('elif i == ' + str(i) + ' :\n\tself.' + n + ' = var')
With a couple of small changes, you can in fact use function pointers in numba. I came up with the following based on a whole bunch of experimentation, and the first item on the FAQ.
The first thing to notice is that getattr(C1, 'set_A') returns a Python method object. This is not something that is directly supported in numba at time of writing (v0.53.1). Instead, you have to get a pointer to the actual jitted function, which you can do through the relatively undocumented _numba_type_ attribute. This contains a bunch of descriptors of the methods and attributes of your object that are quite useful. Specifically, your func object is given by
func = C1._numba_type_.jit_methods['set_A']
The second thing to note is that the compiled function func requires an explicit self argument that is a subtype of maClass.class_type.instance_type. Calling it with only one argument will cause a compilation error in update:
func(self, val)
Here is the complete example (including the fixed jitclass import):
from numba import float64
from numba.experimental import jitclass
spec = [('A', float64), ('B', float64), ('C', float64)]
#jitclass(spec)
class maClass:
def __init__(self):
self.A = self.B = self.C = 0
def get_A(self):
return self.A
def set_A(self, x):
self.A = x
def update(self, func, val):
func(self, val)
C1 = maClass()
print('A', C1.A)
Names = ['A']
vals = [1.]
func = C1._numba_type_.jit_methods['set_' + Names[0]]
print(func)
C1.update(func, float64(vals[0]))
print('A', C1.A)

How to create a next method for a class when the next method is supposed to return the values of two generators?

I have the following example in which the next method of a class is supposed to return the values from two generators:
class Test():
def __next__(self):
g1, g2 = self._gen1(), self._gen2()
return next(g1), next(g2)
def _gen1(self):
i = 0
while True:
yield i
i += 2
def _gen2(self):
i = 1
while True:
yield i
i += 2
However, when I call next for this class, the values are not incremented.
>>> t = Test()
>>> next(t)
>>> (0, 1)
>>> next(t)
>>> (0, 1)
What is wrong? Is there a more eloquent way to write this class?
Although I have no idea what you are trying to accomplish, here is a cleaned up version which (I think) does what you want.
class Test():
def __init__(self):
self.g1 = self._gen2()
self.g2 = self._gen1()
def __next__(self):
return next(self.g1), next(self.g2)
def _gen1(self):
i = 0
while True:
yield i
i += 2
def _gen2(self):
i = 1
while True:
yield i
i += 2
t = Test()
print(next(t))
print(next(t))
print(next(t))
Your code doesn't work because it recreates the generator functions every time __next__() is called, which effectively resets them back to their initial state before their next next() values are returned:
def __next__(self):
g1, g2 = self._gen1(), self._gen2() # Don't do this here.
return next(g1), next(g2)
You can fix that by adding an __init__() method and initializing them in it:
class Test:
def __init__(self):
self.g1, self.g2 = self._gen1(), self._gen2() # Initialize here.
def __next__(self):
return next(self.g1), next(self.g2)
...
A more eloquent and slightly more concise way to do it which likewise will avoid the problem would be to use the builtin zip() function to create an "iterator of generators" that will return pairs of next values from each generator every time it's called. Another advantage is it's very easy to extend to handle even more generators simply just changing the __init__() method.
Here's what I mean:
class Test:
def __init__(self):
self.generators = zip(self._gen1(), self._gen2())
def __next__(self):
return next(self.generators)
def _gen1(self):
i = 0
while True:
yield i
i += 2
def _gen2(self):
i = 1
while True:
yield i
i += 2
t = Test()
for _ in range(3):
print(next(t))
Output:
(0, 1)
(2, 3)
(4, 5)

How to call method of other object in Python?

I am new in python, and I have small problem, I have two classes, and it wrotes this : set_gyro_angle() takes exactly 1 argument (2 given) how can I call set_gyro_angle() method from Machine method?
class Gyro(object):
"""gyroskop senzor"""
def __init__(self,gyro_start_angle = 0):
self.gyro_angle = 0
def get_gyro_angle():
return self.gyro_angle
def set_gyro_angle(angle):
self.gyro_angle = angle
return 0
class Maschine(object):
def __init__(self, state = "normal",length = 10,width = 15):
self.length = length
self.width = width
self.gyro = Gyro()
def setPoint(self,alpha):
self.gyro.set_gyro_angle(alpha)
return 0
Main:
maschine = Maschine()
maschine.setPoint()
If you want to create an instance method, you need to add an extra argument that will be a pointer to your instance. Usually it's self:
class Gyro(object):
"""gyroskop senzor"""
def __init__(self,gyro_start_angle = 0):
self.gyro_angle = 0
def get_gyro_angle(self):
return self.gyro_angle
def set_gyro_angle(self, angle):
self.gyro_angle = angle
return 0
And i think you want setPoint to be like this:
def setPoint(self, alpha):
self.gyro.set_gyro_angle(alpha)
All of your instance methods should have another parameter, self, before the others; this is the instance itself, and is passed automatically:
def set_gyro_angle(self, angle):
Alternatively, skip the setter:
self.gyro.gyro_angle = alpha
Machine.gyro.set_gyro_angle(45)
However you need to fix your code by adding the self parameter as the first parameter of your class methods.

issues with calling a superclass's method and adding the returned value to an array

When I try to enter the code below, I get [None, None] printed on the console rather than the expected [3, 3] and was wondering what would help to fix this.
class Blah(object):
def track(self,dot):
self.dot = dot
class Second(Blah):
def __init__(self,arg):
self.blocky = []
x = 0
while x < 2:
self.blocky.append(Blah.track(self,arg))
x += 1
bleh = Second(3)
print bleh.blocky
Among other more minor issues, your track method doesn't return anything, so you're passing the returned value of a function that returns nothing (None in other words) into that list.
The following worked for me:
class Blah(object):
def track(self, dot):
self.dot = dot
return self.dot
class Second(Blah):
def __init__(self, arg):
self.blocky = []
x = 0
while x < 2:
self.blocky.append(self.track(arg))
x += 1
Blah.track doesn't have a return statement, so it returns None.
You could fix this by doing:
class Blah(object):
def track(self, dot):
self.dot = dot
return dot
Also, you're calling Blah.track(self, dot) when you could just be calling self.track(dot), since self is a Second, which is a subclass of Blah.
That might look like this:
class Second(Blah):
def __init__(self,arg):
self.blocky = []
x = 0
while x < 2:
self.blocky.append(self.track(arg))
x += 1
The track method isn't returning anything. Perhaps you meant this?
def track(self, dot):
self.dot = dot
return dot
Also, since Second inherits from Blah you can replace
Blah.track(self, arg)
with
self.track(arg)

Categories

Resources