Reputation: 2238
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
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
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