Python custom modules - error with example code - python

I am reading the book "Python Programming for the Absolute Beginner (3rd edition)". I am in the chapter introducing custom modules and I believe this may be an error in the coding in the book, because I have checked it 5 or 6 times and matched it exactly.
First we have a custom module games.py
class Player(object):
""" A player for a game. """
def __init__(self, name, score = 0):
self.name = name
self.score = score
def __str__(self):
rep = self.name + ":\t" + str(self.score)
return rep
def ask_yes_no(question):
""" Ask a yes or no question. """
response = None
while response not in ("y", "n"):
response = input(question).lower()
return response
def ask_number(question, low, high):
""" Ask for a number within a range """
response = None
while response not in range (low, high):
response = int(input(question))
return response
if __name__ == "__main__":
print("You ran this module directly (and did not 'import' it).")
input("\n\nPress the enter key to exit.")
And now the SimpleGame.py
import games, random
print("Welcome to the world's simplest game!\n")
again = None
while again != "n":
players = []
num = games.ask_number(question = "How many players? (2 - 5): ", low = 2, high = 5)
for i in range(num):
name = input("Player name: ")
score = random.randrange(100) + 1
player = games.Player(name, score)
players.append(player)
print("\nHere are the game results:")
for player in players:
print(player)
again = games.ask_yes_no("\nDo you want to play again? (y/n): ")
input("\n\nPress the enter key to exit.")
So this is exactly how the code appears in the book. When I run the program I get the error IndentationError at for i in range(num):. I expected this would happen so I changed it and removed 1 tab or 4 spaces in front of each line from for i in range(num) to again = games.ask_yes_no("\nDo you want to play again? (y/n): ").
After this the output is "Welcome to the world's simplest game!" and that's it.
I was wondering if someone could let me know why this is happening?
Also, the import games module, is recognized in Eclipse after I added the path to PYTHONPATH.

I actually have this book myself. And yes, it is a typo. Here is how to fix it:
# SimpleGame.py
import games, random
print("Welcome to the world's simplest game!\n")
again = None
while again != "n":
players = []
num = games.ask_number(question = "How many players? (2 - 5): ", low = 2, high = 5)
for i in range(num):
name = input("Player name: ")
score = random.randrange(100) + 1
player = games.Player(name, score)
players.append(player)
print("\nHere are the game results:")
for player in players:
print(player)
again = games.ask_yes_no("\nDo you want to play again? (y/n): ")
input("\n\nPress the enter key to exit.")
All I did was indent num 4 spaces and lined it up with the first for-loop.

You have an infinite loop here:
again = None
while again != "n":
players = []

If this is exactly the way it's printed in the book, the book does have an error.
You've got these two lines:
num = games.ask_number(question = "How many players? (2 - 5): ", low = 2, high = 5)
for i in range(num):
The second one is more indented than the first. That's only legal if the first one is a block-introducer like a for or while or if. Since it's not, this is an IndentationError. And that's exactly what Python is telling you.
(It's possible that you've copied things wrong. It's also possible that you're mixing tabs and spaces, so it actually looks right in your editor, but it looks wrong to Python. But if neither of those is true, the book is wrong.)
So, you attempted to fix it by dedenting everything from that for loop on.
But when you do that, only one line is still left under the while loop:
while again != "n":
players = []
There's nothing that can possibly change again to "n", so this will just spin forever, doing nothing, and not moving on to the rest of the program.
So, what you probably want to do is to indent the num = … line to the same level as the for i… line, so both of them (and all the stuff after) ends up inside the while loop.

Related

if statement won't execute all of the commands in its indentation level

