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
Related
I have a python class called Player that looks like this ...
class Player:
def __init__(self):
self.display = 'A'
self.num_water_buckets = 0
self.row = 0
self.col = 0
I am having trouble editing the self.row and self.col ... maybe they're immutable but I haven't found much of this on the internet. Player().row and Player().col work, however they print 0 every time. I am trying to edit them using Player().row = value where value is a variable containing a number.
Thanks in advance!
UPDATE: Okay so I understand that you have to define a new occurrence of this instance using x = Player() then x.row = ...
But I need to originally initialise the variable with certain values (which are different every time) so I thought I could use a function and the return value from that function would set the initial value from the variable but it just creates an infinite recursion.
I did...
class Player:
def __init__(self):
from game_parser import read_lines
self.display = 'A'
self.num_water_buckets = 0
self.row = Player().detRow(sys.argv[1])
self.col = Player().detCol(sys.argv[1])
def detRow(fileName):
# use this function to return the row
def detCol(fileName):
# use this function to return the col
If you do
Player().row = value
print(Player().row)
The Player() in the first row creates a new instance; then you modify it by setting its row attribute to value; then you throw away that instance, because you haven't bothered to remember it anywhere. Then in the next row, you create a new instance (where the initialiser sets the row attribute to zero).
Not all players are interchangeable. If you sit John in the second row, it does not mean Harry will be in the second row too; it just means John is in the second row.
To do it correctly, store your Player instance in a variable:
john = Player()
john.row = 2
print(john.row)
You need to add put the variable outside the __init__() method
EDIT: added row and col to the class
class Player:
row = 0 # NO self here
col = 0
def __init__(self):
self.display = 'A'
self.num_water_buckets = 0
john = Player()
john.num_water_buckets = 12
print(f'before change: {john.col}')
john.col = 5
print(f'after change: {john.col}')
print(f'class property: {Player.col}') # note it did not change the class
Output:
before change: 0
after change: 5
class property: 0
I am attempting a practice task I found in an old programming book to increase my knowledge of classes in Python. The task is to create a program which allows a user to set up a series of tests for a school. Each test must contain no more than 10 questions. The task stated that the best way to do this was to use containment, and have the class 'Question' inside the class 'Test'
Basically, I should set up a class called Test which dewfines the basics of the whole test, and then a class called Quesion which sets up the question and passes it back to Test to be included in the array there. I'm having 2 major problems. Firstly, how do I get the setQuestion object in the Question class to pass data in to the Question array in the Test class. Secondly, how do I have the setQuestion object iterate the variable numberofQuestions since that's contained in the Test Class.
Here is the code. Not sure it's clear from the formatting but the Question class is inside the Test class:
class Test(object):
def __init__(self):
self.__testID = 0
self.__maxMarks = 0
self.__questions = []
self.__numberofQuestions = 0
self.__level = ""
self.__dateSet = ""
class Question(object):
def __init__(self):
self.__questionID = 0
self.__questionText = ""
self.__answer = ""
self.__marks = 0
self.__topic = ""
def setQuestion(self, questionID, questionText, answer, marks, topic):
self.__numberofQuestions = self.__numberofQuestions + 1
self.__questionID = self.__questionID
self.__questionText = self.__questionText
self.__answer = self.__answer
self.__marks = self.__marks
self.__topic = self.__topic
This is how I would do that:
class Test(object):
def __init__(self,id,marks):
self.__testID = id
self.__maxMarks = marks
self.__questions = []
self.__numberofQuestions = 0
self.__level = ""
self.__dateSet = ""
def setQuestion(self,question):
self.__numberofQuestions += 1
self.__questions.append(question)
class Question(object):
def __init__(self,id,text,answer,marks,topic):
self.__questionID = id
self.__questionText = text
self.__answer = answer
self.__marks = marks
self.__topic = topic
Now you can put question objects into the __question array of Test like that:
if __name__ == "__main__":
test = Test(1,100)
test.setQuestion(Question(1,"Text","Answer",50,"Topic"))
I have the following tile class which got some instance variables such as UNMAPPED,FREE etc.
Those instance variables get a default value once new instance is created, but i want to set some of this variables from a method within the instance itself.
when im trying to do so, it seems the the new value is not saved but when im doing it without any method it seems to work.
How can it be?
class tile():
def __init__(self):
self.FREE = 0
self.UNMAPPED = 1
def set_tile(self,newVal):
self.FREE = newVal
so that :
tile1 = tile()
tile1.set_tile(20) -> tile1.FREE = 0
but,
tile1.FREE = 20 -> tile1.FREE = 20
thanks for your help
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.
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()