how can I use multiple else statements? - python

I want to use a few else statements in my class method:
class Musician :
def __initiate__(self,name, instrument, years_playing, practice_hours):
self.name = name
self.instrument =instrument
self.years_playing = years_playing
self.practice_hours = practice_hours
# self.level = int(self.practice_hours*self.years_playing)
def level(self):
if self.practice_hours <= 1 and self.years_playing <=1:
print (self.name,'is a beginner')
else:
self.practice_hours <=2 and self.years_playing <=2
print (self.name,'is an intermediate')
else:
self.practice_hours <= 3 and self.years_playing <=3
return (self.name, 'is an expert')
player_1 = Musician('Don', 'guitar', 1,3)
player_1.level()

There are many things wrong with your code. The actual question you have seems to be about using elif where you tried to use else; the general structure is
if condition:
something should happen
elif other_codition:
things which should be done in this case
elif third_condition:
other things
else:
unconditionally do this if none of the conditions above were true
where all the branches after the first are optional.
You are mixing print with return, and I'm guessing you meant __init__. Here's an attempt to fix the code.
class Musician:
def __init__(self, name, instrument, years_playing, practice_hours):
self.name = name
self.instrument =instrument
self.years_playing = years_playing
self.practice_hours = practice_hours
# self.level = int(self.practice_hours*self.years_playing)
def level(self):
if self.practice_hours <= 1 and self.years_playing <= 1:
return "beginner"
elif self.practice_hours <= 2 and self.years_playing <= 2:
return "intermediate"
elif self.practice_hours <= 3 and self.years_playing <= 3:
return "expert"
player_1 = Musician('Don', 'guitar', 1, 3)
lvl = player_1.level()
print(f"the level of {player_1.name} is {lvl}")
__init__ is a reserved name for the method which gets invoked when you create a new instance; you can't use a different name and hope for it to be invoked under the shme circumstances.
The level function will still return None if none of its conditions are true, but how to fix that depends on criteria I can't know. Perhaps a better design would be to check for "expert" or "intermediate" and then otherwise always return "beginner" if neither of those are true.
def level(self):
if self.practice_hours > 2 and self.years_playing > 2:
return "expert"
elif self.practice_hours > 1 and self.years_playing > 1:
return "intermediate"
# else
return "beginner"

You can use bisect to achieve the same, without using if
import bisect
def level(self):
level_dict = {0 : 'is a beginner', 1 : 'is an intermediate', 2 : 'is an expert'}
grade_ranges = [(0, 1), (1, 2), (2, 3)]
points, grade = zip(*grade_ranges)
return self.name + level_dict[bisect.bisect(points, self.practice_hours)-1]

Related

List returns None value instead of class object