I am working on a stupid yet funny practice program to improve my understanding of OOP in Python.
The program is meant to randomly generate some band names from a randomly selected adjective and another randomly selected noun - producing a lot of hilarious band names.
For the most part, the program works fine, but for some reason, there are some problems with the if-statements and the while loop in the menu(self)- method all the way down in the BandList class.
My hypothesis is that there is something wrong with the nesting of the else-if statements, or that the loop doesn't manage to advance the loop when I call on the self._generateBand() method in line 60 due to some technicality I'm not aware of. Either way, I'm not sure.
However, my question is:
Why does my loop stop at the line self._writeBand() and not continue executing the code that follows? (As shown below)
done = False
while done != True:
print("\n=============== BAND NAME GENEREATOR ==================")
start = input("\nDo you want to generate a list of bandnames? (y/n): ")
if start.lower() == "y":
self._generateBand()
self._writeBand() #The loop stops here for some reason and asks the same question over and over.
#The program won't execute this part of the code.
inp = ("\nDo you want to save these band names? (y/n): ")
if inp.lower() == "y":
outfile = input("What do you want to name the file?: ")
self._saveBand(f"{oufile}.txt")
If anyone can help me fix this, I would be super grateful.
In advance: Thank you for your help.
The complete program is pasted in below
import random
class Band:
def __init__(self, name):
self._Bandname = name
def __str__(self):
return f"{self._Bandname}"
def hentName(self):
return self._Bandname
class BandList:
def __init__(self):
self._BandList = []
def _readFile(self, filename1, filename2):
with open(filename1) as infile1, open(filename2) as infile2:
lineAdjective = infile1.read().splitlines()
lineNoun = infile2.read().splitlines()
adjective = random.choice(lineAdjective)
noun = random.choice(lineNoun)
return f"{adjective} {noun}"
def _saveBand(self, filename):
with open(filename, "w") as outfile:
for j, i in enumerate(self._BandList):
outfile.write(f"Nr: {j}\t-{i}\n")
def _generateBand(self):
num = int(input("\nHow many band names would you like to generate?: "))
for i in range(num):
bandname = f"The {self._readFile('adjective.txt', 'noun.txt')}s"
self._BandList.append(Band(name= bandname))
def _writeBand(self):
print("\n========= Genererte bandname =========")
for i in self._BandList:
print(i)
#print(i.hentName())
def _deleteBand(self):
self._BandList.clear()
def _writeGoodbyeMsg(self):
print("\n============ PROGRAM TERMINATING ================")
print("\t- thanks for using the program, goodbye!")
def menu(self):
done = False
while done != True:
print("\n=============== BAND NAME GENEREATOR ==================")
start = input("\nDo you want to generate a list of bandnames? (y/n): ")
if start.lower() == "y":
self._generateBand()
self._writeBand() #This is probably where the bug is...
inp = ("\nDo you want to save these band names? (y/n): ")
if inp.lower() == "y":
utfil = input("What do you want to name the file?: ")
self._saveBand(f"{utfil}.txt")
elif inp.lower() == "n":
self._deleteBand()
inp2 = input("Do you want to generate more band names? (y/n)?: ")
if inp2.lower() == "y":
self._generateBand()
elif inp2.lower() == "n":
done = True
self._writeGoodbyeMsg()
else:
print("Unknown command, please try again")
else:
self._writeGoodbyeMsg()
done = True
if __name__ == '__main__':
new = BandList()
new.menu()
You're missing an input call on your 2nd question for saving the band names. It should be:
inp = input("\nDo you want to save these band names? (y/n): ")
It does work. You just haven't given any values in self._BandList.
Its returning "BandList": null.

How can I retrieve information from a file.txt after adding it before turning off the program

