user1527227
user1527227

Reputation: 2238

python "in" application for classes

How can you extend "in" keyword to a class I made? I am making a card game with a Card class. There is another class which is a Hand of a player. Basically I want to see if a certain card is in a hand. An analogy is below:

>>> 5 in range(0, 5)
True

This is my code. I have a Hand class and I want to see if a Card() is in a Hand()

Also, I'm new to this concept of classes. I'm just starting to understand how this whole thing works. Did I implement len method correctly?

class Card:
    def __init__(self, suit, rank):
        # self.suit > str
        # self.rank > str
        if (suit in SUITS) and (rank in RANKS):
            self.suit = suit
            self.rank = rank
        else:
            self.suit = None
            self.rank = None
            print "Invalid card:", suit, rank

    def __str__(self):
        return self.suit + self.rank

    def get_suit(self):
        return self.suit

    def get_rank(self):
        return self.rank


# define hand class
class Hand:
    # A list of Card objects
    # adding cards to the hand should remove cards from the deck.

    def __init__(self):
        self.hand = []

    def __str__(self):
        cards = []
        for card in self.hand:
            cards += [card.get_suit() + card.get_rank()]
        return str(cards)

    def add_card(self, card):
        return self.hand.append(card)

    def __len__(self):
        counter = 0
        for card in self.hand:
            counter +=1
        return counter

OK, so I added this code in the hand class:

    def __contains__(self, card):
        return card in self.hand

but I tried testing my code and it doesn't work:

c = Card('H','A')

h = Hand()
h.add_card(Card('S','K'))
h.add_card(Card('D','A'))
h.add_card(Card('H','A'))

print 'hand=', h
print 'c=', c
print 'c in h', c in h

It says False in terminal... Why??

Upvotes: 2

Views: 296

Answers (2)

Blckknght
Blckknght

Reputation: 104712

@BrenBarn gave you a pointer in the right direction to look at __contains__. However, as I commented on his answer, implementing that method will probably require that your Card objects be comparable. Right now, two cards will only appear equal if they are both the same object.

For an example of what I mean, try this:

c1 = Card("H", "A")
c2 = Card("H", "A")

print c1 == c2 # False!

To fix this, you need to add the __eq__ method to your Card class (and probably the __ne__ method too, so you'll be able to use != tests). Here's a possible implementation:

def __eq__(self, other):
    return self.suit == other.suit and self.rank == other rank

def __ne__(self, other):
    return not self == other

There's one other thing I'd like to point out (unrelated to your question). Your Card class has "getter" methods for the suit and rank. Those are usually unnecessary in Python, where you can generally program everything using public variables at first. That is, anything that currently calls card.get_suit should just access card.suit instead.

In less common situation where you need to do complicated things in response to variable access (like calculating certain values when they're requested, or preventing certain values from being assigned), you can put a Property instance in the class (usually as a decorator to a function), and external code can still access it just as if it was still a public variable. Code with lots of getters is common in other programming languages which can't switch between regular variables and Properties like Python can.

Upvotes: 1

BrenBarn
BrenBarn

Reputation: 251383

You're looking for the __contains__ magic method.

As for len, your implementation gives the right result, but is needlessly complicated. You can just do:

def __len__(self):
    return len(self.hand)

Upvotes: 4

Related Questions