In my role-playing party creator program, I am trying to have the user create a class object, adding the attributes, and storing it into a party-list index. However, by the time the player goes back to the main menu (the main() function that displays the party list), the slot still shows a None value. Here is my code:
class Creature:
def __init__(self):
self.name = None
self.feet = None
self.inches = None
self.weight = None
self.gender = None
def getName(self):
return "Name: {}".format(self.name)
def setName(self, name):
self.name = name
def getHeight(self):
return "Height: {} ft. {} in.".format(self.feet, self.inches)
def setFeet(self, feet):
self.feet = feet
def setInches(self, inches):
self.inches = inches
def getWeight(self):
return "Weight: {} lbs.".format(self.weight)
def setWeight(self, weight):
self.weight = weight
def getGender(self):
return "Gender: {}".format(self.gender)
def setGender(self, index):
genders = ['Male', 'Female', 'Others']
if int(index) == 1:
self.gender = genders[0]
elif int(index) == 2:
self.gender = genders[1]
elif int(index) == 3:
self.gender = genders[2]
class Dragon(Creature):
pass
class Mermaid(Creature):
pass
class Fairy(Creature):
pass
class Vampire(Creature):
pass
#allows the user to change attributes of creature
def changeAttributes(creature):
value = input("Pick an attribute to change: 1) name 2) height 3) weight 4) gender 5) save")
if int(value) == 1:
creature.setName(input("Enter a name: "))
return changeAttributes(creature)
elif int(value) == 2:
creature.setFeet(input("Enter a foot value: "))
creature.setInches(input("Enter an inch value: "))
return changeAttributes(creature)
elif int(value) == 3:
creature.setWeight(input("Enter a value in pounds: "))
return changeAttributes(creature)
elif int(value) == 4:
creature.setGender(input("Enter a value to set gender; 1 = male, 2 = female, 3 = others: "))
return changeAttributes(creature)
elif int(value) == 5:
confirm = input("Save? 1) yes 2) no")
if int(confirm) == 1:
print('Saving...')
return menu(creature)
else:
return changeAttributes(creature)
else:
print("Not a valid input, please try again.")
return changeAttributes(creature)
#prints the attributes of the creature
def showAttributes(creature):
print(creature.getName())
print(creature.getHeight())
print(creature.getWeight())
print(creature.getGender())
menu(creature)
def Delete(creature):
a = input("Are you sure? 1) yes 2) no ")
if int(a) == 1:
print("Deleting...")
creature = None
return main()
elif int(a) == 2:
print("Cancelled")
return menu(creature)
#checks to see if slot is empty or has a creature object; if empty, create a creature, otherwise go to creature menu
def menu(creature):
value = input("Select an option 1) Show Attributes 2) Change Attributes 3) Delete 4) Back")
if int(value) == 1:
return showAttributes(creature)
return menu(creature)
elif int(value) == 2:
return changeAttributes(creature)
return menu(creature)
elif int(value) == 3:
return Delete(creature)
elif int(value) == 4:
return main()
#checks if slot is empty, if empty, choose a creature subclass and change attributes, else takes user directly to change attribute menu
def check(slot):
if slot == None:
a = input('Choose a creature: 1) Dragon 2) Fairy 3) Mermaid 4) Vampire')
if int(a) == 1:
slot = Dragon()
elif int(a) == 2:
slot = Fairy()
elif int(a) == 3:
slot = Mermaid()
elif int(a) == 4:
slot = Vampire()
return changeAttributes(slot)
else:
return menu(slot)
#user select a slot; note that since development has not finished, you can only change slot 1
def main():
global party
print(party)
inp = input("Select a slot: ")
inp_1 = int(inp) - 1
if int(inp) > 0 and int(inp) < 6:
print("Slot {} selected!".format(int(inp)))
return check(party[inp_1])
party = [None, None, None, None, None]
main()
This is how the program runs so far:
[None, None, None, None, None]
Select a slot:
#User inputs 1
Slot 1 selected!
Choose a creature: 1) Dragon 2) Fairy 3) Mermaid 4) Vampire
#User inputs 1
Pick an attribute to change: 1) name 2) height 3) weight 4) gender 5) save
#User inputs 1
Enter a name: *Name*
Pick an attribute to change: 1) name 2) height 3) weight 4) gender 5) save
#User inputs 5
Save? 1) yes 2) no
#User inputs 1
Saving...
Select an option 1) Show Attributes 2) Change Attributes 3) Delete 4) Back
#User inputs 4
However, after you go back to main(), the list still displays as such:
[None, None, None, None, None]
Select a slot:
What doesn't make sense is that the parameters in the functions should have followed a chain rule that will eventually lead to the party slot. I want it so that a slot index will have a class object stored in it rather than None. As far as I know, I might need to use global variables, but I haven't found out much after that. Any ways to fix this?
Edit: So I managed to fix the problem. I just put the check() function in the main(). It goes like this:
def main():
print(party)
inp = input("Select a slot: ")
inp_1 = int(inp) - 1
if int(inp) > 0 and int(inp) < 6:
print("Slot {} selected!".format(int(inp)))
if party[inp_1] == None:
a = input('Choose a creature: 1) Dragon 2) Fairy 3) Mermaid 4) Vampire')
if int(a) == 1:
slot = Dragon()
elif int(a) == 2:
slot = Fairy()
elif int(a) == 3:
slot = Mermaid()
elif int(a) == 4:
slot = Vampire()
party[inp_1] = slot
return changeAttributes(party[inp_1])
else:
return menu(party[inp_1])
There is no sens of "return" at the end of main. What you should want to do I think is editing the party list. Therefor instead of
return check(party[inp_1])
you should try
party[inp_1] = check(party[inp_1])
Make sure the check function returns a creature type, I'm not sure of that.
there is some really weird interaction you really should try to make class and methods for all of that. In your 4th elseif in the menu function, you shouldn't have to call main again.

