I defined a class that holds a list collection of objects, and defined the __iter__ and __next__ methods to make it for loopable. The collection here, is a Deck class that holds a list of Card objects.
Code:
import random
class Card:
@staticmethod
def get_ranks():
return ("A", "K", "Q", "J", "10", "9", "8", "7", "6", "5", "4", "3", "2") # A is highest, 2 is lowest
@staticmethod
def get_suites():
return ("H", "D", "S", "C")
def __init__(self, suite, rank):
if suite not in Card.get_suites():
raise Exception("Invalid suite")
if rank not in Card.get_ranks():
raise Exception("Invalid rank")
self.suite = suite
self.rank = rank
def __lt__(self, card2):
self_rank = Card.get_ranks().index(self.rank)
card2_rank = Card.get_ranks().index(card2.rank)
return self_rank > card2_rank
def __le__(self, card2):
self_rank = Card.get_ranks().index(self.rank)
card2_rank = Card.get_ranks().index(card2.rank)
return self_rank >= card2_rank
def __gt__(self, card2):
self_rank = Card.get_ranks().index(self.rank)
card2_rank = Card.get_ranks().index(card2.rank)
return self_rank < card2_rank
def __ge__(self, card2):
self_rank = Card.get_ranks().index(self.rank)
card2_rank = Card.get_ranks().index(card2.rank)
return self_rank <= card2_rank
def __eq__(self, card2):
self_rank = Card.get_ranks().index(self.rank)
card2_rank = Card.get_ranks().index(card2.rank)
return self_rank == card2_rank
def __ne__(self, card2):
self_rank = Card.get_ranks().index(self.rank)
card2_rank = Card.get_ranks().index(card2.rank)
return self_rank != card2_rank
def __str__(self):
return(self.rank + self.suite)
def __repr__(self):
return str(self)
class Deck:
def __init__(self):
self.contents = [Card(suite, rank) for suite in Card.get_suites() for rank in Card.get_ranks()]
random.shuffle(self.contents)
self.index = 0
def __len__(self):
return len(self.contents)
def __iter__(self):
return self
def __next__(self):
if self.index == len(self.contents):
raise StopIteration
item = self.contents[self.index]
self.index += 1
return item
def pick_card(self):
choice = random.randrange(len(self))
card = self.contents.pop(choice)
return card
def return_card_and_shuffle(self, card):
self.contents.append(card)
random.shuffle(self.contents)
def __str__(self):
dstr = ''
for card in self:
dstr += str(card) + ", "
return "{} cards: ".format(len(self)) + dstr[:-2]
def deal_bookends(deck):
card1 = deck.pick_card()
card2 = deck.pick_card()
if card1 > card2:
temp = card1
card1 = card2
card2 = temp
return (card1, card2)
if __name__ == '__main__':
deck = Deck()
for _ in range(3):
c1, c2 = deal_bookends(deck)
print("We have {} and {}".format(c1, c2))
print(deck)
deck.return_card_and_shuffle(c1)
print(deck)
print(deck.contents[-4:])
deck.return_card_and_shuffle(c2)
print(deck)
print(deck.contents[-4:])
On running, I get the following error:
We have 8H and KH
50 cards: 9H, 8C, AC, 7C, 6H, 2S, 2D, 5C, 10H, 5H, JS, 5S, KD, JH, JC, QS, 2H, 3H, 3S, 3D, 4C, 4H, AD, KS, JD, QH, 10D, 6S, 5D, 8D, 3C, 6C, 7D, AS, 7H, AH, 9S, 10C, QC, QD, 7S, 2C, KC, 8S, 4D, 4S, 6D, 10S, 9D, 9C
51 cards: QS
[7D, 5C, 10H, QS]
52 cards: 10C
[KC, 3S, 9H, 10C]
We have 2C and QD
Traceback (most recent call last):
File "playing_cards.py", line 106, in <module>
print(deck)
File "playing_cards.py", line 88, in __str__
for card in self:
File "playing_cards.py", line 73, in __next__
item = self.contents[self.index]
IndexError: list index out of range
It seems the thing doesn't for the second run of the for loop when I push the card object back into the list. How do I solve this while keeping the pop,push functionality.
Edit: The self.index is at 50 after the first call to print(). When the card is added back to list, index remains at 50, whereas the deck length is now 51 cards. So in the second (and third) call to print the last card is printed instead of the entire deck. Then subsequently error is raised.
I think I have read the documentation wrong here. My question is should I reset the index at the StopIteration bit. Is that the correct way to do this, or is the index supposed to reset on its own?
return iter(self.contents)in__iter__? No need to reinvent the wheel.list. This will give you all of the behavior you want, then you can add deck specific stuff on top.DeckIteratorobject that maintains its own position. In the way you have it if you iterate over the Deck within an iteration of the deck you'll have issues. See: docs.python.org/3.3/reference/datamodel.html#object.__iter____iter__return self.contents and not implement__next__?