python "in" application for classes - python

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??

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)

#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.

Related

Python object keeping data from previous?

I've seen multiple instances of this question like this one, but it fails to identify what exactly I am doing wrong since I don't have default arguments.
What am I doing wrong? Python object instantiation keeping data from previous instantiation?
#Table.py
class Table:
def __init__(self, players):
self.deck = Deck()
And this is Main
t = Table(2)
print len(t.deck.cards)
t = Table(2)
print len(t.deck.cards)
I would expect this to print 48 each time, but instead it prints
48 and then 96
Why is this? Shouldn't this member variable be overridden every time?
#Deck.py
from Card import *
import random
class Deck:
suits = ['H','C','D','S']
numbers = [2,3,4,5,6,7,8,9,10,11,12,13,14]
cards = []
def __init__(self):
for num in self.numbers:
for suit in self.suits:
c = Card(num,suit)
self.cards.append(c);
random.shuffle(self.cards)
Card.py
class Card:
def __init__(self, num, suit):
self.num = num
self.suit = suit
def __repr__(self):
return str(self.num) + str(self.suit)
def __str__(self):
return str(self.num) + str(self.suit)
Initialize cards in the constructor, like this:
def __init__(self):
self.cards = []
for num in self.numbers:
for suit in self.suits:
c = Card(num,suit)
self.cards.append(c);
random.shuffle(self.cards)
That way, every time a new instance of the class is created, cards will be freshly initialized.
Your approach didn't work as you wished, since cards is a class data member, shared among all instances of class Deck.
suits, numbers and cards are class variables. So when doing self.cards.append(c) you add to a class variable, which is shared by all instances of all Deck instances.
Put them into __init__ instead:
def __init__(self):
self.cards = []
for num in self.numbers:
for suit in self.suits:
c = Card(num,suit)
self.cards.append(c);
random.shuffle(self.cards)
You are using class variables instead of instance variables. See, for example, python class variables
So even though you instantiate a new instance, you don't get a new instance of the static class variables.
Suits, numbers, cards. If you want instance variables, use "self.", and do it in the init function.
You are appending cards each time you instantiate, but you are appending them to the class variable. Thus you end up with twice as many.

class attribute doesnt work properly (python)

While trying to come up with a Hand class for a card game
I encountered a strange behavior from an attribute
if I try to set self.number as seen below it wont show the proper output
but if I make the same argument through a function total() it works properly
my question is: why does the attribute self.number not getting the value of len(self.cards)?
class Hand (object):
def __init__(self,number=0,cards=[]):
self.cards=cards
self.number=len(self.cards)
def total(self):
return len(self.cards)
hand=Hand()
hand.cards.append(9)
print hand.cards
print len(hand.cards)
print hand.number
print hand.total()
output:
[9]
1
0 #I want this to be equal to 1
1
The attribute self.number is set at instantiation, use a property instead.
class Hand (object):
def __init__(self, cards=None):
self.cards = cards or []
#property
def number(self):
return len(self.cards)
def total(self):
return len(self.cards)
Setting an instance variable to an expression does not create a binding between the inputs of that expression and the value of the instance variable. In other terms, setting self.number to len(self.cards) does not mean that self.number will get updated whenever you update self.cards.
Your program is functioning properly: When you create your object, len(self.cards) is 0, so self.number gets set to 0. When you change hand.cards, there are no statements changing self.number, so it stays 0.
The proper way to make your self.number attribute update is to use a getter-setter pair in order to make sure that self.number changes whenever self.cards changes.
The Pythonic way to create a getter-setter pair is to use the #property decorator.
In your case, you could do it like this:
class Hand(object):
def __init__(self, number = 0, cards = None):
self.cards = cards or []
#property
def number(self):
return len(self.cards)
This way, even though it looks to anyone uses your class that they are reading an attribute, what actually happens under the hood is that the number() method gets called and correctly computes the current length of self.cards.

How to properly implement/overload "__repr__ "?