Name 'x' is not defined / Global variable?

I'm learning to program with python and I came across this issue: I'm trying to make a Guessing Game, and while trying to check for the win condition, the function doesn't recognise the input variable, which I made sure I returned with a previous function. So i get the 'name << 'first_input' is not defined' >> error. I thought it had something to do with the variable not being global or sth like that.
import random
ran_int = random.randint(1,100)
guesses = 0
# here you input the number and it keeps asking unless you do so with 1 to 100
def ask():
first_input = 0
while first_input < 1 or first_input > 100:
first_input = int(input('Enter a number between 1 and 100: '))
return first_input
# this is just to increment the number of guesses stored for showing at the end # of the game
def guesses_inc():
global guesses
guesses += 1
return guesses
# here is where i get the error, as if my ask() function didn't return
# the value properly or as if I assigned it wrongly
def check_win_1():
if first_input == ran_int:
guesses_inc()
print(f'BINGO!\nYou guessed correctly after {guesses} times.')
elif (abs(ran_int - first_input) <= 10):
guesses_inc()
print('WARM!')
ask2()
elif first_input < 1 or first_input > 100:
print('Out of bounds!')
ask2()
else:
guesses_inc()
print('COLD!')
ask2()
ask()
check_win_1()
And here is the error
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-11-bfd5497995df> in <module>
----> 1 check_win_1()
NameError: name 'first_input' is not defined
I didn't paste the whole code because while testing it it returned the error at this stage so I didn't think the rest mattered for this particular problem. I tried making the var input global and stuff like that but i don't think I did it properly.
Your method call is not correct. You should call your functions like this
def check_win_1(first_input):
if first_input == ran_int:
guesses_inc()
print(f'BINGO!\nYou guessed correctly after {guesses} times.')
elif (abs(ran_int - first_input) <= 10):
guesses_inc()
print('WARM!')
ask2()
elif first_input < 1 or first_input > 100:
print('Out of bounds!')
ask2()
else:
guesses_inc()
print('COLD!')
ask2()
first_input = ask()
check_win_1(first_input)
The error is there because you are trying to use first_input somewhere (i.e. inside check_win_1()).
A possible, not recommended, solution is to qualify your variable as global, which should be used VERY sparingly.
Instead, it is recommended to use function parameters, so as to encapsulate your code in self-contained blocks, e.g.:
def func(a, b):
return a + b
x = func(10, 5)
rather than:
def func():
global a, b
return a + b
a = 10
b = 5
x = func()
For your that may mean doing something like:
def check_win_1(first_input, ran_int):
...
and use them accordingly, e.g.:
first_input = ask()
check_win_1(first_input, ran_int)
etc.
EDIT
Following the above principle, your code could have looked like:
import random
MIN_VAL = 1
MAX_VAL = 100
WARM_LIMIT = 10
def ask_number(
min_val=MIN_VAL,
max_val=MAX_VAL):
guess = None
while guess is None:
guess = int(input(f'Enter a number between {min_val} and {max_val}: '))
if guess < min_val or guess > max_val:
print('Out of bounds!')
guess = None
return guess
def check_guess(
guess,
target,
num_guesses,
warm_limit=WARM_LIMIT):
if guess == target:
print(f'BINGO!\nYou guessed correctly after {num_guesses} times.')
return True
else:
if (abs(guess - target) <= warm_limit):
print('WARM!')
else:
print('COLD!')
return False
# : main
target = random.randint(MIN_VAL, MAX_VAL)
num_guesses = 0
won = False
while not won:
guess = ask_number()
num_guesses += 1
won = check_guess(guess, target, num_guesses)

Calling a class function outside of the function it was declared

