Create classes in a loop - python

I want to define a class and then make a dynamic number of copies of that class.
Right now, I have this:
class xyz(object):
def __init__(self):
self.model_type = ensemble.RandomForestClassifier()
self.model_types = {}
self.model = {}
for x in range(0,5):
self.model_types[x] = self.model_type
def fit_model():
for x in range(0,5):
self.model[x] = self.model_types[x].fit(data[x])
def score_model():
for x in range(0,5):
self.pred[x] = self.model[x].predict(data[x])
I want to fit 5 different models but I think Python is pointing to the same class 5 times rather than creating 5 different classes in the model dictionary.
This means that when I use the "score_model" method, it is just scoring the LAST model that was fit rather than 5 unique models.
I think that I just need to use inheritance to populate the model[] dictionary with 5 distinct classes but I'm not sure how to do that?

In your orignal code, you created one instance and used it five times. Instead, you want to initialize the class only when you add it to the model_types array, as in this code.
class xyz(object):
def __init__(self):
self.model_type = ensemble.RandomForestClassifier
self.model_types = {}
self.model = {}
for x in range(0,5):
self.model_types[x] = self.model_type()
def fit_model():
for x in range(0,5):
self.model[x] = self.model_types[x].fit(data[x])
def score_model():
for x in range(0,5):
self.pred[x] = self.model[x].predict(data[x])
In python everything is an object, so your variable can point to a class as well, and then your variable can be treated as a class.

Related

How class level attribute values are evaluated in python? [duplicate]

This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 3 years ago.
I've the following class structure:
class StarCount:
one_stars = 0
two_stars = 0
three_stars = 0
four_stars = 0
five_stars = 0
class OrientationAnalysis:
straight = StarCount()
bisexual = StarCount()
gay = StarCount()
class GenderAnalysis:
men = OrientationAnalysis()
women = OrientationAnalysis()
I've written the following code:
genderanalysis = GenderAnalysis()
genderanalysis.men.straight.five_stars = 100
print genderanalysis.men.straight.five_stars # outputs 100
print genderanalysis.women.straight.five_stars # this is also 100
Why genderanalysis.women.straight.five_stars is also updated? I've checked the genderanalysis.women.gay.five_stars also but it's not updated?
When you declare some variables like this:
class StarCount:
one_stars = 0
two_stars = 0
three_stars = 0
four_stars = 0
five_stars = 0
These variables become class variables. Class variables are variables that are shared by all instances of a class. So when you updated genderanalysis.men.straight.five_stars, it actually updated StarCount.five_stars and as genderanalysis.women.straight.five_stars also points to the same variable, it seemed to have updated too.
I think what you are looking for are instance variables. You can declare them like this:
class StarCount:
def __init__(self):
self.one_stars = 0
self.two_stars = 0
self.three_stars = 0
self.four_stars = 0
self.five_stars = 0
Edit
Why genderanalysis.women.gay.five_stars is not updated?
What happens is that before you updated any variable of genderanalysis object, all of the variables were pointing to the variables of StarCount class. As you can see they have same id:
print(id(StarCount.five_stars)) # prints '94016229389744'
print(id(genderanalysis.men.straight.five_stars)) # prints '94016229389744'
print(id(genderanalysis.women.gay.five_stars)) # prints '94016229389744'
But when you changed genderanalysis.men.straight.five_stars, the reference/pointer got replaced with your provided value, in this case 100. You can see difference in their id's:
print(id(StarCount.five_stars)) # prints '94016229389744'
print(id(genderanalysis.men.straight.five_stars)) # prints '94016229391328', see the difference?
So now genderanalysis.men.straight.five_stars does not point to StarCount.five_stars, rather it points to OrientationAnalysis.straight.five_stars. Once again, let's check their id's:
print(id(OrientationAnalysis.straight.five_stars)) # prints '94016229391328'
print(id(genderanalysis.men.straight.five_stars)) # prints '94016229391328', same right?
Now onto your question, at this point genderanalysis.women.gay.five_stars is still untouched so it points to StarCount.five_stars and so it still prints 0. Change StarCount.five_stars and you can see the change reflecting in genderanalysis.women.gay.five_stars.
StarCount.five_stars = 101
print(genderanalysis.women.gay.five_stars) # prints `101`
Define the values in the init methods of the classes, so that they become attached to intance objects, not the class object itself.
class StarCount:
def __init__(self):
self.one_stars = 0
self.two_stars = 0
self.three_stars = 0
self.four_stars = 0
self.five_stars = 0
class OrientationAnalysis:
def __init__(self):
self.straight = StarCount()
self.bisexual = StarCount()
self.gay = StarCount()
class GenderAnalysis:
def __init__(self):
self.men = OrientationAnalysis()
self.women = OrientationAnalysis()
genderanalysis = GenderAnalysis()
genderanalysis.men.straight.five_stars = 100
print genderanalysis.men.straight.five_stars # outputs 100
print genderanalysis.women.straight.five_stars # this is now 0
your attributes should not be class attributes but instance attributes instead. this would be a start for you:
class StarCount:
def __init__(self, five_stars=0):
self.five_stars = five_stars
# ...
class OrientationAnalysis:
def __init__(self):
self.straight = StarCount()
# ...
class GenderAnalysis:
def __init__(self):
self.men = OrientationAnalysis()
self.women = OrientationAnalysis()
you’re almost there— you are referencing and modifying class variables rather than instance variables.
You need an __init__(self) method, and to create all the attributes on self

