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"))
Related
class User:
def __init__(self, username):
self.username = username
#classmethod
def get_user(cls):
return cls(input("Username: "))
class Budget(User):
def __init__(self, monthtly_income):
super().__init__(User.get_user())
self.monthly_income = monthtly_income
self.perc_lis = []
self.category_obj_lis = []
self.category_name_lis = []
self.value_lis = []
...
# creates objects and adds them to a list
for i in range(len(self.category_name_lis)):
self.category_obj_lis.append("")
self.category_obj_lis[i] = Category(self.category_name_lis[i], self.monthly_income)
...
class Category(Budget):
def __init__(self, name, monthly_income):
super().__init__(monthly_income)
self.name = name
self.perc = 0
self.amt = 0
self.left = 0
self.max = 0
# defines the percentage for each category
def get_perc(self):
self.perc = float(input(f"{self.name}: "))
When I run this it asks for username 3 times after I have asked for the categories because I create each Category object in a loop. Username is asked for at the start of the code. I need the category class to be a child class because I need the monthly_income variable to use in code not posted. I am not very good at inheritance so i could easily be missing something obvious. Is there a way for each instance of Category to not ask for username and just use the one already asked in the beginning? I apologize if this is somewhat confusing but idk where to go from here.
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
This is my experimenting code:
def settingValues():
hi = "Hello"
name = "Jake"
isTrue = True
def callingValues():
print(hi, name, isTrue)
settingValues()
callingValues()
Obviously this emmits an error...
Is there an easy way to transfer the variables throughout the two functions?
What I will be using this for requires setting quite a lot of variables... so I cant use 'global' for all of them. :)
Just use OOP:
class SomeClass:
def __init__(self):
self.hi = "Hello"
self.name = "Jake"
self.isTrue = True
def callingValues(self):
print(self.hi, self.name, self.isTrue)
sc = SomeClass()
sc.callingValues()
The easiest way would be to define them as globals, which could then be accessed from all the functions in the same module
hi = ""
name = ""
isTrue = False
def settingValues():
hi = "Hello"
name = "Jake"
isTrue = True
def callingValues():
print(hi, name, isTrue)
settingValues()
callingValues()
This question already has answers here:
How to avoid having class data shared among instances?
(7 answers)
Closed 8 years ago.
I'd like to take a list from football player (which have few attributes like name, goals, club...) and add them to their club (which is another class) but it seems that I'm missing something because the list of players of the club is changing in the loop even if it's not called (I think I'm not correctly managing the instances of the players).
So, here is the code :
clubsWithGoal = []
class Player:
nickname = ""
imageURL = ""
numberOfGoal = 0
clubId = ""
def __init__(self, nickname, imageURL, clubId, numberOfGoal = 0):
self.nickname = nickname
self.imageURL = imageURL
self.clubId = clubId
self.numberOfGoal = numberOfGoal
def __str__(self):
return self.nickname
class Club:
Name = ""
ImageURL = u""
id = u""
numberOfGoal = 0
listOfPlayer = []
def __init__(self, id):
del self.listOfPlayer [:]
self.id = id
self.getData()
def __str__(self):
return self.Name
def getData(self):
try:
results = json.load(urllib.urlopen(
"http://worldcup.kimonolabs.com/api/clubs/" + self.id + "?apikey={youwon'tseeit}"))
self.ImageURL = results["logo"]
self.Name = results["name"]
except:
print(self.id)
def addGoal(self, numberOfGoalsToAdd):
self.numberOfGoal += numberOfGoalsToAdd
def addPlayer(self, player):
self.listOfPlayer.append(player)
print("added "+player.nickname+" to "+self.Name)
self.addGoal(player.numberOfGoal)
print("added the "+str(player.numberOfGoal)+" of "+player.nickname+" to "+self.Name)
So here are for the model class and here is the function which must sort the players and is not working:
def createAndOrderInClub(playerlist):
foundHisClub = False
for player in playerlist:
for club in clubsWithGoal:
# Case 1: The club already exists and the player is part of the club
if player.clubId == club.id:
club.addPlayer(player)
foundHisClub = True
break
# Case 2: The club doesn't already exist
if (foundHisClub == False):
newclub = Club(player.clubId)
newclub.addPlayer(player)
clubsWithGoal.append(newclub)
And an example that it's changing inside the loop (I'm java developer and new to Python):
I think the problem is that the listOfPlayer variable in the Club class is declared as a static class member, and is not initialized inside the __init__ function. This demo http://dbgr.cc/R demonstrates the point.
Besides the error above, it also looks like you're not resetting the foundHisClub variable inside the loop back to False. I would instead declare the foundHisClub variable inside the first for loop:
def createAndOrderInClub(playerlist):
for player in playerlist:
foundHisClub = False
for club in clubsWithGoal:
# Case 1: The club already exists and the player is part of the club
if player.clubId == club.id:
club.addPlayer(player)
foundHisClub = True
break
# Case 2: The club doesn't already exist
if (foundHisClub == False):
newclub = Club(player.clubId)
newclub.addPlayer(player)
clubsWithGoal.append(newclub)
listOfPlayer = []
This is a class attribute, meaning it's shared for all instances of your Club class. If you're coming from Java you can think of this as a static class variable. To make this list unique for each Club class, make sure you initialize it in the constructor with the self prefix.
def __init__(self, id):
del self.listOfPlayer [:]
self.id = id
self.listOfPlayer = []
self.getData()
Make sure you do the same for all the other variables you've defined at the class level:
Name = ""
ImageURL = u""
id = u""
numberOfGoal = 0
Remove these, and initialize them in the constructor using self.
The listOfPlayer container, as you declared it, is a "class variable" (a bit like a static class member in java), and since lists are mutable, whenever you modify this list in any instance of Club, it will change for all instances as well.
To fix it simply remove it from the class definition, and initialize the list in the __init__ function (the strings aren't problematic since they are immutable):
class Club:
Name = ""
ImageURL = u""
id = u""
numberOfGoal = 0
def __init__(self, id):
self.listOfPlayer = []
self.id = id
self.getData()
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()