2

I'm playing around with some basic card/deck manipulations in python. Below you can see my Card class and Deck class. Assume that I know that some cards are dead and would like to remove them from the deck.

import itertools

SUIT_LIST = ("h", "s", "d", "c")
NUMERAL_LIST = ("2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A")

class Card:
    def __init__(self, numeral, suit):
        self.numeral = numeral
        self.suit = suit
        self.card = self.numeral, self.suit
    def __repr__(self):
        return self.numeral + self.suit

class Deck(set):
    def __init__(self):
        for numeral, suit in itertools.product(NUMERAL_LIST, SUIT_LIST):
            self.add(Card(numeral, suit))

deck = Deck()    
dead_card = Card('J','s')
deck.remove(dead_card)

Raises the following error:

Traceback (most recent call last):

  File "<ipython-input-93-06af62ea1273>", line 23, in <module>
    deck.remove(dead_card)

KeyError: Js

What is the proper way of removing dead cards from my deck? And why the way I do it does not work?

wim
  • 338,267
  • 99
  • 616
  • 750
kroonike
  • 1,109
  • 2
  • 13
  • 20

1 Answers1

4

You'll need two new methods on your Card class, for membership in sets and dicts to work sanely:

class Card:
    ...
    def __hash__(self):
        return hash(self.card)
    def __eq__(self, other):
        if isinstance(other, Card):
            return self.card == other.card
        return NotImplemented

This is because sets are implemented with hash tables, and unless you define how to hash and compare instances of your custom class, a hash based on the identity (memory location in CPython) will be used as a default. Using id provides a fairly crappy default here - two cards with same number/suit would not be considered "equal" and the membership in a Deck is not recognised.

wim
  • 338,267
  • 99
  • 616
  • 750
  • don't you need `__ne__` as well? – Jean-François Fabre Jul 31 '18 at 14:38
  • No, I don't believe you do here. – wim Jul 31 '18 at 14:39
  • @Jean-FrançoisFabre The underlying algorithm uses ``__hash__`` to deduce positions in the hash table where an item *might* be. It then uses ``__eq__`` to test if is actually there, or just another object with the same hash. No other special method is used. – MisterMiyagi Jul 31 '18 at 14:49
  • related: https://stackoverflow.com/questions/4169252/remove-duplicates-in-list-of-object-with-python. Couldn't find a proper duplicate, a lot of q&a are similar, but nothing like this exactly. – Jean-François Fabre Jul 31 '18 at 14:54