dynamic instances of a class object overwriting each other

I have a simple class that stores simple data. The class is as follows.
class DataFormater:
def __init__(self, N, P, K, price):
self.N = N
self.P = P
self.K = K
self.price = price
The code that calls this class is
from DataFormater import DataFormater
#global variables
ObjectList = [0,1,2,3,4,5,6,7,8,9,10,
11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,
31,32,33,34,35,36,37,38,39,40,
41,42,43,44,45,46,47,48,49,50]
ObjectListCounter = 0
# main
print "enter you N-P-K values, followed by a coma, then the price"
print "example ----> 5 5 5 %50 "
print "return as many values as you want to sort, then enter, 'done!' when done."
while True:
RawData = raw_input()
if RawData == 'done!':
break
else:
ObjectList[ObjectListCounter] = DataFormater
ObjectList[ObjectListCounter].N = int(RawData[0])
# very simple test way of putting first indice in ObjectList[ObjectListCounter].N
ObjectListCounter += 1
print ObjectList[0].N
print ObjectList[1].N
My idea is that ObjectList[0] would create that object '1' that I could call with 1.N
But, when I call these, it seems that I have overwritten the previous instances.
this is what prints...
return as many values as you want to sort, then enter, 'done!' when done.
12
1
done!
1
1
Thanks so much! And I know that my post is messy, I don't exactly know how to make it more "pretty"
So, it looks like you are assigning the actual class (instead of an instance of the class) in your loop. Where you do this:
ObjectList[ObjectListCounter] = DataFormater
I think what you actually want is this
ObjectList[ObjectListCounter] = DataFormater(...insert args here....)
EDIT to address the comments:
Your class init method looks like this:
def __init__(self, N, P, K, price):
That means that to create an instance of your class, it would look like this:
my_formater = DataFormater(1, 2, 3, 4)
You would then be able to access my_formater.N which would have a value of 1.
What you are trying to do instead is access a CLASS level attribute, DataFormater.N. This is generally used in situations where you have a constant variable that does not change between instances of the class. For example:
class DataFormater():
CONSTANT_THING = 'my thing that is always the same for every instance'
You would then be able to access that variable directly from the class, like this:
DataFormater.CONSTANT_THING
I hope that clears things up.

Method changes all instances of class inside list

In my script I have these classes:
class action:
def __init__(self,ac_type,ac_date):
self.ac_type = ac_type
self.ac_date = ac_date
class user:
actions = []
def __init__(self,i_id):
self.ivi_id = i_id
def add(self,act):
self.actions.append(act)
def get_len(self):
return len(self.actions)
I want to create list of "user" elements and add to some of theme actions. I do this in the following way:
for i in range(len(data_queue)):
ind = users_id.index(data_queue[i].i_id);
act = action(0,data_queue[i].added)
users[ind].add(act)
But after running this I see that every action from data_queue was added to every user from users. This is wrong! What shall I change?
The code is using class attribute which is shared by all instances of the class and the class itself.
Use an instance attribute instead:
class user:
def __init__(self,i_id):
self.ivi_id = i_id
self.actions = []
def add(self,act):
self.actions.append(act)
def get_len(self):
return len(self.actions)
BTW, the code is using an index to iterate the sequence data_queue. Just iterate the sequence unless you really need the index.
for queue in data_queue:
ind = users_id.index(queue.i_id)
act = action(0, queue.added)
users[ind].add(act)