I am learning to generate .txt files and how to write and read them, the problem I have now is that I want to make a program in which I can create, edit, and delete characters that appear similar to a table and that these new characters are saved in the file.txt
Seeing that the characters had attributes in common, I thought about using classes, I'm not really an expert in that, but I know a little, my main idea was to ask for the attributes and to generate an object and add it to a list of characters, doing it that way everything is fine but the data is not saved when you start the program again
class PLAYER:
def __init__(self,Name,Attack,Defense,Health):
self.Name=Name
self.Attack=Attack
self.Defense=Defense
self.Health=Health
options=[0,1,2,3]
characters=[]
while True:
counter=0
archive = open("players.txt","w")
print("ID|NAME|ATTACK|DEFENSE|HEALTH")
while counter != len(characters):
print("(",counter,")", characters[0+counter].Name,characters[0+counter].Attack,characters[0+counter].Defense,characters[0+counter].Health)
counter += 1
print("Character Manager")
print("( 0 ) Create")
print("( 1 ) Edit")
print("( 2 ) Delete")
print("( 3 ) Exit")
while True:#Choose the option
try:
option=int(input("-> "))
while option not in options:
print("Are you looking to find more options? Sorry, friend, there is no budget for that")
option=int(input("-> "))
break
except ValueError:
print("Please write the number in parentheses of the option you want to do")
if option==3:#Exit
archive.close()
break
elif option==0:#Crear
NAME=input("What will his name be? ")
while True:#Ataque
try:
ATTACK=int(input("How much attack will it have? "))
break
except:
print("please put an integer value")
while True:#Defensa
try:
DEFENSE=input("How much will your defense be? ")
break
except:
print("please put an integer value")
while True:#Vida
try:
HEALTH=input("How much life will it have?")
break
except:
print("please put an integer value")
characters.append(PLAYER(NAME,ATTACK,DEFENSE,HEALTH))
elif option==1:#Edit
print("Which character do you want to edit?")
ID=int(input("-> "))
characters.remove(characters[ID])
NAME=input("What will be his new name? ")
while True:#Ataque
try:
ATTACK=int(input("How much attack will it have? "))
break
except:
print("please put an integer value")
while True:#Defensa
try:
DEFENSE=input("How much will your defense be?")
break
except:
print("please put an integer value")
while True:#Vida
try:
HEALTH=input("How much life will it have?")
break
except:
print("please put an integer value")
characters.append(PLAYER(NAME,ATTACK,DEFENSE,HEALTH))
elif option==2:#Delete
print("¿Qué personaje quieres eliminar?")
ID=int(input("-> "))
characters.remove(characters[ID])
Up to this point I had not used the .txt file, so as not to show the complete code again I will only put the parts that change
counter=0
archive = open("players.txt","w")
print("ID|NAME|ATTACK|DEFENSE|HEALTH")
while counter != len(characters):
a=("(",counter,")", characters[0+counter].Name,characters[0+counter].Attack,characters[0+counter].Defense,characters[0+counter].Health)
archive.write(a)
archive.write("\n")
counter += 1
archive.close()
archive = open("personajes.txt","r")
print(archive.read())
print("Character Manager")
print("( 0 ) Create")
print("( 1 ) Edit")
print("( 2 ) Delete")
print("( 3 ) Exit")
The problem I found here is that the write argument must be str, not tuple but when putting
a = str ("all the stuff")
It appears to me
TypeError: str() takes at most 3 arguments (7 given)
So for now I run out of ideas to find a way to solve it, I hope you can help me to solve this since I couldn't find the solution on my own
an example of what I'm looking for is like this
ID|NAME|ATTACK|DEFENSE|HEALTH
Character Manager
( 0 ) Create
( 1 ) Edit
( 2 ) Delete
( 3 ) Exit
-> 0
What will his name be? Joseph
How much attack will it have? 20
How much will your defense be? 10
How much life will it have?50
ID|NAME|ATTACK|DEFENSE|HEALTH
( 0 ) Joseph 20 10 50
Character Manager
( 0 ) Create
( 1 ) Edit
( 2 ) Delete
( 3 ) Exit
but I want that when I start, old registered characters appear if there are
If I use an f-string
counter=0
archive = open("players.txt","w")
print("ID|NAME|ATTACK|DEFENSE|HEALTH")
while counter != len(characters):
a=f"( {counter} ) {characters[counter].Name} {characters[counter].Attack} {characters[counter].Defense} {characters[counter].Health}"
archive.write(a)
archive.write("\n")
counter += 1
archive.close()
archive = open("personajes.txt","r")
print(archive.read())
The new characters are printed but at the moment of closing the program and reopening the characters that where created before disappear
You can use pickle to store and retrieve objects to disk. Here is an example based off your code:
import pickle
class Player:
def __init__(self, name, attack, defense, health):
self.name = name
self.attack = attack
self.defense = defense
self.health = health
def __str__(self):
return f"{self.name} {self.attack} {self.defense} {self.health}"
# Create 2 objects and save
def create_and_save():
p1 = Player("name1", 1, 2, 3)
p2 = Player("name2", 4, 5, 6)
with open("players.bin", "wb") as f:
pickle.dump(p1, f)
pickle.dump(p2, f)
# Load the previously store object and print them
def load_data():
with open("players.bin", "rb") as f:
p1 = pickle.load(f)
p2 = pickle.load(f)
print(p1)
print(p2)
choice = int(input("1. Save, 2. Load: "))
if choice == 1:
create_and_save()
else:
load_data()
Run the program twice. First time enter "1" and 2nd time enter "2".
P.S. I have modified the class member names to fit the pep guide.
use list comprehension to create all the lines as strings and then use join to join them all with \n
info = '\n'.join([list comprehension here])
file.write(info)

