Here is a sketch of an approach. You can easily combine this with your own approach, the biggest change being for the Card
class. Here, I've used namedtuple
to make a Card
class, but your current class can simply wrap a tuple
value:
import enum
from functools import total_ordering
from collections import namedtuple
@total_ordering
class OrderedEnum(enum.Enum):
def __lt__(self, other):
if isinstance(other, type(self)):
return self.value < other.value
return NotImplemented
Rank = OrderedEnum('Rank', ['one', 'two', 'three', 'four', 'five', 'six',
'seven', 'eight', 'nine', 'jack', 'queen','king', 'ace'])
Suit = OrderedEnum('Suit', ['clubs', 'diamonds', 'hearts', 'spades'])
Card = namedtuple('Card', ['rank', 'suit'])
c1 = Card(Rank.four, Suit.clubs)
c2 = Card(Rank.four, Suit.spades)
c3 = Card(Rank.ace, Suit.diamonds)
Now, in action:
>>> c1
Card(rank=<Rank.four: 4>, suit=<Suit.clubs: 1>)
>>> c2
Card(rank=<Rank.four: 4>, suit=<Suit.spades: 4>)
>>> c1 < c2
True
>>> c1 > c3
False
Tuple sorting is lexicographic! Nice!
>>> hand = [c2, c1, c3]
>>> hand
[Card(rank=<Rank.four: 4>, suit=<Suit.spades: 4>), Card(rank=<Rank.four: 4>, suit=<Suit.clubs: 1>), Card(rank=<Rank.ace: 13>, suit=<Suit.diamonds: 2>)]
>>> sorted(hand)
[Card(rank=<Rank.four: 4>, suit=<Suit.clubs: 1>), Card(rank=<Rank.four: 4>, suit=<Suit.spades: 4>), Card(rank=<Rank.ace: 13>, suit=<Suit.diamonds: 2>)]
>>>
Note, I've used the total_ordering
decorator, which is simply a shortcut, and indeed, I think it might be better to simply do the whole class by hand. Here's a recipe.
EDIT
So, to elaborate, here is how I would implement your Card
and Deck
classes. Notice how much more readable your code becomes when you use the enum
and namedtuple
.
import enum
from functools import total_ordering
from collections import namedtuple
from random import shuffle
@total_ordering
class OrderedEnum(enum.Enum):
def __lt__(self, other):
if isinstance(other, type(self)):
return self.value < other.value
return NotImplemented
Rank = OrderedEnum('Rank', ['one', 'two', 'three', 'four', 'five', 'six',
'seven', 'eight', 'nine', 'jack', 'queen','king', 'ace'])
Suit = OrderedEnum('Suit', ['clubs', 'diamonds', 'hearts', 'spades'])
CardValue = namedtuple('CardValue', ['rank', 'suit'])
@total_ordering
class Card(object):
def __init__(self, rank, suit):
self.value = CardValue(rank, suit)
def __repr__(self):
return "Card({:s}, {:s})".format(self.value.rank, self.value.suit)
def __lt__(self, other):
if isinstance(other, __class__):
return self.value < other.value
return NotImplemented
def __eq__(self, other):
if isinstance(other, __class__):
return self.value == other.value
return NotImplemented
class Deck(object):
def __init__(self):
self.cards = []
for rank in Rank:
for suit in Suit:
self.cards.append(Card(rank, suit))
shuffle(self.cards)
Now, in action:
>>> deck = Deck()
>>> c1 = deck.cards.pop()
>>> c2 = deck.cards.pop()
>>> c1
Card(Rank.queen, Suit.hearts)
>>> c2
Card(Rank.king, Suit.clubs)
>>> c1 == c2
False
>>> c1 > c2
False
>>> c1 < c2
True
>>> c1.value
CardValue(rank=<Rank.queen: 11>, suit=<Suit.hearts: 3>)
>>> c2.value
CardValue(rank=<Rank.king: 12>, suit=<Suit.clubs: 1>)
Also, notice that __repr__
should try to represent the object, if you want a pretty message, use __str__
. See this question