I'm trying to call a class function player.Decide() in another function where it wasn't declared. Im getting the error 'player is not defined. How would I go about fixing this?
def BattleLogic():
global enemy, enemy_race, name, level, strength, dexterity, cunning, enemy_speed, player_speed, type_speed
global move, turn, prefix
if enemy in ['deer']:
enemy_race = 'animal'
if enemy_race == 'animal':
chance = random.randint(1,1000)
if chance <= 10:
prefix = 'crippled'
elif chance > 10 and chance <= 50:
prefix = 'old'
elif chance >50 and chance <= 250:
prefix = 'young'
elif chance > 250 and chance <= 750:
prefix = None
elif chance > 750 and chance <= 950:
prefix = 'strong'
elif chance > 950 and chance <= 990:
prefix = 'alpha'
elif chance > 990 and chance <= 999:
prefix = 'possessed'
elif chance == 1000:
prefix = '*CONVERTED*'
else:
prefix = 'error'
opponent = Enemy(str(prefix),str(enemy),str(enemy_race))
player = Player(str(name),level,strength,dexterity,cunning)
player.Initiative()
opponent.Initiative()
if enemy_speed > player_speed:
move = 0
elif player_speed > enemy_speed:
move = 1
else:
move = random.randint(0,1)
turn = 0
Battle()
def Battle():
global turn, move, prefix, enemy, type_speed, enemy_title
if turn == 0:
print('\n\n')
if prefix == None:
enemy_title = enemy.capitalize()
else:
enemy_title = prefix.capitalize()+' '+enemy.capitalize()
SlowPrint('A '+enemy_title+' gets into position for battle',type_speed,0.5)
if move == 1:
SlowPrint(enemy_title+' makes the first move',type_speed,0.25)
else:
SlowPrint('You make the first move',type_speed,0.25)
if move == 0:
turn += 1
move = 1
player.Decide()
else:
turn += 1
move = 0
opponent.Decide()
Just pass a player object into the function. The you are free to use player.Decide().
The major problems with your coding, which is my personal opinion, is that you do not have the habit of passing something into the function, which as a result forces you to declare too many global variables.
A simple example (in your case, you want the object to be a player)
def Battle(obj):
return obj.Decide()

Rewriting pre-written code to implement class. (Python)

