0

I create a class Card, with attributes suitList and number. When I change both of them, I expect it will not affect different objects. However, the result is confusing. Why change suitList will affect other objects' suitList? Please explain that.

class Card:
  suitList = ["CLubs"]
  number = 1

  def __init__ (self, rank = 0):
      self.rank = rank

  def __str__ (self):
      return (self.suitList[self.rank] + "  " + str(self.number) + ";\n")
c1 = Card()
c2 = Card()
print c1
c1.suitList[0] = "Heart"
c1.number = 3
print c1
print c2
c3 = Card()
print c3
Peterxwl
  • 1,023
  • 3
  • 16
  • 24

3 Answers3

3

That is because you have made suitList a class attribute, which is shared among all instances of the class. If you want a unique suitList list per instance, you should be making it an instance attribute:

def __init__(self, rank = 0):
     self.rank = rank
     self.suitList = ["CLubs"]

You may also want to move the definition of number into the __init__ method so that it too becomes an instance attribute:

def __init__(self, rank = 0):
     self.rank = rank
     self.suitList = ["CLubs"]
     self.number = 1

For more information on this, see Python: Difference between class and instance attributes


Also, you will notice that I prefixed all the names with self. You need to do this so that the values are made attributes of the class. Had we done this instead:

def __init__(self, rank = 0):
     self.rank = rank
     suitList = ["CLubs"]
     number = 1

suitList and number would be made local to the __init__ method.

Community
  • 1
  • 1
  • Is number the class attribute? – Peterxwl Jan 08 '15 at 02:04
  • Yes, `number` is also a class attribute and will be shared among all instances. If you want it to be unique per instance, move its definition inside `__init__`. –  Jan 08 '15 at 02:05
  • Okey, you say number is also attribute and shared by all instances. In my example, I changed number's value to 3, and expect all instances have number's value as 3. However, only c1's number changes. Can you explain why? – Peterxwl Jan 08 '15 at 02:08
  • Because when you did `c1.number = 3`, you created a *new* instance attribute on `c1` named `number` and set its value to `3`. The `number` class attribute however was overshadowed. –  Jan 08 '15 at 02:12
  • Pardon me but I am still a little confused. number and suitList are both class attributes which should have same behavior, right? You say changing c1.suitList[0] changes all other instances' suitList, while changing c1.number doesn't, with your explanation that the number attribute was overshadowed. Do your words mean that such behavior relates to not only the attribute is class attribute or not, but also the attribute's type itself? – Peterxwl Jan 08 '15 at 02:22
  • No problem; I'll try to be more clear. When you do `c1.suitList[0] = "Heart"`, you are modifying the list referenced by `suitList` by setting its first item to the value of `"Heart"`. Moreover, this change is reflected across all instances of the class because `suitList` is a class attribute. When you do `c1.number = 3` however, you create a new instance attribute on the instance `c1`. The class attribute `number` on that specific instance is overshadowed by the new instance attribute. So, `c1.number` is now an instance attribute while `c2.number` is still the class attribute. –  Jan 08 '15 at 02:27
1

Those 2 properties are class properties or static variables. If you want to declare non static properties, you have to precede the name of the variables with self, which refers to the current instance of the class you are working with:

class MyClass:
    def __init__(self):
        self.suitList = ["CLubs"]
        self.number = 1

For more information on what is self and the difference between static and non static class variables:

  1. Python __init__ and self what do they do?

  2. Python 'self' keyword

  3. Explaining the python 'self' variable to a beginner

  4. What is the purpose of self?

  5. https://stackoverflow.com/questions/3332454/oop-python-oriented-tutorials

  6. Python: Difference between class and instance attributes

Community
  • 1
  • 1
nbro
  • 15,395
  • 32
  • 113
  • 196
1

As pointed out, you are confusing class attribute to instance attributes.

In you exemple, suitList and number are class attribute, they are shared among all the instance(c1, c2, c3) When you change a class attribute as in c1.suitList[0] = "Heart", it will reflect in all class instances.

To fix this you have some options: I give you two.

1) Use only instance attributes:

class Card:
    def __init__(self,number=0,suit="CLubs"):
        self.number = number
        self.suit = suit

    def __str__(self):
        return "%s %d"%(self.suit,self.number)

c1 = Card()
c2 = Card()
c1.suit = "Heart"
c1.number = 3
print c1
print c2

In this case, there's no class attribute, to change a card suite you assign it direcly using c1.suit.

2) Use a mix of class/attribute:

class Card:
    suitList = ["CLubs", "Heart"]
    def __init__(self,number=0,rank=0):
        self.rank = rank
        self.number = number

    def __str__(self):
        return (Card.suitList[self.rank] + "  " + str(self.number))

c1 = Card()
c2 = Card()
c1.rank = 1
c1.number = 3
print c1
print c2

Rank in this case is a index that looks in the suitList. To change the Suit of card, you change it's rank, not the suitList.

Both exemple output:

Heart 3
CLubs 0
Community
  • 1
  • 1
f.rodrigues
  • 3,499
  • 6
  • 26
  • 62