1

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
0

2 Answers 2

4

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)
Sign up to request clarification or add additional context in comments.

4 Comments

so for all a: __init__(a): self.a=a are only checked and used during initiation thanks for the help :)
@Jared the @property decorator just saves time & effort by, among other things, automatically giving me set and get commands, is that right? One could achieve the same by defining aspects of property by hand? Thanks!
@patrick: The @property decorator makes the method it decorates into a getter. Users of the class will be able to access number as if it were an attribute. To make a setter, you decorate the setter method with @attribute_name.setter (in this case it would be @number.setter).
There's some info about how @property is implemented here.
2

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.