Using a class as a dictionary value in python - python

I'm new to to Python and could use some help. I'm trying to use several class parameters as a dictionary value and I don't know how to return the value with the class's parameter variables. Here's what I have so far:
import random
class Movie:
def __init__(self, title)
self.__title=title
def __str__(self):
return
questions={
"May the Force be with you.":Movie("Star Wars: Episode IV - A New Hope",1977,"George Lucas",["Sci-fi","Action"],121,"PG")
}
print("Here's a random selection of", 2,"questions:")
rset = random.sample(list(questions), 2)
print()
#Accumulator Start
total=0
qt=0
#Question Loop
for q in rset:
qt+=1
print("Question #",qt,":",q)
ans=input('Answer: ')
if ans.casefold()==Movie(self.__title).casefold():
print('Correct! The answer is:' ,questions[q])
print()
total+=1
else:
print("Incorrect. The answer is:", questions[q])
print()
I'd like the questions[q] to return class Movie if possible. Any suggestions?

You can't use self outside a class.
Just use questions[q] you can return a instance of Moive class, no need to return a class itself in the situation.
The attribute start with __ treat as private in python, which can't access from outside.
code:
import random
class Movie:
def __init__(self, title, releaseYear, director, genre, length, rating):
self.title=title
self.releaseYear=releaseYear
self.director=director
self.genre=genre
self.length=length
self.rating=rating
def __str__(self):
return
questions={
"May the Force be with you.":Movie("Star Wars: Episode IV - A New Hope",1977,"George Lucas",["Sci-fi","Action"],121,"PG"),
"test":Movie("test_title",1978,"test_director",["test1","test2"],99,"test_rating")
}
#Determine quantity
quantity=int(input("How many questions? "))
print()
print("Here's a random selection of", quantity,"questions:")
rset = random.sample(list(questions), quantity)
print()
#Accumulator Start
total=0
qt=0
#Question Loop
for q in rset:
qt+=1
print(f"Question # {qt}:{q}")
ans=input('Answer: ')
if ans.casefold()==questions[q].title.casefold():
print('Correct! The answer is:' ,questions[q].title.casefold())
print()
total+=1
else:
print("Incorrect. The answer is:", questions[q].title.casefold())
print()
result:
How many questions? 2
Here's a random selection of 2 questions:
Question # 1:test
Answer: a
Incorrect. The answer is: test_title
Question # 2:May the Force be with you.
Answer: Star Wars: Episode IV - A New Hope
Correct! The answer is: star wars: episode iv - a new hope

Yes, that is possible. However, Python dicts are unordered. So return the value of a dict via index does not make sense, But if you want, you can do:
value_at_index = dic.values()[index]
Instead, you will want to return the value via key, for your code:
value = questions["May the Force be with you."]
but right now, for your str method, you did not return anything. Keep in mind that str should return a string. For example you want to return the title, you code would be like:
import random
class Movie:
def __init__(self, title, releaseYear, director, genre, length, rating):
self.__title = title
self.__releaseYear = releaseYear
self.__director = director
self.__genre = genre
self.__length = length
self.__rating = rating
def __str__(self):
return self.__title
questions = {
"May the Force be with you.": Movie("Star Wars: Episode IV - A New Hope", 1977, "George Lucas",
["Sci-fi", "Action"], 121, "PG")
}
# Determine quantity
print(questions)
for keys in questions:
print(questions[keys])
Which will output:
{'May the Force be with you.': <__main__.Movie object at 0x00000268062039C8>}
Star Wars: Episode IV - A New Hope
Process finished with exit code 0

Related

How to stop code from repeating in all of the code