New to python and this might be a silly question, but how does one properly implement the repr method?
I wrote a quick little program to simulate a game of cards but I don't know what to write for the repr method. The repr method for the Card class was pretty straight forward, but I don't know what to do for the DeckOfCards class Here's my code:
import random
class Card:
'''Create a single card, by id number'''
# Class variables, created once for the class
suits = [ '\u2660', '\u2661', '\u2662', '\u2663' ]
ranks = [ 'A','2','3','4','5','6','7','8','9','10','J','Q','K' ]
def __init__(self, n=0):
# instance variables for _num, _rank, _suit, _value
if 0 <= n < 52:
self._num = n
self._rank = Card.ranks[n%13] # note referencing class vars
self._suit = Card.suits[n//13]
self._value = n%13 + 1
if self._rank == 'A':
self._value = 14
else: # invalid card indicators
self._rank = 'x'
self._suit = 'x'
self._value = -1
def __repr__(self):
return self._rank + self._suit
def __lt__(self,other):
return self._value < other._value
def __le__(self,other):
return self._value <= other._value
def __eq__(self,other):
return self._value == other._value
class DeckOfCards:
'''A Deck is a collection of cards'''
def __init__(self):
self._deck = [ Card(i) for i in range(52) ]
def __repr__(self):
return 'Deck : ', self._deck
def shuffle(self):
return random.shuffle(self._deck)
def deal_a_card(self, i=-1):
#that way player can choose where to draw from
return self._deck.pop(i)
def cards_left(self,count):
return len(self._deck)
new_deck = DeckOfCards()
Also, feel free to comment on anything you'd like, whether it be a design flaw or redundancy in code, literally anything. Thanks in advance!
You should return a string type, for example in Deck:
def __repr__(self):
...
return 'Deck : '+str(self._deck)
__repr__ ideally could return the representation of the object that you would use to create this instance.
From repr():
For many types, this function makes an attempt to return a string that would yield an object with the same value when passed to eval(), otherwise the representation is a string enclosed in angle brackets that contains the name of the type of the object together with additional information often including the name and address of the object.
First, It should be noted that you don't have to implement the __repr__ method. Python provides a somewhat reasonable default (it'll at least tell you the type).
If you want to implement __repr__, the "rule of thumb" is that where it makes sense, you should provide enough information about the object that a user could reconstruct it. In your case, there doesn't seem to be any real difference from one deck to another, so
def __repr__(self):
return 'Deck()'
might be a reasonable return value. This doesn't get the state right (after shuffling), but you don't provide an interface for constructing a deck in a particular state. If you did, it might look like:
def __repr__(self):
return 'Deck(%s)' % self._deck

Having trouble understanding order of evaluation in Python method call