Few days into Python, I wrote this a day ago to get the gist of Python Grammar. While it might not be efficient it works as i intended.
def LongerString():
if len(String1) == len(String2):
return len(String1)
elif len(String1) > len(String2):
return len(String1)
elif len(String1) < len(String2):
return len(String2)
def ShorterString():
if len(String1) == len(String2):
return len(String1)
elif not len(String1) > len(String2):
return len(String1)
elif not len(String1) < len(String2):
return len(String2)
#String1 and 2 from userinput
String1 = input('Input String1\n')
String2 = input('Input String2\n')
#After finishing comparing, evaluates in %
score = 0
for i in range(ShorterString()):
if String1[i] == String2[i]:
score += 1
print(score, "/", LongerString(), "Match", float(score / LongerString()) * 100, "%")
As my progress goes, decided to re-write this code under single class to
get better understanding of how class is used. But looks like I'm missing something. Below is my little attempt.
class CompareString:
def LongerString():
if len(String1) == len(String2):
return len(String1)
elif len(String1) > len(String2):
return len(String1)
elif len(String1) < len(String2):
return len(String2)
def ShorterString():
if len(String1) == len(String2):
return len(String1)
elif not len(String1) > len(String2):
return len(String1)
elif not len(String1) < len(String2):
return len(String2)
def Result():
for i in range(ShorterString()):
if String1[i] == String2[i]:
score += 1
print(score, "/", LongerString(), "Match", float(score / LongerString()) * 100, "%")
String1 = input('Input String1\n')
String2 = input('Input String2\n')
CompareString.Result()
When trying to run latter code i get an error message "line 20, in Result
for i in range(ShorterString()):
NameError: name 'ShorterString' is not defined"
For the immediate problem, you need to call your methods via self - ie self.LongerString() - and they need to accept self as the first argument.
But this isn't really how you use classes. You should write an __init__ method that takes the two strings and stores them as instance attributes, then refer to those throughout.
(Also note, you should use PEP8 style for your variable names.)
class CompareString:
def __init__(self, string1, string2):
self.string1 = string1
self.string2 = string2
def longer_string(self):
if len(self.string1) == len(self.string2):
...
def result(self):
... call self.longer_string() etc ...
compare = CompareString(string1, string2)
print compare.result()
(Also also note, your longer_string method could just be return max(len(self.string1), len(self.string2)), and your shorter_string method similarly.)
(Also also also note, your result method should not iterate over a range, but just over zip(self.string1, self.string2), so you don't even need either of those other methods.)

Calling a method within another method in the context of #property

With the following sample code containing #property decorators:
class Grade_Calculator(object):
def __init__(self,score):
self.score = score
#property
def grade(self):
if self.score in range(60,70):
grade = 'D'
elif self.score in range(70,80):
grade = 'C'
elif self.score in range(80,90):
grade = 'B'
elif self.score in range(90,101):
grade = 'A'
return grade
#property
def failure(self):
if self.score < 60:
print 'See me'
grade = 'F'
return grade
and an instance:
g = Grade_Calculator(28)
g.grade returns an UnboundLocalError. I would like to call failure() within grade() to avoid this error.
With an instance:
g = Grade_Calculator(89)
g.failure() fails silently. I would like to call grade() within failure() to act as a fail-safe in this situation.
I've seen a lot of references to just doing self.method() calls, but they're not working for me and I think the presence of the decorator is screwing me up somehow:
#property
def grade(self):
if self.score < 60:
self.failure()
elif self.score in range(60,70):
grade = 'D'
elif self.score in range(70,80):
grade = 'C'
elif self.score in range(80,90):
grade = 'B'
elif self.score in range(90,101):
grade = 'A'
return grade
g = Grade_Calculator(28)
g.grade
See me
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-667-fb10e6cf27d4> in <module>()
----> 1 g.grade
./sample.py in grade(self)
6 def grade(self):
7 if self.score < 60:
----> 8 self.failure()
9 elif self.score in range(60,70):
10 grade = 'D'
TypeError: 'str' object is not callable
What am I not understanding?
You have defined failure as a property. Therefore, you can't call it using () notation; in fact, not doing that is kinda the whole point of using a property. self.failure returns 'F' and you then try to call it, which is why you get the error about not being able to call a string. It's as if you wrote 'F'() which is an obvious mistake.
Also, you're throwing away the value you get from failure and you're only returning a value in the case of failure (which means you get None otherwise). Of course you only access failure if you have a failing grade to begin with... in which case you don't need to check that condition inside failure, now do you?
Also, printing stuff in properties is pretty bad style. You don't expect something to be printed when you access an attribute. Separation of concerns: something outside of the class should probably be doing the printing, or you should have a separate method to call to print whatever is returned by grade.
I'd rewrite your class as follows:
class Grade_Calculator(object):
def __init__(self, score):
self.score = score
#property
def grade(self):
if self.failure:
return 'F'
if 60 <= self.score < 70:
return 'D'
if 70 <= self.score < 80:
return 'C'
if 80 <= self.score < 90:
return 'B'
if 90 <= self.score < 100:
return 'A'
#property
def failure(self):
return self.score < 60 # returns True or False
def print_grade(self):
print self.grade, "- see me" * self.failure
if __name__ == '__main__':
c = Grade_Calculator(71)
c.print_grade()
class Grade_Calculator(object):
def __init__(self, score):
self.score = score
#property
def grade(self):
grade = self.failure
if 60 <= self.score < 70:
grade = 'D'
elif 70 <= self.score < 80:
grade = 'C'
elif 80 <= self.score < 90:
grade = 'B'
elif 90 <= self.score < 100:
grade = 'A'
return grade
#property
def failure(self):
if self.score < 60:
print 'See me'
grade = 'F'
return grade
if __name__ == '__main__':
a = Grade_Calculator(71)
print a.grade
You're basically creating a new attribute to your object with the failure method. And you're never trying to get its value, that is why you'll never see 'See me'
By the way, you're overriding the value of grade which was initially an integer. You converted it to a string ('A', 'B', 'C', ...)
The code I provided works, but I changed a few things. There is no need to call range (this is expensive). You can use value < variable < other_value in Python

Categories