I’m new and still learning but I’m making a random character Generator and I don’t know how to make it stop or end the code and start a new one.
import random
Rac = ["Human", "Half-Human", "Not Human"]
Race = random.choice(Rac)
Age = random.randint(1,100)
skil = [“Beserker”,”Mage”,”Assassin”]
Skill = random.choice(skil)
Gende = [“Male”,”Female”]
Gender = random.choice(Gende)
class human:
def __dir__(self):
return[Race,Age,Skill,Gender]
person = human()
print(dir(person))
print(“Race: {}, Age: {}, Class: {}, Gender: {}”.format(Race,Age,Class,Gender))
I have two of these but the ages keep printing out the same and I don’t know how to separate them so it’s a different age and not the same number for both.
I need to end the Age for that generator because it interferes and makes it all the same age, Instead of random ages.
Sorry for being confusing.
Based on what I understood,
First of all, I have corrected the code and removed syntax errors.
(Below given code is not an answer. It is just the updated code. I have given my answer below that.)
import random
Rac = ["Human", "Half-Human", "Not Human"]
Race = random.choice(Rac)
Age = random.randint(1,100)
skil = ["Beserker", "Mage", "Assassin"]
Skill = random.choice(skil)
Gende = ["Male", "Female"]
Gender = random.choice(Gende)
class human:
def __dir__(self):
return [Race, Age, Skill, Gender]
person = human()
print(dir(person))
print("Race: {}, Age: {}, Skill: {}, Gender: {}".format(Race, Age, Skill, Gender))
My answer :
Now,
When you create the random object,
Here : Age = random.randint(1,100)
It gets called only once, so only one random number is generated and you are using it 2 times.
I have two of these but the ages keep printing out the same and I don’t know how to separate them how do I separate them so it’s a different age and not the same number for both
What you need, is to generate this number inside the class definition. So it will get called every time you create the object of that class.
Code:
import random
class human:
def __dir__(self) -> list:
self.Rac = ["Human", "Half-Human", "Not Human"]
self.Race = random.choice(self.Rac)
self.Age = random.randint(1,100)
self.skil = ["Beserker", "Mage", "Assassin"]
self.Skill = random.choice(self.skil)
self.Gende = ["Male", "Female"]
self.Gender = random.choice(self.Gende)
return [self.Race, str(self.Age), self.Skill, self.Gender]
person = human()
print(dir(person))
(Note the str() conversion of self.Age)
Now some other knowledge:
dir() method calls __dir__ method in the class and __dir__ is supposed to return list containing items of similar data types (int or str).
This means list should contain all integers or all strings.
For example, code:
class Hello():
def __dir__(self):
return ["1", "2", "3"]
print(dir(Hello()))
Output: ['1', '2', '3']
if:
class Hello():
def __dir__(self):
return [1, "2", 3]
print(dir(Hello()))
Output: TypeError: '<' not supported between instances of 'str' and 'int'
For more information: What's the difference between dir() and __dir__?

How do I make a class instance using user input?

I am making a text based adventure game in python. Once the game begins, I would like to create an instance of a class called "Character" which is the player's character object. I would like the user to be able to choose the race of the character they want to play. So far I have:
class Race:
def __init__(self, name, passive, hp):
self.name = name
self.passive = passive
self.hp = hp
and
class Lizard(Race):
def __init__(self, name, passive, hp):
super().__init__(name, passive, hp)
self.name = 'Lizardman'
self.passive = 'Regrowth'
self.hp = 20
def regrowth(self):
if 0 < self.hp <= 18:
self.hp += 2
and
def race_select():
races = ['Lizard']
while True:
for i, j in enumerate(races):
print(f"[{i + 1}]", j)
choice = int(input('Pick a race:'))
if choice <= len(races):
print('You are a ', races[choice - 1])
return races[choice - 1]
else:
continue
If I understand correctly, if I wanted the race to be a Lizard, I would still have to do
character = Lizard('Lizardman', 'Regrowth', 20)
Is there an easy way to let the user choose the race and the object to be created accordingly? Thanks
A simple solution would be to map a name to a class using a dictionary. As a simple example:
race_map = {"lizard": Lizard,
"human": Human} # I'm adding a theoretical other class as an example
choice = input('Pick a race:')
race_initializer = race_map.get(choice, None) # Get the chosen class, or None if input is bad
if race_initializer is None:
# They entered bad input that doesn't correspond to a race
else:
new_creature = race_initializer(their_name, their_passive, their_hp)
new_creature is now the new object of the chosen class.
You may want to standardize the input using choice.lower() to ensure that capitalization doesn't matter when they enter their choice.
I changed it to allow for specifying a race by a string name instead of a number. If you wanted a number, you could keep your list, but apply the same idea. Something like:
race_list = races = [('Lizard', Lizard), ('human', Human)]
choice = int(input('Pick a race:'))
try:
race_initializer = race_list[choice][1] # 1 because the class object is the second in the tuple
new_creature = race_initializer(their_name, their_passive, their_hp)
except IndexError:
# Bad input
I included the name in the race_list so that you can loop over the list and print out index->name associations for the user to pick from.
You may also want to use a more robust structure than a plain tuple to store name->initializer mappings, but it works well in simple cases.

Can't run through a list in print function

