Reputation: 4513
I'm attempting the canonical pedagogical programming exercise of building a program to simulate a card game. This is how far I am:
class Card:
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
self.card = str(self.suit) + " " + str(self.rank)
class Deck:
def __init__(self):
self.deck = []
def Deck_Maker(self):
ranks = range(1, 11) + ["J", "Q", "K", "A"]
suits = ["Club", "Heart", "Spade", "Diamond"]
return self.deck.append(Card(suits[0], ranks[0]))
Obviously I'm not done, I have to loop over suits and ranks to construct the deck. However, before continuing I want to check that I'm on track. To do this I want to print the results after invoking the Deck_Maker method. I have attempted to do this with the below code...
def __str__(self):
d = self.deck
d2 = d.Deck_Maker()
return str(d2)
Unfortunately, print(Deck().Deck_Maker())
returns None
.
What am I doing wrong? More generally, how do programmers poke around within Classes and Methods while they make them?
Thanks!
Upvotes: 8
Views: 10110
Reputation: 1524
A few suggestions:
One change to Card
I'd suggest: instead of having an attribute card
, initialized in __init__
, remove that last line from __init__
and add a __str__
method:
class Card():
def __init__(self, suit, rank):
self.suit = suit
self.rank = rank
def __str__(self):
return str(self.suit) + " " + str(self.rank)
Now every Card
knows how to represent itself as a str
on demand. You might add a __repr__
method, instead of or in addition to __str__
:
def __repr__(self):
return 'Card(%s, %s)' % (self.suit, self.rank)
The Deck
class
You can also add a __str__
method to the Deck
class.
I've renamed the Deck_Maker
method to make_Deck
, and I've made it a classmethod, because the name suggests that you mean it to be a "factory method", callable on the Deck
class rather than on Deck
instances. The method now does need to return the Deck
it creates.
I also renamed the deck
member variable to cards
, since after all it is a list of Cards, and we will want to use deck
to refer to Decks :)
Finally, I made the lists of suits and ranks class attributes, rather than locals of make_Deck
, on the theory that eventually some other method will want to use them. In Python 3, range
returns an iterable, hence list()
around it.
A couple of issues: I'm not sure what cards of rank 1 are, given that you have "A"
for aces; 2 through 11 seems more likely. (Thus, Deck
s presently have 56 cards :) Also, it will prove awkward to have both int
s and str
s as ranks: surely you'll want to compare ranks, and this representation will fight you. (Solution: store ranks as int
only, and convert in __str__
and __repr__
methods.) You may want rank comparison to be a Card method. In that case, _ranks
and _suits
should be accessible to that class too (module-globals, for example).
But these rank and suit problems are for another day, and I'll leave the definitions basically unchanged.
class Deck():
_ranks = list(range(1, 11)) + ["J", "Q", "K", "A"]
_suits = ["Club", "Heart", "Spade", "Diamond"]
def __init__(self):
self.cards = []
@classmethod
def make_Deck(cls):
"""cls is Deck"""
deck = cls() # make a Deck
for s in cls._suits:
for r in cls._ranks:
deck.cards.append(Card(s, r))
return deck
# A basic example:
def __str__(self):
return ', '.join([str(card) for card in self.cards])
Now you can use code like the following:
>>> deck = Deck.make_Deck()
>>> print(deck) # calls deck.__str__
which prints the str
representations of all 56 (!) cards in deck
, comma-separated and all on one line:
Club 1, Club 2, Club 3, Club 4, Club 5, Club 6, Club 7, Club 8, Club 9, Club 10, Club J, Club Q, Club K, Club A, Heart 1, Heart 2, Heart 3, Heart 4, Heart 5, Heart 6, Heart 7, Heart 8, Heart 9, Heart 10, Heart J, Heart Q, Heart K, Heart A, Spade 1, Spade 2, Spade 3, Spade 4, Spade 5, Spade 6, Spade 7, Spade 8, Spade 9, Spade 10, Spade J, Spade Q, Spade K, Spade A, Diamond 1, Diamond 2, Diamond 3, Diamond 4, Diamond 5, Diamond 6, Diamond 7, Diamond 8, Diamond 9, Diamond 10, Diamond J, Diamond Q, Diamond K, Diamond A
Upvotes: 2
Reputation: 3658
I know it doesn't sound super smart, but a good debuger, with breakpoints, helps. You can actually see the flow of execution.
The bumpy issues are mro (method resolution order along the inheritance chain), class methods (does the result comes from instance/class), and metaclasses (class that creates class with type). To understand these issues, initialisation and what object provided the result, drop a logging or a print statement in every init , and on methods, that says "enters method foo from object baz". This will make things clearer.
Upvotes: 1
Reputation: 1136
The append method on a list returns None so this is expected. Try splitting up your lines:
deck = Deck()
deck.Deck_Maker()
print(deck)
or change the method Deck_Maker to be:
def Deck_Maker(self):
ranks = range(1, 11) + ["J", "Q", "K", "A"]
suits = ["Club", "Heart", "Spade", "Diamond"]
self.deck.append(Card(suits[0], ranks[0]))
return self
Edit: There is also a problem in your str method. When you set d to self.deck you are setting it to be a list. The list doesn't know anything about the method Deck_Maker. Try changing to the method below. This will turn your list into a string. To make it more readable, you will probably also want to add a str method to the card class.
def __str__(self):
return str(self.deck)
Upvotes: 5