class attribute doesnt work properly (python) - 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.

Related

Assistance needed with variables in classes

I'm having some trouble with variables in classes.
Say I have this class:
some_number = 200
class Numbers:
def __init__(self):
self.number = some_number
def edit(self):
self.number = self.number - 50
def printNumber(self):
print(self.number)
If I run this in the shell:
number = Numbers()
number.edit()
number.printNumber()
This prints the number 150
But this:
print(some_number)
Prints the number 200. How do I get the variable some_number to change in a class, after which printing it returns the edited value?
The variable some_number is outside of the class, therefore it wouldn't change. The easiest way would be to avoid classes and modify the variable some_number. The alternative would be to drop some_number altogether and do the following:
class Numbers:
def __init__(self, number):
self.number = number
def edit(self):
self.number = self.number - 50
def printNumber(self):
print(self.number)
That would be called like this:
number = Numbers(150)
number.edit()
number.printNumber()
There are many ways to edit that variable, but these are two potential methods.
As a caution, global variables can get tricky, but declaring it globally would work as well.
You can access and alter some_number from the global scope by
class Numbers:
def edit(self):
global some_number
some_number = some_number - 50
def printNumber(self):
print(some_number)
However, in general, the need to modify global variables usually points to a design problem ...
You declared the variable some_number as a global variable. The whole point of adding the attribute .number to the Numbers class would be for it to be specific to each instance of the class.
You can change some_number as a side effect, but why would you want to?
Here's how you would if you have a good reason:
some_number = 200
class Numbers:
def __init__(self):
self.number = some_number
def edit(self):
global some_number
self.number = self.number - 50
some_number = self.number
def printNumber(self):
print(self.number)
n = Numbers()
n.edit()
n.printNumber()
print(some_number)
More likely though, you'd want something like this:
some_number = 200
class Number:
def __init__(self, number):
self.number = number
def edit(self, change):
self.number = self.number + change
def __str__(self):
return str(self.number)
n = Number(some_number)
n.edit(-50)
print(n)
print(some_number)
It's generally a really bad idea to keep stuff around in global variables, unless there's a really good reason to have one value available to all of your program, with changes to that value affecting the entire program. And even in those cases, it's often better to explicitly pass the value around, instead of just referring to it, betting it has the correct value.
In the case of a class, if you do have a good reason, you should consider a class attribute instead of a global variable, which will keep the one value the same for all instances of the class, but at least there are no global side effects.

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.

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

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

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