I'm a beginner to python. I've written this code but I can't execute the for loop inside the print function to iterate through the list of marks:
class student:
def __init__(self, name, age, iden, lMark):
self.name=name
self.age=age
self.iden=iden
self.lMark=lMark
def printAve(self, obj):
for i in obj.lMark:
res+=i
av=res/len(lMark)
return av
def disInfo(self, obj):
print("the name is: ",obj.name)
print("the age is: ",obj.age)
print("the identity is: ",obj.iden)
print("the marks are: ", for i in lMark:
print(i))
def addStudent(self, n, a, i, l):
ob=student(n,a,i,l)
ls.append(ob)
return true
def searchStudent(self, _val):
for i in len(ls):
if ls[i]==_val:
return i
ls=[]
s=student("Paul", 23,14, list(15,16,17,20,12))
bool added = s.add("Van", 20, 12, list(12,18,14,16))
if added==True:
print("Student added successfully")
for i in range(ls.__len__())
s.disInfo(ls[i])
Can someone help me to solve this problem and explain me how to do?
You can't put a bare for loop in the argument list to print, and of course the embedded print doesn't return what it prints. What you can do is
print("the marks are:", ", ".join(lMark))
or perhaps
print("the marks are:", *lMark)
(Also, I believe you mean obj.lMark.)
To reiterate, if you call some code in the argument to print, that code should evaluate to the text you want print to produce; not do printing on its own. For example,
def pi()
return 3.141592653589793 # not print(3.141592653589793)
def adjective(arg):
if arg > 100:
return "big" # not print("big")
else:
return "small" # not print("small")
print(str(pi()), "is", adjective(pi()))
Notice how each function call returns something, and the caller does something with the returned value.
from statistics import mean
# classes are named LikeThis
# constants named LIKE_THIS
# other variables should be named like_this
# hungarian notation is not used in Python
class Student:
# instead of a global variable named ls,
# I'm using a class variable with a clear name instead
all_students = []
def __init__(self, name, age, iden, marks):
self.name = name
self.age = age
self.iden = iden
self.marks = marks
def printAve(self):
# you don't need to pass two arguments when you're only operating on a single object
# you don't need to reinvent the wheel, see the import statement above
return mean(self.marks)
def disInfo(self):
print("the name is:", self.name)
print("the age is:", self.age)
print("the identity is:", self.iden)
# you can't put a statement inside of an expression
# I'm guessing you want the marks all on the same line?
# the *args notation can pass any iterable as multiple arguments
print("the marks are:", *self.marks)
# this makes more sense as a classmethod
# clear variable names are important!
#classmethod
def addStudent(cls, name, age, iden, marks):
# I'm using cls instead of Student here, so you can subclass Student if you so desire
# (for example HonorStudent), and then HonorStudent.addStudent would create an HonerStudent
# instead of a plain Student object
cls.all_students.append(cls(name, age, iden, marks))
# note the capital letter!
return True
# again, this should be a classmethod
#classmethod
def searchStudent(cls, student):
# use standard methods!
try:
return cls.all_students.index(student)
except ValueError:
return None
# the literal syntax for lists in Python is `[1, 2, 3]`, _not_ `list(1, 2, 3)`.
# it also makes more sense to use Student.addStudent here, because just calling Student() doesn't add it
# to the list (although you could do that in __init__ if you always want to add them to the list)
Student.addStudent("Paul", 23, 14, [15, 16, 17, 20, 12])
# in Python, type annotations are optional, and don't look like they do in C or Java
# you also used `add` instead of `addStudent` here!
added: bool = Student.addStudent("Van", 20, 12, [12,18,14,16])
# don't need == True, that's redundant for a boolean value
if added:
print("Student added successfully")
# never call `x.__len__()` instead of `len(x)`
# (almost) never use `for i in range(len(x))` instead of `for item in x`
for student in Student.all_students:
student.disInfo()
First I answer your initial question, you can print a list in different ways, here are some of them.
You can iterate through it with a for loop and print every element on a different line:
for elem in self.lMark:
print(elem)
You can also append all values to a string and separate them by a character or something.
(Note: There will be a trailing space at the end.)
myString = ""
for elem in self.lMark:
myString = myString + str(elem) + " "
print(myString)
Better is this by doing it with strings-join method and a short version of the container iteration:
", ".join([str(i) for i in self.lMark])
There were some more issues in the code example.
Here is a running version of the script:
class Student:
def __init__(self, name, age, iden, lMark):
self.name=name
self.age=age
self.iden=iden
self.lMark=lMark
def printAve(self, obj):
for i in obj.lMark:
res+=i
av=res/len(self.lMark)
return av
def disInfo(self):
print("the name is: ",self.name)
print("the age is: ",self.age)
print("the identity is: ",self.iden)
print("the marks are: ", ", ".join([str(i) for i in self.lMark]))
class StudentList:
def __init__(self):
self.__list_of_students = []
def add(self, s):
self.__list_of_students.append(s)
return True
def get(self):
return self.__list_of_students
def search(self, name):
for s in self.__list_of_students:
if s.name == name:
return s
return None
ls = StudentList()
s=Student("Paul", 23,14, [15,16,17,20,12])
ls.add(s)
added = ls.add(Student("Van", 20, 12, [12,18,14,16]))
if added:
print("Student added successfully")
search_name1 = "Paula"
search_name2 = "Van"
if ls.search(search_name1):
print(search_name1, "is part of the list!")
else:
print("There is no", search_name1)
if ls.search(search_name2):
print(search_name2, "is part of the list!")
else:
print("There is no", search_name2)
for student in ls.get():
student.disInfo()
print("-"*10)
I would suggest to separate the list of students and the students to two different classes as shown in the code above.
You can unpack your list with the * operator:
print("the marks are: ", *lMark)
Try setting this as a function. i.e
def quantity():
for i in lMark:
print(i)
Then,
def disInfo(self, obj):
print("the name is: ",obj.name)
print("the age is: ",obj.age)
print("the identity is: ",obj.iden)
print("the marks are: ", + quantity)

