0

Follow the book i have a confused

the book says delete the bullet in bullets copy instead of the group it self

but i try for both two effect ,it really the same??

so what is the difference between these two codes??

for bullet in bullets.copy():   
     if bullet.rect.y <= 0:
        bullets.remove(bullet)

==========================================

for bullet in bullets():   
         if bullet.rect.y <= 0:
            bullets.remove(bullet)

==========================================

here's the whole code:

    #! /usr/bin/python
    import pygame as p
    import sys

    class Setting():
        def __init__(self,width,height):
            self.w=width
            self.h=height
            self.flag=p.RESIZABLE
            self.color=(255,255,255)
            self.speed=1
            self.screen=p.display.set_mode((self.w,self.h),self.flag)
            p.display.set_caption("Bullet")
            self.bullet_s=1
            self.bullet_w=5
            self.bullet_h=30
            self.bullet_c=(0,0,0)

    class Bullet(p.sprite.Sprite):
        def __init__(self,setting):
            super().__init__()
            self.screen_rect=setting.screen.get_rect()
            self.screen_center=self.screen_rect.center

            self.rect=p.Rect((0,0),(setting.bullet_w,setting.bullet_h))

            self.rect.center=self.screen_center
            self.rect.bottom=self.screen_rect.bottom
            self.color=setting.bullet_c
            self.speed=setting.bullet_s

            self.y=float(self.rect.centery)
        def bullet_check(self,bullets,setting):
            for event in p.event.get():
                if event.type == p.QUIT:
                    sys.exit()
                elif event.type == p.KEYDOWN:
                    if event.key ==p.K_SPACE:
                        bullets.add(Bullet(setting))
        def move(self):
            self.y -= self.speed
            self.rect.y=self.y
        def draw(self,setting):
            p.draw.rect(setting.screen,self.color,self.rect)
        def bullet_blit(self,bullets,setting):
            for bullet in bullets.sprites():
                bullet.draw(setting)
                bullet.move()
            for bullet in bullets.copy():     **<-- for bullet in bullets:  really same effect**
                if bullet.rect.y <= 0:
                   bullets.remove(bullet)
            print(len(bullets))
def game():
    p.init()
    setting=Setting(1200,800)
    bullet=Bullet(setting)
    bullets=p.sprite.Group()    
    while True:
        bullet.bullet_check(bullets,setting)
        setting.screen.fill((255,0,0))
        bullet.bullet_blit(bullets,setting)
        p.display.flip()
game()
M_Sea
  • 389
  • 4
  • 13
  • 2
    The `.copy()` is to work around the [removing items while iterating](https://stackoverflow.com/questions/1207406/how-to-remove-items-from-a-list-while-iterating) issue. – Selcuk Nov 25 '19 at 23:32
  • in Python instead removing item from list we create list with elements which should stay and later we replace lists. – furas Nov 26 '19 at 00:44
  • problem with removing is when you have two or more elements to remove from list and some elements are next to each other. When you delete first element then other elements moves and `for` skips second element - so finally it doesn't remove second element so it doesn't work as expected. – furas Nov 26 '19 at 00:46

1 Answers1

1

Problem is when you have more elements to remove and they are next to each other.

When you delete first element then other elements moves and for skips second element - so finally it doesn't remove all elements.

This works correctly - it removes all elements and you get empty list []

data = ['a', 'a']

for x in data.copy():
    if x == 'a':
        data.remove(x)

print(data)

This doesn't works - it skip some elements and you get list with ['a']

data = ['a', 'a']

for x in data:
    if x == 'a':
        data.remove(x)

print(data)

In python is very popular to create new list with elements which you want to keep

data = ['a', 'a', 'b', 'a', 'a']

result = []

for x in data:
    if x != 'a':
        result.append(x)

data = result

print(data)

But it can be done shorter with list comprehension

data = ['a', 'a', 'b', 'a', 'a']

data = [x for x in data if x != 'a']

print(data)
furas
  • 134,197
  • 12
  • 106
  • 148
  • I got it !!! really good example, if there are same element in list ,remove one then the next one move the the first site, so the `for` skip the second element,but if i set `data=['a','a','b','a']` not use the copy list to remove finally `print(data) >>['b','a']` ,for my understanding the final result is `['a','b']`, why????? – M_Sea Nov 26 '19 at 12:51
  • 1
    `remove("a")` always removes first `'a'` on list - so it removes different `'a'` then it checkes in `if` :) – furas Nov 26 '19 at 13:19
  • is that mean if `data=['a','a','b','a']` the match element is data[0] and data[3],after fisrt `data.remove(a) >> data =['a','b','a']` after match second a -->data[3], then `data.remove(a)` remove the first a in the list, iteration finished, is it right? – M_Sea Nov 26 '19 at 13:25
  • 1
    yes, `remove()` works this way. It doesn't get index of this element but its value so it has to find it on list and it finds first element instead of third. – furas Nov 26 '19 at 14:27