Using Python Class to make game- how to update self init?

I am making a text-based game on python using the class system to keep track of main character changes (like its name). I am writing the main code for the game outside of the Main Character Class- inside of the main function.
I am struggling because I need to update self.character_name inside the Main Character class to an input from the user inside the main function. I am unsure how to do this, I have the code written below- however it is not updating the name inside Main Character class. How can I rewrite this?
I'm also worried that I will have this problem when trying to update pets, characters_known. However, I do not seem to have this problem with updating Health or XP....
class Main_Character():
def __init__(self):
self.health=100
self.exp=0
self.level=0
self.character_name=""
self.characters_known={None}
self.pets={None}
self.progression_tracker=0
def __str__(self):
return "Name: "+ str(self.character_name)+" | "+ "Health:"+ str(self.health) + " | " +"XP:"+ str(self.exp) + " | "+ "Level:"+ str(self.level)+" | "+"Pets:"+str(self.pets)
def Char_Name(self,name):
if name.isalpha()==False:
print("You entered a name containing non-alphabetic characters, pease reenter a new name:")
main()
elif len(name)>=10:
print("You entered a name containing 10 or more characters, pease reenter a new name:")
main()
else:
self.character_name=name
def Char_Level_Experience(self,exp,b):
self.exp+=exp
b=2
if exp<=0:
exp=1
ans = 1
level=0
while ans<exp:
ans *= b
level += 1
if ans == exp:
self.level=level
print("You have reached level", self.level)
else:
level = int(log(exp, 2))
level = min(level, exp)
if level>=0:
self.level=level
else:
level=0
def healing(self,heal):
if self.health+heal>=100:
self.health=100
else:
self.health+=heal
def other_answers(answer):
if answer=='quit':
raise SystemExit
if answer=='pets':
print("Pets owned:", Main_Character().pets)
user_decision=input("Would you like to continue where you left off? Type 'yes' to continue, or 'no' to go back to main menu")
if user_decision=='yes':
if Main_Character().progression_tracker==0:
main()
elif Main_Character().progression_tracker==1:
choice1()
if user_decision=='no':
main()
else:
other_answers(user_decision)
if answer=='characters':
print("Characters met:", Main_Character().characters_known)
user_decision=input("Would you like to continue where you left off? Type 'yes' to continue, or 'no' to go back to main menu:")
if user_decision=='yes':
if Main_Character().progression_tracker==0:
main()
if Main_Character().progression_tracker==1:
choice1()
if user_decision=='no':
main()
else:
other_answers(user_decision)
def start_check():
print("If you understand the game, type 'go' to continue- if not, type 'more information' to receive more information about how to play the game")
begin_game=input("")
if begin_game=="go":
choice1()
if begin_game=='more information':
print("\n","The object of the game is to gain XP [experience points] without dying")
start_check()
else:
other_answers(begin_game)
def choice1():
Main_Character().progression_tracker=1
print("You are a knight in the Kings Guard- the King has asked to meet with you about a very special mission")
print("What would you like to do?")
print(" 1.Go Directly to King","\n", "2. Finish your dinner")
choice=input("1 or 2?")
if choice=="1":
Main_Character().Char_Level_Experience(1,2)
elif choice=="2":
Main_Character().Char_Level_Experience(.5,2)
else:
other_answers(choice)
print(Main_Character())
def main():
print("Welcome!")
unfiltered_name=input("Please enter the name of your character:")
Main_Character().Char_Name(unfiltered_name)
print("Welcome,", Main_Character().character_name,"!", "Here are your current stats!")
print(Main_Character())
start_check()
You haven't quite understood how classes and instances work.
Calling the class is what you do when you need a new character. Every time you call Main_Character(), you get a whole new instance - with the default values as set in __init__. If you had characters for each of your friends, you would call it one time for each one. You then would need to keep each of those instances in a variable, so you can reference them again each time.
So, for instance:
my_character = Main_Character()
unfiltered_name=input("Please enter the name of your character:")
my_character.Char_Name(unfiltered_name)
print("Welcome,", my_character.character_name,"!", "Here are your current stats!")
print(my_character)
You create a new character each time you call Main_Character. Instead, you should call it once:
the_character = Main_Character()
...
the_character.name = "..."