A confusing object composition python code

Currently, I am on an online crash course on python and I encountered a confusing code.
As shown below, it is a code that is designed to find out the number of cotton polo shirts.
class Clothing:
stock={ 'name': [],'material' :[], 'amount':[]}
def __init__(self,name):
material = ""
self.name = name
def add_item(self, name, material, amount):
Clothing.stock['name'].append(self.name)
Clothing.stock['material'].append(self.material)
Clothing.stock['amount'].append(amount)
def Stock_by_Material(self, material):
count=0
n=0
for item in Clothing.stock['material']:
if item == material:
count += Clothing.stock['amount'][n]
n+=1
return count
class shirt(Clothing):
material="Cotton"
class pants(Clothing):
material="Cotton"
polo = shirt("Polo")
sweatpants = pants("Sweatpants")
polo.add_item(polo.name, polo.material, 4)
sweatpants.add_item(sweatpants.name, sweatpants.material, 6)
current_stock = polo.Stock_by_Material("Cotton")
print(current_stock)
it is obvious that the number of cotton polo shirts is 4 and yet the code gives 10, the sum of the number of cotton polo and sweatpants, as the answer (which is considered a correct one actually).
My question is, shouldn't the polo.Stock_by_Material method only iterates elements in the dictionary in the instance "polo" instead of both "polo" and "sweatpants"? I mean "polo" and "sweatpants" are not even in the same class so how come the polo.Stock_by_Material method would count the amount of both classes?
Please forgive me if I made some stupid mistakes here. I am only 1 week into python without any prior programming experience. Many thanks!
You are aggregating by the material (Cotton). Both the shirt and sweatpants class has the material attribute set as Cotton. Hence there are 10 Cotton items, which is what you are displaying at the end.
If you want to aggregate by item, you could do as shown below.
class Clothing:
stock={ 'name': [],'material' :[], 'amount':[]}
def __init__(self,name):
material = ""
self.name = name
def add_item(self, name, material, amount):
Clothing.stock['name'].append(self.name)
Clothing.stock['material'].append(self.material)
Clothing.stock['amount'].append(amount)
def Stock_by_Material(self, material):
count=0
n=0
for item in Clothing.stock['material']:
if item == material:
count += Clothing.stock['amount'][n]
n+=1
return count
def Stock_by_item(self, name):
count=0
n=0
for rec in Clothing.stock['name']:
if rec == name:
count += Clothing.stock['amount'][n]
n+=1
return count
class shirt(Clothing):
material="Cotton"
class pants(Clothing):
material="Cotton"
polo = shirt("Polo")
other_polo_shirts = shirt("Polo")
sweatpants = pants("Sweatpants")
polo.add_item(polo.name, polo.material, 4)
other_polo_shirts.add_item(other_polo_shirts.name, other_polo_shirts.material, 16)
sweatpants.add_item(sweatpants.name, sweatpants.material, 6)
current_stock = polo.Stock_by_item("Polo")
print(current_stock)
If i got your question right,
stock is a static variable for the class Clothing. any children classes of this will share this variable.
Hence both polo and sweatpants share the same dictionary.
Hope that is helpful.
As #Sagi mention it returns all cotton stock as stock is shared between objects of Cloathing and its subclasses. Your confusion however is reasonable as this code breaks single responsibility principle, stock shouldn't be part of Clothing class.
Sagi is correct. The Stock_by_Material function needs to also check 'name' to make sure it is 'Polo', only then adding it to the count. You're not missing anything, the makers of the course just made an error.
In this conditional statement create problem in iteration so, try to comment it out. Sourly your program will run.
class Clothing:
stock={ 'name': [],'material' :[], 'amount':[]}
def __init__(self,name):
material = ""
self.name = name
def add_item(self, name, material, amount):
Clothing.stock['name'].append(self.name)
Clothing.stock['material'].append(self.material)
Clothing.stock['amount'].append(amount)
def Stock_by_Material(self, material):
count=0
n=0
for item in Clothing.stock['amount']:
# if item == material:
count += Clothing.stock['amount'][n]
n+=1
return count
class shirt(Clothing):
material="Cotton"
class pants(Clothing):
material="Cotton"
polo = shirt("Polo")
sweatpants = pants("Sweatpants")
polo.add_item(polo.name, polo.material, 4)
sweatpants.add_item(sweatpants.name, sweatpants.material, 6)
current_stock = polo.Stock_by_Material("Cotton")
print(current_stock)

