Allan Felipe
Allan Felipe

Reputation: 201

Copy/reference issue with Python class instance

I have the following class of a card:

class Card:
    def __init__(self, number, position):
        self.number = number
        self.position = position * 50
        self.rect = pg.Rect(self.position, 0, 50, 100)
    def draw(self, surface):
        pg.draw.rect(surface, green, (self.position, 0, 50, 100))
        ## pg.draw.rect(surface, green, self.rect)

And I'd like to create 16 different cards side by side, from 0 to 800, each one with width=50. Their numbers would be 0 to 7 and then 0 to 7 again. I already came up with a solution for this problem, which is:

cards = []
for n in range(16):
    cards.append(Card(n%8, n))

However, broken solutions that appeared along the way left me with unanswered questions. For example:

cards = [Card(n, 0) for n in range(8)] * 2
for n in range(16):
    cards[n].position = n * 50

Here, I realized that cards[0] is cards[8] returns True. The result is that only the last half of the cards appears on screen. Apparently that is the same thing that happens in:

cards = [Card(n, 0) for n in range(8)]
cards.extend(cards)

But I thought changing the second line to:

cards.extend(cards[:])

would solve the issue making a copy and not a reference, but it still doesn't work. That is something I wanted an explanation. Is there an easy way to fix this copy vs. reference issue in the previous 2 cases?

One other issue is the commented version of the draw method. In some of the broken methods I used, this version doesn't work because it never updates the position, which is always stuck in 0. It seems that outside the class, card.rect[0] doesn't receive the value of card.position, although I can use self.position directly inside the draw method. What's going on with the self.rect here?

EDIT

My second issue is solved here: How do you change the value of one attribute by changing the value of another? (dependent attributes)

Upvotes: 2

Views: 78

Answers (2)

Rabbid76
Rabbid76

Reputation: 210968

The loop

cards = []
for n in range(16):
   cards.append(Card(n%8, n))

runs 16 times. Hence there are constructed 16 Card objects and append to a list. Finally you get 16 separate instances of the class Card and a list with 16 elements.

But the expression

cards  = [Card(n, 0) for n in range(8)] * 2

does not create 16 Card objects. It constructs 8 Card objects and a list with 16 elements, where each object is contained twice. Card (n, 0) is only invoked 8 times, so there can only be 8 instances of Card.

The same applies to

cards.extend(cards[:])

[:] creates a shallow copy of a list. The list just contains references to the objects. The list respectively the references to the objects are copied, but the objects themselves are not copied, but no new Card object is created.

Upvotes: 2

Connor Ferster
Connor Ferster

Reputation: 311

This relates to how Python stores integers. The "common" integers from -5 to 255 are already stored in memory. When you assign a variable like a = 1, you are creating a pointer to the "hard coded" integer 1 already in memory, not instantiating a new integer. Any time you create an integer within this range, it will not be a new number so this will work:

>>> a = 7
>>> b = 7
>>> a is b
True

Whereas for a number outside of this range:

>>> a = 1234
>>> b = 1234
>>> a is b
False

Thus:

>>> a = [1, 2, 3]
>>> b = [1, 2, 3]
>>> a[0] is b[0]
True

>>> a is b
False

Upvotes: -1

Related Questions