Just wondering if anyone can help me understand this code a little better. Here is the program:
class Card(object):
""" A playing card. """
RANKS = ["A", "2", "3", "4", "5", "6", "7",
"8", "9", "10", "J", "Q", "K"]
SUITS = ["c", "d", "h", "s"]
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
def __str__(self):
rep = self.rank + self.suit
return rep
class Hand(object):
""" A hand of playing cards. """
def __init__(self):
self.cards = []
def __str__(self):
if self.cards:
rep = ""
for card in self.cards:
rep += str(card) + "\t"
else:
rep = "<empty>"
return rep
def clear(self):
self.cards = []
def add(self, card):
self.cards.append(card)
def give(self, card, other_hand):
self.cards.remove(card)
other_hand.add(card)
class Deck(Hand):
""" A deck of playing cards. """
def populate(self):
for suit in Card.SUITS:
for rank in Card.RANKS:
self.add(Card(rank, suit)) # <- HERE
def shuffle(self):
import random
random.shuffle(self.cards)
def deal(self, hands, per_hand = 1):
for rounds in range(per_hand):
for hand in hands:
if self.cards:
top_card = self.cards[0]
self.give(top_card, hand)
else:
print("Can't continue deal. Out of cards!")
# main
deck1 = Deck()
print("Created a new deck.")
print("Deck:")
print(deck1)
deck1.populate()
print("\nPopulated the deck.")
print("Deck:")
print(deck1)
The thing I'm wondering about is self.add(Card(rank, suit)) it's confusing me. Does Card(rank, suit) get sent to the add method straight away or does it get sent to the Card class first and then go to the add method? The add method has a cardparameter, what gets put into that parameter?
If I'm not making much sense it's because I've been staring at this for 2 hours.
Thanks
It's pretty simple.
Any time you call a class in Python, you create a new instance of the class. So if you call the Card class, you create a new instance of class Card... in other words, you create a new card.
Let's create the ace of spades:
ace_of_spades = Card('A', 's')
But I'm cheating. I looked and saw what strings to pass to Card when I should be using the pre-defined constants. Let's do it more properly:
ace_of_spades = Card(Card.RANKS[0], Card.SUITS[3])
Hmm, I'm not sure that Card.RANKS[0] is that easy to understand. Personally I would probably do something like
class Card(object):
RANK_ACE = 'A'
RANK_2 = '2'
# and so on
and then:
ace_of_spades = Card(Card.RANK_ACE, Card.SUIT_SPADES)
Now that we have used the variable name ace_of_spades to hold a reference to the card, we can use the card:
hand = Hand()
hand.add(ace_of_spades)
But if we just want to get the card into the hand, we don't need that variable name ace_of_spades. We could remove the name:
del(ace_of_spades)
But there is no need to use the name in the first place. We can simply do:
hand.add(Card(Card.RANK_ACE, Card.SUIT_SPADES))
This creates the card, then immediately passes the newly created card to Hand.add() to add it to a hand.
You don't, strictly speaking, need to store the ranks and suits inside the Card class. In Python, it might be more common to put these things outside of the classes, in "module" scope. I would most likely do:
RANK_ACE = 'A'
RANK_2 = '2'
# and so on
SUIT_CLUBS = 'c'
# and so on
class Card(object):
# and so on
But if you are taking a class and your teacher wants you to put constants inside your programs' classes, then do that.
EDIT: You are trying to figure out what self.add() does in the program.
Here's the source code for it:
# inside class Hand
def add(self, card):
self.cards.append(card)
self refers to the object. To see how this works, let's imagine you have an instance of class Hand, saved in the variable h. If you call h.add(ace_of_spades) then the self in this function will be set to the same instance of Hand that h refers to.
In fact, the call h.add(ace_of_spades) is exactly the same as doing this call:
Hand.add(h, ace_of_spades)
When you use the form h.add(), Python automatically does the same thing as the above call. Python looks at h and figures out that it is an instance of class Hand. Then Python looks in class Hand for a function called add(). Then Python builds a call similar to the one shown above.
self.cards refers to a list object stored inside the Hand instance. Python lists have a method function, .append(), that appends an item to the list.
You can read more about classes and method functions here: http://docs.python.org/2/tutorial/classes.html
Good question. You are definitely making sense :)
Here's what's happening in self.add(Card(rank, suit)): note that self.add(Card(rank, suit)) is being called from within a nested for loop. The rank and suit parameters are defined by those loops. You are right, Card(rank, suit) is evaluated first, then the returned card object is fed as a parameter to self.add method on the Deck. So, the combination of loops and method calls populates the deck with 52 card objects -- one of each suit and face. Does that make sense?
Q1: Does (Card(rank, suit)) get sent to the add method straight away or
does it get sent to the Card class first and then go to the add
method?
Here's what's happening in self.add(Card(rank, suit)):
Card(rank, suit) calls Card's __new__ and __init__ methods. __new__ creates a new Card object and the __init__ method initializes it to have the given rank an suit.
That new card is passed to Deck.add() method where it is appended to self.cards list (Note that Deck inherits the cards list and add method from Hand)
Here I've stripped the code down to only the necessary methods for reference:
class Card(object):
def __init__(self, rank, suit):
self.rank = rank
self.suit = suit
class Hand(object):
def __init__(self):
self.cards = []
def add(self, card):
self.cards.append(card)
class Deck(Hand):
def populate(self):
for suit in Card.SUITS:
for rank in Card.RANKS:
self.add(Card(rank, suit))
Q2: The add method has a 'card' parameter, what gets put into that
parameter?
The newly created Card as described above
If you add two print statements like these:
In Card class:
def __init__(self, rank, suit):
print "card.init: {} {}".format(rank, suit)
self.rank = rank
self.suit = suit
In Hand:
def add(self, card):
print "add: {}".format(card)
self.cards.append(card)
You will see this output:
Created a new deck.
Deck:
<empty>
card.init: A c
add: Ac
card.init: 2 c
add: 2c
card.init: 3 c
add: 3c

