2

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)

Allan Felipe
  • 201
  • 2
  • 8
  • 1
    `cards.extend(cards[:])` makes a copy of *`cards`*, not the items inside `cards`. i.e. it is a shallow copy. – juanpa.arrivillaga Sep 24 '20 at 22:51
  • As to your other example, `pg.Rect(self.position, 0, 50, 100)` has no idea that `self.position` is some attribute of some class, it just evaluates to whatever value and that value gets passed to `pg.Rect`. Just because you update `self.position = something_else` isn't going to affect anything in `pg.Rect`. – juanpa.arrivillaga Sep 24 '20 at 22:53
  • Tks for the answer. But I still don`t understand the difference between both lines. What I meant is that the first option actually does what it's supposed to do, but the second one (commented) doesn't. About the first issue, so because of the way "extend" works, both cases (with and without [:]) behave in exactly the same way? – Allan Felipe Sep 25 '20 at 01:51

2 Answers2

2

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.

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Hi, tks, and extend(cards) as well as extend(cards[:]) would work the same way you described, pointing to the same first 8 objects? I am used to the idea that [:] creates a new list, that's why I thought it would work. – Allan Felipe Sep 25 '20 at 07:23
  • @AllanFelipe Of course it work the same way. It creates a shallow copy. The list contains references to the Objects. The list is copied, but the objects are the same. – Rabbid76 Sep 25 '20 at 07:31
  • I'll accept your answer and make a new one for my other issue. Could you fix the grammar of the last 2 sentences? – Allan Felipe Sep 28 '20 at 07:13
  • @AllanFelipe My native language isn't English. – Rabbid76 Sep 28 '20 at 07:21
-1

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