How can I turn a user input into a global function? (Python)

I'm trying to create my own text game, as instructed in Ex 45 of Learning Python the Hard Way, but I'm a bit stuck. I have my own inventory system set up and I need to figure out how to display a list of the inventory any time the user types "inventory" (I also want to do similar things for "help")... I know that I could add an if-statement after every raw_input for it, but I wanted to see if there was some global function or code that I could use to make this a lot easier on me.
I've searched and searched but I have no idea how to phrase the question or what I could possibly do to fix this. Here's my inventory set-up, with how I'm using to call it:
class Item(object):
def __init__(self, name, quantity=1):
self.name = name
self.raw = name.strip().lower()
self.quantity = quantity
def recalc(self):
self.netValue = self.quantity * self.value
class Container(object):
def __init__(self, name):
self.name = name
self.inside = {}
def __iter__(self):
return iter(self.inside.items())
def __len__(self):
return len(self.inside)
def __contains__(self, item):
return item.raw in self.inside
def __getitem__(self, item):
return self.inside[item.raw]
def __setitem__(self, item, value):
self.inside[item.raw] = value
return self[item]
def add(self, item, quantity=1):
if quantity < 0:
print "ERROR."
if item in self:
self[item].quantity += quantity
self[item].recalc()
else:
self[item] = item
inventory = Container("Inventory")
And here's what I'm using to call it when inventory is typed:
print "Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"
You could create a function for each valid user input, such as inventory
Then, you could do:
input = raw_input('...')
self.method = getattr(<your class>, input)
self.method()
which would call
def inventory(self, ...):
pass
you could use a dictionary combined with lambdas, this would be compact and fast
from __future__ import print_function
#store commands
inputLookup = {"Inventory" : lambda: print ("Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"), "other text" : lambda: 3} #and so on
text = input() #replace with hover you get input
inputLookup[text]() # call the function from dictionary at that command
the import will change all your print statements to a print() however.
For lambdas you can also do.
def f():
print "Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"
inputLookup = {"Inventory": lambda: f()}
text = input()
try:
inputLookup[text]
except:
print "that is not a valid input" #or something
this will allow you to use the old print statment
yes, you should never use recursion to the same function in this manner, you'll generate a large stack. Also, those print statements are in a weird place.
def func1():
print "Uh oh."
print "You're outside the house and it's quiet."
print "You EXAMINE and find a ROCK."
While True:
userinput1 = raw_input(">>> ")
if userinput1 == "inventory":
print "Inventory: [" + ",".join((item[1].name for item in inventory)) + "]"
elif userinput1 == "pick up":
if rock not in inventory: # so they can't pick up infinite rocks
inventory.add(rock)
print "You picked up a ROCK."
else:
print "you've already picked up the rock"
elif userinput1 == "examine":
print "Man's best friend."
else:
print "Let's go on about our way then."
break
I failed to see you were just starting out. I would stay away from lambda's for the moment, as they're a bit more advanced, sorry for the confusion. Before, if you look at your if/elif/else tree, there was no way for the else statement to be called, so i've edited it a bit, i hope it functions the way you intended. Also, are inventory and rock globals? if not you would need to declare or reference them in the function. (although as a future note, staying a way from globals is generally good)
hmm...i assume you mean items like the rock and such.
Items = {'rock': Rock(), 'item2': Item2(), 'item3' : item3()} #where Item() is creating the object
Inventory.add(Items[itemname]) # adds the item
You could make Items as a global so you can access it everywhere. This is ok for now, but as you learn, you should generally stay away from globals.

Categories

Resources