While trying to complete an assignment, my result keeps coming up as partially correct in the ZyBooks system. I’ve tried everything I can possibly think of to solve the issue, and have no idea what else to try. Here are the instructions for the assignment:
class Team:
def __init__(self):
self.name = 'team'
self.wins = 0
self.losses = 0
# TODO: Define get_win_percentage()
def get_win_percentage(self):
return team.wins / (team.wins + team.losses)
# TODO: Define print_standing()
def print_standing(self):
print(f'Win percentage: {team.get_win_percentage():.2f}')
if team.get_win_percentage() >= 0.5:
print('Congratulations, Team', team.name,'has a winning average!')
else:
print('Team', team.name, 'has a losing average.')
if __name__ == "__main__":
team = Team()
user_name = input()
user_wins = int(input())
user_losses = int(input())
team.name = user_name
team.wins = user_wins
team.losses = user_losses
team.print_standing()
I’m passing all the auto-generated tests aside from the last three, and I can’t understand why? To Do’s have to be included as well.
As noted in comments, in the below you've used team rather than self.
# TODO: Define get_win_percentage()
def get_win_percentage(self):
return team.wins / (team.wins + team.losses)
# TODO: Define print_standing()
def print_standing(self):
print(f'Win percentage: {team.get_win_percentage():.2f}')
if team.get_win_percentage() >= 0.5:
print('Congratulations, Team', team.name,'has a winning average!')
else:
print('Team', team.name, 'has a losing average.')
Corrected:
# TODO: Define get_win_percentage()
def get_win_percentage(self):
return self.wins / (self.wins + self.losses)
# TODO: Define print_standing()
def print_standing(self):
print(f'Win percentage: {self.get_win_percentage():.2f}')
if self.get_win_percentage() >= 0.5:
print('Congratulations, Team', self.name, 'has a winning average!')
else:
print('Team', self.name, 'has a losing average.')
Just to add to the answer of joshmeranda.
In the class, when you call the atribute of this object you use the "self" parameter. This parameter is used to reference the instance itself.
For example:
def print_wins(self): print(self.wins) to refence the instance
When you do:
team = Team()
you created a instance of team that is called "team". With this, you can print the numbers of win, for example: print(team.wins)
Or, call the function "print_wins":
team.print_wins()
Related
I've recently been refactoring some of my code to use OOP, and I've run into a problem where I can't quite get either global vars, exec(), or a combination of the two to work. The relevant code is below:
# invObject class. Has all properties of an inventory object.
class invObject:
def __init__(self, name, info, output, effect):
self.name = name # Used in menus.
self.info = info # Describes effect.
self.output = output # Printed on use.
self.effect = effect # Executed on use.
def printInfo(self): # Function for name and description.
return "{} - {}".format(self.name, self.info)
def use(self): # Function to use items. It's that easy.
global dodgeChance
global maxHp
global hp
global atk
exec(self.effect)
print(self.output) # Prints output. Also very simple.
print("{} {} {} {}".format(dodgeChance, maxHp, hp, atk)) # debugging
...
inventory[slot].use() # does not update values
Basic rundown: inventory[slot].use() should call the use() function of the object. use() should execute the code stored in inventory[slot].effect.
The output from the debugging line doesn't change anything, even inside the function. I've already tried making it return exec(self.effect) to no avail. print(self.output) does work.
EDIT: Here's a minimal reproducible example that includes everything it needs to run, not just the most important things.
# Assign base stats
dodgeChance = 0
maxHp = int(input("Input maximum HP. > "))
hp = int(input("Input current HP. > "))
# invObject class. Has all properties of an inventory object.
class invObject:
def __init__(self, name, info, output, effect):
self.name = name # Used in menus.
self.info = info # Describes effect.
self.output = output # Printed on use.
self.effect = effect # Executed on use.
def printInfo(self): # Function for name and description.
return "{} - {}".format(self.name, self.info)
def use(self): # Function to use items. It's that easy.
global dodgeChance
global maxHp
global hp
global atk
exec(self.effect)
print(self.output) # Prints output. Also very simple.
print("{} {} {} {}".format(dodgeChance, maxHp, hp, atk)) # debugging
empty = invObject("None", "Vacant slot.", "There's nothing in that slot!", "")
apple = invObject("Apple", "Gives 20 health.", "Ate the apple. Gained 20 health.", "hp = hp + 20\nif hp > maxHp: hp = maxHp")
drink = invObject("Drink", "Some kind of energy drink. Raises dodge chance to 75%.", "Drank the drink. Dodge chance is now 75%!", "dodgeChance = 75")
# Assign base inventory
inventory = [apple, drink, empty]
slot = int(input("Input slot number to use. ")) - 1
inventory[slot].use() # does not update values
# Show final stats
print("New HP value: " + str(hp))
print("Dodge chance: " + str(dodgeChance) + "%")
print()
print("Inventory contents:")
print("Slot 1: " + str(inventory[0].name))
print("Slot 2: " + str(inventory[1].name))
print("Slot 3: " + str(inventory[2].name))
EDIT 2: Another thing: the code works if I don't use exec() (e.g. change it out for hp += 20).
exec() has optional arguments for you to provide the global and local variable contexts.
But you didn't provide them.
I know that I have a misunderstanding of how Python attributes work because I'm here writing this problem, but I don't know exactly what I'm misunderstanding. I'm trying to get
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
to attain their values based on self.hand, which I pass to __init__ upon instantiation. Throughout the game I am altering .hand, and I want .card & .card_val to change every time I change .hand, instead of having to tell it to do that elsewhere (outside of the attribute definitions). I know there is a way to do this, or at least I think there is, and by that I mean simply by defining their values as inherited based on whatever .hand is at any given time, without calling an internal or external function.
In the posted code, I have altered it to work as the game instructions require, using...
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
...but that's what I want to change. I want what that function is doing to be executed more concisely inside of the attribute definitions upon handling of the instance. Basically my question is why can't these two attributes get their values directly from the first attribute that I define via "hand" passed to the init?
Any help would be appreciated, and more importantly, I think more than just solutions, it would help me even more to understand what I am misunderstanding about how attributes, instances, and instantiation and all that works so that I know where my thinking is wrong. Thanks!
import random
from random import shuffle
from collections import deque
class Deck():
def __init__(self):
self.ranks = {"Ace":14, "King":13, "Queen":12, "Jack":11, "10":10, "9":9, "8":8, "7":7, "6":6, "5":5, "4":4, "3":3, "2":2}
self.suites = ["Heart", "Diamond", "Spade", "Club"]
self.cards = []
def create_cards(self):
for suite in self.suites:
for key in self.ranks.keys():
self.cards.append(key + " " + suite)
def shuffle(self):
random.shuffle(deck.cards)
deck = Deck()
deck.create_cards()
deck.shuffle()
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
self.card = self.hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
def war(bool, p1, p2):
if bool == True:
for player in [p1, p2]:
player.card_number = 4
else:
for player in [p1, p2]:
player.card_number = 0
p2 = Player(deque(deck.cards[::2]))
p1 = Player(deque(deck.cards[1::2]))
p2.name = "The Computer"
def get_card_vals(p1, p2):
for player in [p1, p2]:
player.card = player.hand[player.card_number].split()
player.card_val = deck.ranks.get(player.card[0])
print("{a} vs. {b}".format(a = p1.card, b = p2.card))
print("---------------------------")
def cant_war_lose(winner, loser):
print("{a} doesn't have enough cards to go to war, so {b} wins the Battle!".format(a = loser, b = winner))
def battle_win(winner, loser):
print("{a} has run out of cards, therefore {b} has won via Battle!".format(a = loser, b = winner))
def play_cards(p1, p2):
war(False, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-1)
elif p1.card_val == p2.card_val:
if len(p1.hand) < 5 or len(p2.hand) < 5:
if len(p1.hand) > len(p2.hand):
cant_war_lose(p1.name, p2.name)
else:
cant_war_lose(p2.name, p1.name)
return 0
else:
input("War is inititated! Press Enter to continue!")
print("---------------------------")
war(True, p1, p2)
get_card_vals(p1, p2)
if p1.card_val > p2.card_val:
for i in range(0,5):
p1.hand.append(p2.hand.popleft())
p1.hand.rotate(-5)
elif p1.card_val < p2.card_val:
for i in range(0,5):
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-5)
else:
p1.hand.rotate(-1)
p2.hand.rotate(-1)
elif p1.card_val < p2.card_val:
p2.hand.append(p1.hand.popleft())
p2.hand.rotate(-1)
if len(p1.hand) != 0 and len(p2.hand) != 0:
input("After the last round of Battle, {a} now has {b} cards, and {c} now has {d} cards! Press Enter to continue!".format(a = p1.name, b = len(p1.hand), c = p2.name, d = len(p2.hand)))
print("---------------------------")
else:
if len(p1.hand) > len(p2.hand):
battle_win(p1.name, p2.name)
else:
battle_win(p2.name, p1.name)
return 0
def game_run():
run = 1
p1.name = input("Player 1's name? ")
print("---------------------------")
while run == 1:
if play_cards(p1, p2) == 0:
run = 0
game_run()
You can use the property decorator to create a calculated property
class Player():
def __init__(self, hand):
self.name = "name"
self.hand = hand
self.card_number = 1
#property
def hand(self):
return self._hand
#hand.setter
def hand(self, value):
self._hand = value
self.card = self._hand[self.card_number].split()
self.card_val = deck.ranks.get(self.card[0])
What you misunderstand is variables, not instances. For instance, the attribute card is a scalar variable attached to the instance. Assigning to it with
self.card = <blah>
does not bind it to blah for constant recomputation. This is a value assignment, not a memory mapping. If you want that long-term binding, you must either write the maintenance routine yourself -- which you've already done, in a way, with the consistent recomputation -- or you must assign a mutable reference to self.card, so that card refers to teh same object as the expression you created.
Given that you are consistently rotating and altering the hand, this is not feasible in your design. Instead, simply write an access routine, perhaps get_next_card(hand), which will rotate the hand, extract the card, and return the desired rank and suit.
If you plan to program more card games, you will also find it handy to define a class card and class hand, with appropriate support routines. Maintain the card as a pair of integers; convert to strings only for printing.
Does that get you moving?
For anyone who wanted to compare a before and after of the problem & final solution, below is the working code for my specific issue. All I had to do was convert self.card and self.card_val to a calculated property. By passing in hand, and subsequently handling only hand, self.card & self.card_val are calculated, since every time I handle the instance of the class (by handling hand), these "method attributes" are being called and altered. Thanks for the input, guys!
class Player():
def __init__(self, hand):
self.name = "name"
self.card_number = 1
self.hand = hand
#property
def card(self):
return self.hand[self.card_number].split()
#property
def card_val(self):
return deck.ranks.get(self.card[0])
Our teacher has assigned us an assignment for doing a turned based game.
This only included name.strip() but this does not prompt player to input unique name:
def start():
print("\nNew game started...Setting up Player 1's team...\n")
for total_characters in range (1,4):
name = input('Enter a unique name for unit #'+str(total_characters)+'==> ')
if not name.strip():
print('Cant be blank name,please provide a unique name')
return start()
else:
role_selection()
def role_selection():
for total_characters in range (1):
role = input('Select unit #'+str(total_characters)+' type: (W)arrior, (T)anker, or Wi(Z)ard ==> ')
total_characters+=1
if role.upper() == 'W':
pass
elif role.upper() == 'T':
pass
elif role.upper() == 'Z':
pass
else:
print("I don't understand what are you typing.")
return role_selection()
There are things that doesn't make sense :
You have the exact same function twice :
def set_up(team_size)
def set_up(name)
You are doing :
for total_units in range(team_size):
[...]
invalid = True
[...]
while invalid: # Infinite loop
set_up() # What's this function ?
As you can see from the comments in the code above, you never set invalid to False, leading to the infinite loop.
Note: My recommendation is that you should check out some tutorial on python before moving on coding a complex project, because your homework is not that easy.
Edit :
From the new code you've posted, you could do something like this :
def new_game():
names = []
for unit_id in range(1,4):
print(f'Enter a unique name for unit #{unit_id} ->')
empty = True
while empty:
name = input('>')
if name == "":
continue
empty = False
if name in names:
print('Unit name must be unique.')
else:
print('Name looks good!')
names.append(name)
python menu
At first glance, this stood out to me:
if not name.strip():
print('Unit name could not be blank.')
invalid = True
Remember in python the indentation matters. You are setting invalid to True regardless of the if condition. Further down you have a while loop that checks it.
The other if conditions have invalid=True inside the condition. Plus you don't have invalid=False anywhere as far as I see, so you'll get an error if you don't declare it somewhere so it's always on the path before the while.
this doesn't seem like a specific problem, more an ask for general guidance for going about this kind of problem?
One thing to note is that your above script only uses functions (which store behaviour) whilst really for something like the turn based game, you need to store behaviour (attacks etc) and information (how much health is left etc).
I won't write the script for you, but here's an example of how you might define an rpg like entity, capable of attacking, being attacked by another entity etc:
class Entity:
"""Abstract class for any combat creature"""
def __init__(self, name, health):
self.name = name
self.health = health
self.is_alive = True
def attack(self, other):
dmg = random.randint(7, 12)
other.be_attacked(dmg)
def be_attacked(self, dmg):
self.health = self.health - dmg
if self.health <= 0:
self.die()
def die(self):
self.is_alive = False
def check_life(self):
return self.is_alive
You can then initialise a bunch of these to make up the 'team' you where talking about:
one_person = Entity('Lee', 34)
another_person = Entity('Someone Else', 21)
etc. Hope that helps a bit. Good luck with your project and have fun!
I have a function that creates a player object but when referencing the object, I get a NameError. I think it is happening due to local scope but global should fix it...
I just started out OOP and this code is working in the python shell but it is not working in script mode.
endl = lambda a: print("\n"*a)
class Score:
_tie = 0
def __init__(self):
self._name = ""
self._wins = 0
self._loses = 0
def get_name(self):
print
self._name = input().upper()
def inc_score(self, wlt):
if wlt=="w": self._wins += 1
elif wlt=="l": self._loses += 1
elif wlt=="t": _tie += 1
else: raise ValueError("Bad Input")
def player_num(): #Gets number of players
while True:
clear()
endl(10)
print("1 player or 2 players?")
endl(5)
pnum = input('Enter 1 or 2: '.rjust(55))
try:
assert int(pnum) == 1 or int(pnum) == 2
clear()
return int(pnum)
except:
print("\n\nPlease enter 1 or 2.")
def create_player(): #Creates players
global p1
p1 = Score()
yield 0 #stops here if there is only 1 player
global p2
p2 = Score()
def pr_(): #testing object
input(p1._wins)
input(p2._wins)
for i in range(player_num()):
create_player()
input(p1)
input(p1._wins())
pr_()
wherever I reference p1 I should get the required object attributes but I'm getting this error
Traceback (most recent call last):
File "G:/Python/TicTacTwo.py", line 83, in <module>
input(p1)
NameError: name 'p1' is not defined
Your issue is not with global but with the yield in create_player(), which turns the function into a generator.
What you could do:
Actually run through the generator, by executing list(create_player()) (not nice, but works).
But I suggest you re-design your code instead, e.g. by calling the method with the number of players:
def create_player(num): #Creates players
if num >= 1:
global p1
p1 = Score()
if num >= 2:
global p2
p2 = Score()
If you fix this issue, the next issues will be
1) input(p1) will print the string representation of p1 and the input will be lost, you probably want p1.get_name() instead.
2) input(p1._wins()) will raise TypeError: 'int' object is not callable
I will redesign the app to introduce really powerful python constructs that may help you when getting into OOP.
Your objects are going to represent players, then don't call them Score, call them Player.
Using _tie like that makes it a class variable, so the value is shared for all the players. With only two participants this may be true but this will come to hurt you when you try to extend to more players. Keep it as a instance variable.
I am a fan of __slots__. It is a class special variable that tells the instance variables what attributes they can have. This will prevent to insert new attributes by mistake and also improve the memory needed for each instance, you can remove this line and it will work but I suggest you leave it. __slots__ is any kind of iterable. Using tuples as they are inmutable is my recomendation.
Properties are also a really nice feature. They will act as instance attribute but allow you to specify how they behave when you get the value (a = instance.property), assign them a value (instance.property = value), or delete the value (del instance.property). Name seems to be a really nice fit for a property. The getter will just return the value stored in _name, the setter will remove the leading and trailing spaces and will capitalize the first letter of each word, and the deletter will set the default name again.
Using a single function to compute a result is not very descriptive. Let's do it with 3 functions.
The code could look like this:
# DEFAULT_NAME is a contant so that we only have to modify it here if we want another
# default name instead of having to change it in several places
DEFAULT_NAME = "Unknown"
class Player:
# ( and ) are not needed but I'll keep them for clarity
__slots__ = ("_name", "_wins", "_loses", "_ties")
# We give a default name in case none is provided when the instance is built
def __init__(self, name=DEFAULT_NAME):
self._name = name
self._wins = 0
self._loses = 0
self._ties = 0
# This is part of the name property, more specifically the getter and the documentation
#property
def name(self):
""" The name of the player """
return self._name
# This is the setter of the name property, it removes spaces with .strip() and
# capitalizes first letters of each word with .title()
#name.setter
def name(self, name):
self._name = name.strip().title()
# This is the last part, the deleter, that assigns the default name again
#name.deleter
def name(self):
self._name = DEFAULT_NAME
def won(self):
self._wins += 1
def lost(self):
self._loses += 1
def tied(self):
self._ties += 1
Now that's all we need for the player itself. The game should have a different class where the players are created.
class Game:
_min_players = 1
_max_players = 2
def __init__(self, players):
# Check that the number of players is correct
if not(self._min_players <= players <= self._max_players):
raise ValueError("Number of players is invalid")
self._players = []
for i in range(1, players+1):
self._players.append(Player(input("Insert player {}'s name: ".format(i))))
#property
def players(self):
# We return a copy of the list to avoid mutating the inner list
return self._players.copy()
Now the game would be created as follows:
def new_game():
return Game(int(input("How many players? ")))
After that you would create new methods for the game like playing matches that will call the players won, lost or tied method, etc.
I hope that some of the concepts introduced here are useful for you, like properties, slots, delegating object creation to the owner object, etc.
with this program the outcome should display employee name, number , shift (day or night based on the number imputed) and hourly pay rate.
I've tried a few different ways and can "almost" get the desired results. Any assistance would be appreciated.
# This Employee class holds general data about employess and will
# end up as the superclass for this example.
class Employee:
#__init__ method initialzes the attributes.
def __init__(self, emp_name, emp_number):
self.__emp_name = emp_name
self.__emp_number = emp_number
# The set_emp_name method gets the employee name.
def set_emp_name(self, emp_name):
self.__emp_name = emp_name
# The set_emp_name method gets the employee number.
def set_emp_number(self, emp_number):
self.__emp_number = emp_number
# The get_emp_name method returns the employee name.
def get_emp_name(self):
return self.__emp_name
# The get_emp_number method returns the employee number.
def get_emp_number(self):
return self.__emp_number
# The ProductionWorker class holds the general data from superclass Employee
# as well as Employee shift time and pay rate making it a subclass
# of Employee.
class ProductionWorker(Employee):
# __init__ method initializes the attributes.
def __init__(self, emp_name, emp_number, shift, payrate):
# Call the superclass
Employee.__init__(self, emp_name, emp_number)
self.__shift = shift
self.__payrate = payrate
# The set_shift method get employee shift.
def set_shift(self, shift):
self.__shift = shift
# The set_payrate method gets employee hourly pay rate.
def set_payrate(self, payrate):
self.__payrate = payrate
# The get_shift method returns the employee shift.
def get_shift(self):
if self.shift == 1:
self.shift = 'Day shift'
elif self.shift == 2:
self.shift = 'Night shift'
return self.__shift
# The get_payrate method returns the employee hourly pay rate.
def get_payrate(self):
return self.__payrate
# This program will test the Employee superclass and ProductionWorker subclass
# by returning and displaying the gathered information.
import sys
# Get the Employee info.
emp_name = input('Employee Name: ')
emp_number = input('Employee Number: ')
shift = float(input ('Shift Number 1 or 2: '))
payrate = input('Hourly Pay Rate: $')
# Determine True or False for mailing list.
#if shift == 1:
#print('Day')
#else:
#print ('Night')
# Create an instance of the ProductionWorker class.
my_productionworker = ProductionWorker(emp_name, emp_number, shift, payrate)
# Display the object's data.
print('Employee Information')
print('---------------------')
print('Employee Name:', my_productionworker.get_emp_name())
print('Employee Number:', my_productionworker.get_emp_number())
print('Shift:', my_productionworker.get_shift())
print('Hourly Pay Rate:$', my_productionworker.get_payrate())
In your method
def get_shift(self):
if self.shift == 1:
self.shift = 'Day shift'
elif self.shift == 2:
self.shift = 'Night shift'
return self.__shift
you're mixing up .shift and .__shift In the if and elif, it should be .__shift and in the assignments it should be just shift (a local variable, not a member variable) and then you should return that local variable (or maybe just return directly from inside the if and elif, depending on how you feel about multiple exit points).
Also, it's a good practice to include a final else in your if / elif, possibly something like this:
else:
shift = 'Cannot convert {} to a valid shift.'.format(self.__shift)
or
else:
raise Exception('Invalid shift value {}.'.format(self.__shift)
which will alert you to the fact that you fell through all the valid options, and give you a hint as to what value is causing the problem.
BTW, you should not be using double underscore variables in this way and, in general, should not try to write Java code in Python. I'd get rid of the getters and setters and just read and write the member values directly.