I saw square bracketed arguments inside of parentheses. I don't know what it means and how it differs from a normal argument, so I am hoping for some enlightenment on this matter.
Here are the code lines in question:
(I'll print the whole block since someone might find it useful but the important parts are line 3 of the first code snippet and line 2 of the second code snippet)
First case:
def __additional_cards(self, player):
while not player.is_busted() and player.is_hitting():
self.deck.deal([player])
print(player)
if player.is_busted():
player.bust()
The bracketed argument in this line represents an object of a Player class (there can be many players) for the game and the code's purpose is to deal an additional card if the player asks for it.
Second case:
def play(self):
self.deck.deal(self.players + [self.dealer], per_hand = 2)
self.dealer.flip_first_card() # hide dealer's first card
for player in self.players:
print(player)
print(self.dealer)
In this case, the method .deal is used to deal starting cards to all of the players in the game as well as to the dealer. Here's the code for the .deal method for reference:
def deal(self, hands, per_hand = 1):
for rounds in range(per_hand):
for hand in hands:
if self.cards:
top_card = self.cards[0]
self.give(top_card, hand)
As you can see, it only takes 1 argument as to which 'hands' the cards should be dealt with, but the code above adds the dealer with the command + [self.dealer] and this is something I see for the first time.
Is the dealer added to hands simply by the + and if so, why is it in square brackets?
The code is taken from Michael Dawson's book, Python Programming for the Absolute Beginner, 3rd Edition and it is a section where he teaches OOP by explaining how to make a simple game of Blackjack.
The method deck.deal() takes as an argument a list of people to deal to. You cannot pass it a single object, because the method probably loops through every object in the list passed in a deals to those people. When you input [player], you're telling it the only person to deal to is that single player. It's good to leave it as a list because it allows you to generalize the code to accept an arbitrary number of people to deal to.
Looking at self.deck.deal(self.players + [self.dealer], per_hand = 2), what is happening here is we're adding the dealer to the list of the people being dealt to. Again, deal takes in a list of people, and since we want to deal to all the current players and the dealer we can just combine those two things into a single list. self.players is probably just a list of all the people who are playing, and when we + [self.dealer] we create a new list that has all of the players and the dealer. Python has operator overloading, meaning we can "add" two lists to combine them into a single longer list.
If I was coding this, I would just make player always be a list itself and self.dealer always be a single element list, since the code is generalized to only accept lists it seems.
EDIT for clarification:
I imagine the method deal looks something like:
def deal(people):
'''
people = a list of person objects to deal to
'''
for person in people:
# do something to that person
player is just an instance of an object. If we want to deal to ONLY that person, we need to pass in player in a list: [player] otherwise the code to deal to an arbitrary number of people would break, since you cannot loop over the player object, only a list of people.
Related
My very first post, sorry if I made any mistakes!
I'm messing around the pygame library, and I'm extremely new to python in general. I have created 3 enemies classes and they work as intended, however, this is how I make them move (I call the move function stored inside the class).
I wonder if there is a more clean way to do things. I have many other enemies to code and this seems very repetitive to type, so it's a sign I'm doing something wrong.
I tried creating a "control list" where I list every enemy list there is and I try to access them through their index but it isn't working. I also tried to concatenate but I'm getting an error saying I can't concatenate list names, another error was that my list names turned into strings (yes, I tried using quotation marks). I'm sure this will be a simple fix, but I spent 3 days and I wrap my head around it. If it's possible to do so
The sample of my code so far - it is located in the main run loop of my game.
for giant in lst_enemy_giants:
giant.move()
else:
pass
for spider in lst_enemy_spiders:
spider.move()
else:
pass
for goblin in lst_enemy_goblin:
giant.move()
else:
pass
# The pattern the I want
for ENEMY in lst_enemy_ENEMY:
ENEMY.move()
# where ENEMY is any enemy list that can be stored somewhere
Just chain the iterators together.
from itertools import chain
for enemy in chain(lst_enemy_giants, lst_enemy_spiders, lst_enemy_goblin):
enemy.move()
This is a slightly nicer way of writing a nested loop like
for enemy_list in [lst_enemy_giants, lst_enemy_spiders, lst_enemy_goblin]:
for enemy in enemy_list:
enemy.move()
Unrelated, but I recommend keeping a single dict like
enemies = {'giants': [...], 'spiders': [...], 'goblins': [...]}
rather than three separate variables referencing separate lists. You can still use, for example, enemies['giants'] anywhere you would have used lst_enemy_giants, but now you can write
for enemy in chain(*enemies.values()):
enemy.move()
I am fairly new to intermediate programming, yet have played around with code for a while.
At the moment I am making a simple card game.
This issue is I am not sure where to use my functions or when to make them class methods.
For example, here is a simple function that deals out 5 cards to each player (from a predefined list) and then turns a card from the top of the pack (actually all just random selections).
The cards are returned as a list of items (3 lists).
I have also made a class called "Player".
p1_hand = []
p2_hand = []
flip_card = []
def deal_deck():
count = 0
for i in range(0, 10, 1):
count += 1
if count % 2 == 0:
card = random.choice(deck)
p2_hand.append(card)
deck.remove(card)
else:
card = random.choice(deck)
p1_hand.append(card)
deck.remove(card)
if count == 10:
flip_card.append(random.choice(deck))
return p2_hand, p1_hand, flip_card
In this example, it's just a deal, so I wonder why it would need to be a class method of "Player"?
Possibly the class "Player" does not do much at all except keep score, keep track of who is dealing and what the score is?
To put it simply I am having trouble understanding the class as an object that interacts and preforms actions, I have made other classes and they have ended up working like a mini database rather than using much complexity in the methods at all.
There is an art to designing classes and objects. The fundamental purpose of using classes is information hiding. The rest of your program should not have to know how a deck of cards is implemented, which allows you to change the implementation without redesigning the whole program. Thus, you create a Deck class that has all of the data stored internally, and only exposes to the outside world the things you want to DO with a deck, like shuffle and deal_card. A Player class might include a hand and a score, and functions to add another card, and the Game object (maybe) could coordinate dealing cards into the hand and triggering the plays.
The code you have is mixing all of this. It has to know how a deck is implemented, and how a hand is implemented, and how a card is flipped.
By the way, for the sake of realism, it would be better for you to shuffle the deck and deal cards off the top, instead of using random.choice.
I originally down-voted this as too broad, but changed my mind as I wrote up my notes for you. Classes are a programming tool whose implementation doesn't receive much treatment at the level you're asking. There are many examples of good card-game classes available on the Internet ... and many bad ones. The research isn't easy for you.
Use a class to represent a unit of your system (a card game, in this case) that is cohesive (a set of data and capabilities with a readily understood boundary) and interacts with other units, or with the main program.
In this case, you have a good start: you've identified card, player, and hand as entities in your game system. You may want to treat the deck as a hand instance (just another list of cards), or you may want to give it special treatment due to different functions within the game.
The classes and functions I've seen as useful include:
Deck
The impartial source of cards
data
a list of cards
methods
reset -- shuffle all 52 cards into a list
deal(n) -- return a list of n cards
Hand
cards held by a single player
data
a list of cards
methods
reset -- whatever is needed to return the hand to a game-start state
draw(n) -- acquire n cards
play(n) -- play n cards to the game area
Card
A single card, containing all information needed to identify it to the game
data
suit
rank
methods
none
Player
Game information about each player
data
hand -- see Hand class above
game-role -- depending on the game, this could be "dealer", "idle", "active", ...
... other, depending on the game: points, money, etc.
methods
... very dependent on the game being played
Game
the overall monitor for the game
data
roster -- list of players
deck -- see Deck class above
... other, depending on the game: round, player to play next, etc.
Some of these overlap a bit, such as "Deck.deal" and "Hand.draw".
One of the design decisions you face is to choose which entity will drive an interaction between two objects.
As for implementation, I do suggest that you make your basic operations a lot simpler.
For the desk, initialize by generating all 52 cards with a nested list comprehension.
shuffle the deck, and then use list slicing to deal the cards. For instance, to deal 5 cards to each player:
def deal(n):
take = deck[:n]
deck = deck[n:]
return take
# elsewhere, to drive the initial dealing ...
for player in roster:
player.hand = deck.deal(5)
Simply reduce the interactions to match the way you talk about the game: "deal each player five cards" should look just like that in the code. Bury the mechanics within each class.
It's the last piece of code I cannot grasp from the official solution for the PokerHand exercise from ThinkPython book:
card.py
and
PokerHandSoln.py
def has_straightflush(self):
"""Checks whether this hand has a straight flush.
Better algorithm (in the sense of being more demonstrably
correct).
"""
# partition the hand by suit and check each
# sub-hand for a straight
d = {}
for c in self.cards:
d.setdefault(c.suit, PokerHand()).add_card(c)
print '\n', c.suit, '\n', d[c.suit]
wait = raw_input()
# see if any of the partitioned hands has a straight
for hand in d.values():
if len(hand.cards) < 5:
continue
hand.make_histograms()
if hand.has_straight():
return True
return False
print and wait are mine to try to figure out what it does.
I wrote my own solution but of course it's different from the author's one.
My solution works after some debugging, but I'm studying the author's official solution. Self-starter and Python newbie here
I got how setdefault works, but not the add_card method.
The add_card method is a class method that executes an append() operation.
The values of the dictionary are class objects.
So is that code just executing the method proper of that class, on an object of that class? It's a very concise code expression and a bit difficult to grasp for me.
dict.setdefault() returns the value for the given key (having optionally set a default value first, if the key is missing).
The d.setdefault(c.suit, PokerHand()).add_card(c) expression makes sure there is a value for the c.suit key, and returns that. The default is PokerHand(), an instance of a class that defines the add_card() method.
Split it out into two statements, if that makes it easier to read:
hand_for_suit = d.setdefault(c.suit, PokerHand())
hand_for_suit.add_card(c)
In the end you have grouped the cards into hands by suit; all diamond cards, all hearts, all clubs and all spades each grouped into a separate PokerHand() instance. This makes it easier to test if there is a straight flush, an ordered series of cards in the same suit.
I need to make a league table for a project. There has to be 3 files,2 files consist of 1 class and the last file is for running a program. I have done all of the parts but when I call a method to add a team, the program adds the name but it does not insert it into the list of teams(which should do). When I try to display the items in the list, the program displays an error message instead of showing the actual team.
How can I fix it?Any help would be appreciated. :)
A few things here:
When I try to display the items in the list, the program displays: team.Team object at 0x000000000332A978 insted of showing the actual team.
The default display for a user class is something like <team.Team object at 0x000000000332A978>. If you want it to display something different, you have to tell Python what you want to display. There are two separate functions for this: __repr__ and __str__. The idea is that the first is a representation for the programmer, the second for the user. If you don't need two different representations, just define __repr__ and it'll use that whenever it needs __str__.
So, a really simple way to fix this is to add this to the Team class:
def __repr__(self):
return 'Team("{}")'.format(self._name)
Now, if you call league.addTeam('Dodgers'), then print(l._table), you'll get [Team("Dodgers")] instead of [<team.Team object at 0x000000000332A978>].
Meanwhile, these two methods are probably not what you want:
def removeTeam(self,team):
self._table.remove(team)
def returnPosition(self,team):
return self._table.index(team)
These will remove or find a team given the Team object—not the name, or even a new Team created from the name, but a reference to the exact same object stored in the _table. This is not all that useful, and you seem to want to call them with just names.
There are two ways to fix this: You could change Team so that it compares by name instead of by object identity, by adding this method to the class:
def __eq__(self, other):
return self._name == other._name
What this means is that if you say Team('Giants') == Team('Giants'), it will now be true instead of False. Even if the first team is in a different league, and has a different W-L record, and so on (e.g., like the baseball "Giants" from San Francisco vs. the football "Giants" from New York), as far as Python is concerned, they're now the same team. Of course if that's not what you want, you can write any other __eq__ function that seems more appropriate.
Anyway, if you do this, the index and remove functions will now be able to find any Team with the same name, instead of just the exact same team, so:
def removeTeam(self,team_name):
self._table.remove(Team(team_name))
def returnPosition(self,team_name):
return self._table.index(Team(team_name))
If you go this way, you might want to consider defining all of the comparison methods, so you can, e.g., sort a list of teams, and they sort by name.
Or you could change these methods so they don't work based on equality, e.g., by redefining them like this:
def removeTeam(self,team_name):
self._table = [team for team in self._table if team._name != team_name]
def returnPosition(self,team_name):
return [team._name for team in self._table].index(team_name)
To understand how these work, if you're not used to reading list comprehensions, turn each one back into the equivalent loop:
self._table = [team for team in self._table if team._name != team_name]
temp = []
for team in self._table:
if team._name != team_name:
temp.append(team)
self._table = temp
If you step through this, temp ends up with a list of every team in the table, except the one you wanted to remove, and then you replace the old self._table with the new filtered one. (Another way to write the same idea is with filter, if you know that function.)
It's usually better to create a new filtered list than to modify a list in-place. Sometimes there are performance reasons not do this, and sometimes it ends up being very complex and hard to understand, but it's usually both faster and simpler to reason about. Also, modifying lists in place leads to problems like this:
for i, value in enumerate(mylist):
if value == value_to_remove:
del mylist[i]
Play with this for a while, and you'll see that it doesn't actually work. Understanding why is a bit complicated, and you probably don't want to learn that until later. The usual trick to solve the problem is to iterate over a copy of the list… but once you're doing that, you've now got the worst of filtering and the worst of deleting-in-place at the same time.
The second function may be a little too clever, but let's look at it:
def returnPosition(self,team_name):
return [team._name for team in self._table].index(team_name)
First, I'm creating a list like the original one, but it's a list of just the names instead of the team objects. Again, let's decompose the list comprehension:
temp = []
for team in self._table:
temp.append(team._name)
Or try to translate it into English: This is a list of the team name of every team in the table.
Now, because this is a list of team names, I can use index(team_name) and it will find it. And, because the two lists have the same shape, I know that this is the right index to use in the original team list as well.
A much simpler solution would be to change _tables from a list of Teams into a dict mapping names to Teams. This is probably the most Pythonic solution—it looks a lot simpler than writing list comprehensions to do simple operations. (It's also probably the most efficient, but that's hardly relevant unless you have some truly gigantic leagues.) And then you don't even need returnPosition for anything. To do that:
def __init__(self):
self._table={}
def addTeam(self,name):
self._table[name]=Team(name)
def removeTeam(self,team_name):
del self._table[team_name]
def returnPosition(self,team_name):
return team_name
def updateLeague(self,team1_name1,team_name2,score1,score2):
if score1>score2:
self._table[team_name1].win()
self._table[team_name2].loss()
elif score1==score2:
self._table[team_name1].draw()
self._table[team_name2].draw()
elif score1<score2:
self._table[team_name1].loss()
self._table[team_name2].win()
Note that I've defined returnPosition to just return the team name itself as the position. If you think about it, dict keys are used exactly the same way as list indices, so this means any code someone wrote for the "old" API that required returnPosition will still work with the "new" API. (I probably wouldn't try to sell this to a teacher who assigned a problem that required us to use returnPosition, but for a real-life library where I wanted to make it easier for my 1.3 users to migrate to 2.0, I probably would.)
This only requires a few other changes. In displayList and saveList, you iterate over self._table.values() rather than self._table; in loadList, you change self._table.append(team) to self._table[a] = team. Speaking of loadList: You might want to consider renaming those local variables from a, b, c, and d to name, wins, losses, and draws.
A few other comments:
As kreativitea says in the comments, you should not create "private" variables and then add do-nothing accessor methods in Python. It's just more boilerplate that hides the real code, and one more thing you can get wrong with a silly typo that you'll spend hours debugging one day. Just have members named name, wins, losses, etc., and access them directly. (If someone told you that this is bad style because it doesn't let you replace the implementation in the future without changing the interface, that's only true in Java and C++, not in Python. If you ever need to replace the implementation, just read up on #property.)
You don't need print("""""")—and it's very easy to accidentally miscount the number of " characters. (Especially since some IDEs will actually be confused by this and think the multi-line string never ends.) Just do print().
You've got the same ending condition both in the while loop (while x!="q":) and in an internal break. You don't need it in both places. Either change it to while True:, or get rid of the break (just make options("q") do print("Goodbye"), so you don't need to special-case it at all inside the loop).
Whenever you have a long chain of elif statements, think about whether you can turn it into a dict of short functions. I'm not sure it's a good idea in this case, but it's always worth thinking about and making the explicit decision.
The last idea would look something like this:
def addTeam():
name=input("Enter the name of the team:")
l.addTeam(name)
def removeTeam():
teamToRemove=input("Enter the name of the team you want to remove:")
l.removeTeam(teamToRemove)
def recordGame():
team1=input("What is the name of the team?")
ans1=int(input("Enter the number of goals for the first team:"))
team2=input("What is the name of the team?")
ans2=int(input("Enter the number of goals for the second time:"))
l.updateLeague(team1,team2,ans1,ans2)
optionsdict = {
"a": addTeam,
"d": l.displayList,
"s": l.saveList,
"l": l.loadList,
"r": removeTeam,
"rec": recordGame,
}
def options(x):
func = optionsdict.get(x)
if func:
func()
As I said, I'm not sure it's actually clearer in this case, but it's worth considering.
My situation is as follows: I'm working on an implementation of BlackJack. I've got classes for Card, Hand, Player, Deck, and Game. The main game object stores players and a deck, while players stores hands which store cards.
I often do something like the following. In this example I am dealing the initial cards to each player.
num_cards = 2
for player in self.players:
new_hand = Hand()
for i in range(num_cards):
new_hand.add_card(self.deck.get_next_card())
player.assign_hand(new_hand)
This works splendidly. My problem now is that I wish to delete a hand from a player's set of hands (a player can split in BlackJack, causing more than one hand to be generated). In the following function, I intend to loop through each player's hands. If the value of the hand is greater than 21, I want to delete the hand. (Note that the remove() functionality below is normally performed in the Player class, called via a Player method named fold_hand(). I was having the same problem, so I have moved the code to somewhere more visible for expository purposes.)
for player in self.players:
for hand in player.hands:
if hand.smallest_value() > 21:
player.hands.remove(hand)
This does not work. To be clear, I am able to print out the hand before the remove() line and it does not print out after. That is, it seems to be removed. However, the in the next turn of play, the hand is back again. Thus the players' hands grow every turn.
The above code is in a function called validate_player_hands() in the Game class. This function is called from a file called play.py, which exists to start/end the game and facilitate the primary game loop. Thus, the only call to validate_player_hands() is in the play.py file, one indent in, in the game loop. I call:
game.validate_player_hands()
I have also tried finding the index of the hand and using the 'del' keyword, but the result is the same.
Why would the list element (a Hand object in a list called player.hands) fail to delete when it looks like it has been deleted?
Thanks in advance,
ParagonRG
How about using a simple list comprehension to eliminate hands:
for player in self.players:
player.hands = [hand for hand in player.hands if hand.smallest_value() <= 21]
EDIT
With filter:
for player in self.players:
player.hands = filter(lambda x: x.smallest_value() <= 21, player.hands)
Create a copy and iterate over the the object with an index tied to the object length. Set every elements to be deleted with a 0 and then filter the hands to purge the zeros.
for player in self.players:
for hand_idx in range(len(player.hands)):
if player.hands[hand_idx].smallest_value() > 21:
player.hands[hand_idx]=0
player.hands=filter(None,hands)
You will want to do something like the following:
newList = hands[:]
for hand in newList:
if hand.smallest_value() > 21: #your code here
player.hands.remove(hand)
This allows you to make a copy of the list to be modified while iterating over that copy thus avoiding the "tree limb" scenario that Mihai mentioned.
This can be solved by adding [:] to the end of the "for" statement. This creates a copy of the list. You can then loop over the copy, while changing the original list:
for player in self.players:
for hand in player.hands[:]:
if hand.smallest_value() > 21:
player.hands.remove(hand)
(This is Python's list slicing syntax. It's one of the fastest way to copy a list. More often this syntax is used in the form some_list[3:8] to get the list entries from index 3 to 7 inclusive, but by leaving out the first number you can get everything from the start of the list, and by leaving out the last number you get everything up to the end of the list).