Need help writing algorithm in Python/Sage

I'm a complete novice to python and sage so I need some help and clarification on the steps all the way through. This is a question concerning game theory.
First I will describe the algorithm and then I will propose a solution the best I can.
The algorithm:
I want to start the program with a random variable from 1-100. This
variable will be defined 'S'. I also want to define a set of variables
'C' which can be deducted from S every turn, this set is {1,2,3,4,5,6}
(in other words the user and computer can deduct 1, 2, 3, 4, 5 or 6
from S. If variable S is divisible by 7 (e.g. 21), then print: "I
lose". If not, the game can begin.
Let's say that the random variable turns out to be 20. The player is
now prompted to enter a number within the range of C. When the player
has entered the number, I want the program to deduct that number from
S, so if the player enters 4 (a legal move), S is then 20-4=16. The
computer then calculates mod(S,7) and finds out that modulo 16,7 is 2
so it deducts 2 from S, in other words, 16-2=14.
If the player enters a number which results in S being divisible by 7, such as 6 (20-6=14) then the computer simply deducts 1 and attempts to get to such a number again next round.
The game continues until the computer eventually wins as the player is
eventually placed at 7 and has to deduct a number which the computer
can finish with (user deducts 6, computer deducts the last one and
wins). Print: "I win".
So like I said, I have literally no experience in python and sage so I can only go by my (limited) java experience:
I would attempt to establish a variable S with some 'ran' element (no idea what it's called in python). I would then attempt something like:
if S%7=0 then print "I lose"
else
prompt "Pick a number between 1 and 6, those included".
Declare user input as variable U.
Do S-U=S
Now do S-S%7=S
Now I want the program to realize when S=7 and then print: "You lose". If you can help me go all the way, though, that would be great.
import random
def playgame():
s = random.randint(1,100) #grabs a random integer between 1 and 100
POSS = range(1,7) #range ignores the last number, so this is [1,2,3,4,5,6]
if not s % 7: #if s%7 != 0
print("I lose")
return #exit the function
while s > 0: #while s is still positive
choice = 0 #set choice to 0 (this may as well have been "foo",
# I just needed it to not be in POSS)
while choice not in POSS: #until the user picks a valid number
choice = int(input("Select a number between 1 and 6: ")) #prompt for input
s -= choice #subtract choice from s, then set the difference to s
print("You subtracted {}, leaving {}".format(choice,s)) #print for the user
comp_choice = s%7 #the computer's choice is always s%7
s -= comp_choice #subtract the comp's choice from s, then set the diff to s
print("I subtracted {}, leaving {}".format(comp_choice,s)) #print for user
print("I win!") #since we know computer will always win, I don't have to do a check
playgame() #run the function
Here's a vastly more complicated function that does essentially the exact same thing ;-)
class Entity(object):
"""Base class that should not be instantiated on its own -- only
exists to be inherited from. Use Player() and Computer() instead"""
def __init__(self,name=None):
if name is None:
name = input("What's your name? ")
self.name = name
self.myturn = False
def __str__(self):
# this magic function means calling str(self) returns str(self.name)
# included so I can do print(player)
return self.name
def makemove(self,choice):
"""finds the global s and subtracts a given choice from it,
printing the choice and the result to the user."""
global s
s -= choice
print("{} chooses {}, leaving {}".format(self,choice,s))
return choice
def activate(self):
self.myturn = True
return self
def deactivate(self):
"""does exactly self.myturn = False"""
self.myturn = False
class Player(Entity):
"""A player-controlled Entity"""
def getchoice(self):
"""Prompts the user for a choice, ensuring it's between 1 and 6, then
calls Entity's makemove() with that as an argument"""
choice = None
while choice not in range(1,7):
choice = int(input("Pick a number between 1 and 6: "))
return super().makemove(choice)
class Computer(Entity):
def __init__(self):
super().__init__(name="Computer Player")
#overrides to ensure every Computer object has the name Computer Player
def getchoice(self):
"""grabs a number for the computer, and makes its move"""
global s
choice = s%7
if choice == 0: #edge case where computer goes first on an s where s%7==0
choice = random.randint(1,6)
return super().makemove(choice)
class Game(object):
"""Class defining an instance of the Game
FUNCTIONS:
Game.start() <-- use this to start the game"""
def __init__(self,playerArray=[]):
"""defines s as a global, ensures the players array is built
correctly, and gives s a random int value between 1-100"""
global s
if type(playerArray) is Player:
playerArray = [playerArray]
while len(playerArray) < 2:
playerArray.append(Computer())
self.players = playerArray
s = random.randint(1,100)
def start(self):
"""Let's play!"""
global s
print ("""
====================================
THE GAME BEGINS NOW!!!
We will begin with a value of: {:3}
====================================""".format(s).lstrip())
turn = random.randint(1,len(self.players))-1
while True:
try:active_player = self.players[turn].activate()
except IndexError: print(turn)
choice = active_player.getchoice()
if s <= 0: break
active_player.deactivate() # is active_player.myturn = False
turn += 1
if turn == len(self.players): turn = 0 #wrap the list
for player in self.players:
#this will execute the turn s becomes zero
if player.myturn:
winner = player
break
print("Winner: {}".format(winner))
import random
game = Game()
game.start()
S=random.randint(1,100) #will pick a random number
user_input = int(raw_input("Enter a number:")) #will get an integer from the user
#subtraction and modulo work just like in any other language ...
if(condition):
do_something() #is the format for if statements
does that cover all your questions? or did I miss some?

Problems transferring information from one part of a function to another

While working on my program I have run into a problem where the information stored in Menu option 1 is not being transferred to Menu option 2. As you can see it is correctly stored when in menu one. When it returns to go to menu option 2 its like it never went to option 1.
update #1:
some suggestions I've had is to understand scope? from what I can tell the program is not passing the data along to its parent program even though I've typed out return in each of the definitions.
#Must be able to store at least 4 grades
#Each class can have up to 6 tests and 8 hw's
#Weighted 40%*testavg 40% hw average attendance is 20%
#User must be able to input a minimum grade warning
#after each test the your program must calculate the students average and issue warning if necessary
##Define the Modules##
import math
def menu (a): #2nd thing to happen
menuend = 'a'
while menuend not in 'e':
menuend = raw_input("Type anything other then 'e' to continue:\n")
print "What would you like to do ?"
menudo = 0
print "1 - Enter Courses\n2 - Select Course to Edit\n3 - Save File\n4 - Load File\n5 - Exit\n"
menudo = input("Enter Selection:")
if (menudo == 1):
menuchck = 0
menuchck = raw_input("\nYou have entered #1 (y/n)?:\n")
if menuchck in ["Yes","yes","y","Y"]:
x = m1()
else:
print "I'm sorry,",nam,",for the confusion, lets try again\n"
menu()
elif (menudo == 2):
menuchck1 = 0
menuchck1 = raw_input("\nYou have entered #2 (y/n)?:\n")
if menuchck1 in ["Yes","yes","y","Y"]:
x = m2()
else:
print "I'm sorry,",nam,",for the confusion, lets try again\n"
menu()
elif (menudo == 3):
print "Entered 3"
elif (menudo == 4):
print "Entered 4"
else:
print "Anything Else Entered"
def course(): #3rd thing to happen
b = {}
while True:
while True:
print "\n",name,", please enter your courses below ('e' to end):"
coursename = raw_input("Course Name:")
if (coursename == 'e'):
break
will = None
while will not in ('y','n'):
will = raw_input('Ok for this name : %s ? (y/n)' % coursename)
if will=='y':
b[coursename] = {}
print "\n",name,", current course load:\n",b
coursechck = None
while coursechck not in ('y','n'):
coursechck = raw_input("Are your courses correct (y/n)")
if coursechck =='y':
return b
else:
b = {}
print
##Menu Options##
def m1():
a = course()
return a
def m2():
print "Excellent",name,"lets see what courses your enrolled in\n"
print x
return x
###User Input Section###
name = raw_input("Enter Students Name:\n")
a = {}
menu(a)
raw_input("This is the end, my only friend the end")
In your if-elif blocks in the do==1 case, you write m1(), but for the last case, you write x=m1(). You should have the latter everywhere (by typing m1() you only run the function, but do not store the returned x anywhere).
By the way, you can avoid this if-elif confusion using if chck in ["Yes","yes","Y","y"]:

Categories