Managing Instances in Python

I am new to Python and this is my first time asking a stackOverflow question, but a long time reader. I am working on a simple card based game but am having trouble managing instances of my Hand class. If you look below you can see that the hand class is a simple container for cards(which are just int values) and each Player class contains a hand class. However, whenever I create multiple instances of my Player class they all seem to manipulate a single instance of the Hand class. From my experience in C and Java it seems that I am somehow making my Hand class static. If anyone could help with this problem I would appreciate it greatly.
Thank you,
Thad
To clarify: An example of this situation would be
p = player.Player()
p1 = player.Player()
p.recieveCard(15)
p1.recieveCard(21)
p.viewHand()
which would result in:
[15,21]
even though only one card was added to p
Hand class:
class Hand:
index = 0
cards = [] #Collections of cards
#Constructor
def __init__(self):
self.index
self.cards
def addCard(self, card):
"""Adds a card to current hand"""
self.cards.append(card)
return card
def discardCard(self, card):
"""Discards a card from current hand"""
self.cards.remove(card)
return card
def viewCards(self):
"""Returns a collection of cards"""
return self.cards
def fold(self):
"""Folds the current hand"""
temp = self.cards
self.cards = []
return temp
Player Class
import hand
class Player:
name = ""
position = 0
chips = 0
dealer = 0
pHand = []
def __init__ (self, nm, pos, buyIn, deal):
self.name = nm
self.position = pos
self.chips = buyIn
self.dealer = deal
self.pHand = hand.Hand()
return
def recieveCard(self, card):
"""Recieve card from the dealer"""
self.pHand.addCard(card)
return card
def discardCard(self, card):
"""Throw away a card"""
self.pHand.discardCard(card)
return card
def viewHand(self):
"""View the players hand"""
return self.pHand.viewCards()
def getChips(self):
"""Get the number of chips the player currently holds"""
return self.chips
def setChips(self, chip):
"""Sets the number of chips the player holds"""
self.chips = chip
return
def makeDealer(self):
"""Makes this player the dealer"""
self.dealer = 1
return
def notDealer(self):
"""Makes this player not the dealer"""
self.dealer = 0
return
def isDealer(self):
"""Returns flag wether this player is the dealer"""
return self.dealer
def getPosition(self):
"""Returns position of the player"""
return self.position
def getName(self):
"""Returns name of the player"""
return self.name
From my experience in C and Java it seems that I am somehow making my Hand class static.
Actually, that is basically what you're doing. Well, not really making the class static, but making the variable static.
When you write declarations like this:
class Hand:
cards = []
that variable (cards) is associated with the class, not with the instance. To make an analogy to Java, every statement in a Python class that isn't part of a method of that class basically runs in a static initializer. You could almost think of it like this:
class Hand {
static {
cards = new object[];
}
}
(merely a rough analogy, of course)
To create an instance variable in Python, you have to set it as an attribute of the instance, which requires you to wait until you have a reference to the instance. In practice, this means you initialize it in the constructor, like so:
class Hand:
def __init__(self):
self.cards = []
Your problem is quite simple
if you assign lists to the body of python classes, when you append items to it, they will be store at Class level, not at instance level.
you can solve this problem by adding the line:
def __init__(self):
self.cards = []
this is a very known case of python pitfall, and I recommend you the reading:
http://zephyrfalcon.org/labs/python_pitfalls.html
As other answers noted, you were confused about class variables vs. instance variables. I suggest you review the basics of how Python classes work. Here is an answer I wrote for another question; reading this might help you.
How to define a class in Python

Categories

Resources