Reputation: 107
Just wondering if anyone can help me understand this code a little better. Here is the program:
class Card(object):
""" A playing card. """
RANKS = ["A", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "J", "Q", "K"]
SUITS = ["c", "d", "h", "s"]
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __str__(self):
rep = self.rank + self.suit
return rep
class Hand(object):
""" A hand of playing cards. """
def __init__(self):
self.cards = []
def __str__(self):
if self.cards:
rep = ""
for card in self.cards:
rep += str(card) + "\t"
else:
rep = "<empty>"
return rep
def clear(self):
self.cards = []
def add(self, card):
self.cards.append(card)
def give(self, card, other_hand):
self.cards.remove(card)
other_hand.add(card)
class Deck(Hand):
""" A deck of playing cards. """
def populate(self):
for suit in Card.SUITS:
for rank in Card.RANKS:
self.add(Card(rank, suit)) # <- HERE
def shuffle(self):
import random
random.shuffle(self.cards)
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)
else:
print("Can't continue deal. Out of cards!")
# main
deck1 = Deck()
print("Created a new deck.")
print("Deck:")
print(deck1)
deck1.populate()
print("\nPopulated the deck.")
print("Deck:")
print(deck1)
The thing I'm wondering about is self.add(Card(rank, suit))
it's confusing me. Does Card(rank, suit)
get sent to the add method straight away or does it get sent to the Card class first and then go to the add method? The add method has a card
parameter, what gets put into that parameter?
If I'm not making much sense it's because I've been staring at this for 2 hours.
Thanks
Upvotes: 2
Views: 160
Reputation: 76725
It's pretty simple.
Any time you call a class in Python, you create a new instance of the class. So if you call the Card
class, you create a new instance of class Card
... in other words, you create a new card.
Let's create the ace of spades:
ace_of_spades = Card('A', 's')
But I'm cheating. I looked and saw what strings to pass to Card
when I should be using the pre-defined constants. Let's do it more properly:
ace_of_spades = Card(Card.RANKS[0], Card.SUITS[3])
Hmm, I'm not sure that Card.RANKS[0]
is that easy to understand. Personally I would probably do something like
class Card(object):
RANK_ACE = 'A'
RANK_2 = '2'
# and so on
and then:
ace_of_spades = Card(Card.RANK_ACE, Card.SUIT_SPADES)
Now that we have used the variable name ace_of_spades
to hold a reference to the card, we can use the card:
hand = Hand()
hand.add(ace_of_spades)
But if we just want to get the card into the hand, we don't need that variable name ace_of_spades
. We could remove the name:
del(ace_of_spades)
But there is no need to use the name in the first place. We can simply do:
hand.add(Card(Card.RANK_ACE, Card.SUIT_SPADES))
This creates the card, then immediately passes the newly created card to Hand.add()
to add it to a hand.
You don't, strictly speaking, need to store the ranks and suits inside the Card
class. In Python, it might be more common to put these things outside of the classes, in "module" scope. I would most likely do:
RANK_ACE = 'A'
RANK_2 = '2'
# and so on
SUIT_CLUBS = 'c'
# and so on
class Card(object):
# and so on
But if you are taking a class and your teacher wants you to put constants inside your programs' classes, then do that.
EDIT: You are trying to figure out what self.add()
does in the program.
Here's the source code for it:
# inside class Hand
def add(self, card):
self.cards.append(card)
self
refers to the object. To see how this works, let's imagine you have an instance of class Hand
, saved in the variable h
. If you call h.add(ace_of_spades)
then the self
in this function will be set to the same instance of Hand
that h
refers to.
In fact, the call h.add(ace_of_spades)
is exactly the same as doing this call:
Hand.add(h, ace_of_spades)
When you use the form h.add()
, Python automatically does the same thing as the above call. Python looks at h
and figures out that it is an instance of class Hand
. Then Python looks in class Hand
for a function called add()
. Then Python builds a call similar to the one shown above.
self.cards
refers to a list
object stored inside the Hand
instance. Python lists have a method function, .append()
, that appends an item to the list.
You can read more about classes and method functions here: http://docs.python.org/2/tutorial/classes.html
Upvotes: 4
Reputation: 47138
Q1: Does (Card(rank, suit)) get sent to the add method straight away or does it get sent to the Card class first and then go to the add method?
Here's what's happening in self.add(Card(rank, suit))
:
Card(rank, suit)
calls Card's __new__
and __init__
methods. __new__
creates a new Card
object and the __init__
method initializes it to have the given rank
an suit
.Deck.add()
method where it is appended to self.cards
list (Note that Deck
inherits the cards
list and add
method from Hand
) Here I've stripped the code down to only the necessary methods for reference:
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
class Hand(object):
def __init__(self):
self.cards = []
def add(self, card):
self.cards.append(card)
class Deck(Hand):
def populate(self):
for suit in Card.SUITS:
for rank in Card.RANKS:
self.add(Card(rank, suit))
Q2: The add method has a 'card' parameter, what gets put into that parameter?
The newly created Card
as described above
Upvotes: 2
Reputation: 98078
If you add two print statements like these:
In Card class:
def __init__(self, rank, suit):
print "card.init: {} {}".format(rank, suit)
self.rank = rank
self.suit = suit
In Hand:
def add(self, card):
print "add: {}".format(card)
self.cards.append(card)
You will see this output:
Created a new deck.
Deck:
<empty>
card.init: A c
add: Ac
card.init: 2 c
add: 2c
card.init: 3 c
add: 3c
Upvotes: 1
Reputation: 188
Good question. You are definitely making sense :)
Here's what's happening in self.add(Card(rank, suit)): note that self.add(Card(rank, suit)) is being called from within a nested for loop. The rank and suit parameters are defined by those loops. You are right, Card(rank, suit) is evaluated first, then the returned card object is fed as a parameter to self.add method on the Deck. So, the combination of loops and method calls populates the deck with 52 card objects -- one of each suit and face. Does that make sense?
Upvotes: 2