Trying to build trie with classes in python - strange behavior

I'm trying to implement trie in python. I'm using dictionaries+classes instead of lists (I know it's not optimal, but I'm just trying to make it work at all).
After debugging I found out that each layer has all letters in dictionary. I cannot understand why.
Here is my code (implementation is 100% most basic, straightforward):
class lttr:
finish = 0
pointers = {} #for letters to reference class instance
eps = lttr()
def add(word):
global eps
last = eps
for ind,x in enumerate(word):
if last.pointers.get(x,None):
last = last.pointers[x]
else:
last.pointers[x] = lttr()
last=last.pointers[x]
last.finish=1
def lookup(word):
global eps
last=eps
for ind,x in enumerate(word):
if last.pointers.get(x,None):
last=last.pointers[x]
else:
return False
return bool(last.finish)
add("pear")
print lookup("ar") #prints True ... but why?
I'm guessing you intended for each individual lttr instance to have its own unique values for finish and pointers. In which case, you need to declare them as attributes of self inside __init__, rather than just defining them at the class scope.
class lttr:
def __init__(self):
self.finish = 0
self.pointers = {} #for letters to reference class instance
Now your script will print False as expected.
Your lttr class has class variables, but you want instance variables. Class variables are unique across all instances of that class, so you only have one pointers object.
class lttr:
finish = 0
pointers = {} #for letters to reference class instance
What you want is
class lttr:
def __init__(self):
self.finish = 0
self.pointers = {}
and that works as expected.

Changing the order of creation of instances is changing their behaviour Python

I have a class which takes a array and calculates an answer. The Class is as follows :
class Delish:
ing = []
rmax = []
rmin = []
lmax = []
lmin = []
answer = 0
def rightmax(self):
# sets rmax
def rightmin(self):
# sets rmin
def leftmax(self):
# sets lmax
def leftmin(self):
# sets lmin
def calculate(self):
#calulates answer
def __init__(self,array):
self.ing = list(array)
self.rightmax()
self.rightmin()
self.leftmax()
self.leftmin()
self.calculate()
Now this gives output 4 13 (which is correct)
b = Delish([1,1,-1,-1])
a = Delish([1,2,3,4,5])
print a.answer,b.answer
And this gives output 7 13 (which is wrong)
a = Delish([1,2,3,4,5])
b = Delish([1,1,-1,-1])
print a.answer,b.answer
I cannot put the full code as it is a part of a live programming contest. But I want to know what is causing this weird behaviour. All of the methods are working on self. variables only. Therefore all the objects should be independent from each other right?
I can add details if it doesn't gives much of the algorithm away. Thank you.
In Python, declare instance variables within the constructor
What you're actually doing is declaring class variables. If you want instance variables in Python, you will need to declare them them in your constructor:
class Delish:
# This is a class variable.
# All instances can refer to this as self.foo
foo = 42
def __init__(self,array):
self.ing = [] # This is an instance variable
self.rmax = []
self.rmin = []
self.lmax = []
self.lmin = []
self.answer = 0
self.ing = list(array)
self.rightmax()
self.rightmin()
self.leftmax()
self.leftmin()
self.calculate()
You are probably a C++/Java programmer.
All your fields, except for ing which is assigned and thus overwritten in __init__, are class fields, that is, they are shared between all the instances of yor class. Move your instance initialisation to __init__:
class Delish:
# Nothing here
def __init__(self, array):
self.ing = list(array)
rmax = []
rmin = []
lmax = []
lmin = []
answer = 0
self.rightmax()
self.rightmin()
self.leftmax()
self.leftmin()
self.calculate()

Categories