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)

3
  • 1
    cards.extend(cards[:]) makes a copy of cards, not the items inside cards. i.e. it is a shallow copy. Commented Sep 24, 2020 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. Commented Sep 24, 2020 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? Commented Sep 25, 2020 at 1:51

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

4 Comments

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.
@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.
I'll accept your answer and make a new one for my other issue. Could you fix the grammar of the last 2 sentences?
@AllanFelipe My native language isn't English.
-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

2 Comments

The caching of integers is totally irrelevant here. It is an implementation detail, anyway, that doesn't affect any of the semantics of what the OP is doing.
Yes, I see that now. Looks like I didn't read the question closely enough.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.