Обновления элементов списка применяются ко всему списку python

#python #pygame #sprite

Вопрос:

Я пытаюсь написать простую программу для моделирования системы N-Body с использованием python (и pygame для графического интерфейса только потому, что у меня больше опыта, чем у tk). Проблема, с которой я сталкиваюсь, заключается в том, что я создал класс тела, который является подклассом из спрайта. У меня есть адаптируемый размер списка n, который добавляет в список столько тел. Для начала я просто решил, что обновления будут выполняться в цикле double for с последним циклом for, который передает обновления после их вычисления (я знаю, что это неэффективно, не в этом дело). Моя проблема заключается в том, что, когда я передаю обновление элементу этого списка, обновление применяется ко всем элементам в этом списке. Что здесь происходит? Почему эти совершенно отдельные элементы связаны друг с другом? Необходимый код ниже

Класс кузова

 class Body(Sprite):
    vel = Vector2(0,0)
    acc = Vector2(0,0)
    def __init__(self, x, y, size, mass, init_val = True, color=(255,255,0)):
        super().__init__()
        self.rect = Rect(x-size[0]/2,y-size[1]/2,size[0],size[1])
        self.mass = mass
        self.pos = Vector2(x,y)
        #self.vel = Vector2(random(),random())
        if init_val:
            self.acc = Vector2(random(),random())
        self.image = Surface(size)
        self.image.fill((255,255,255))
        self.image.set_colorkey((255,255,255))
        draw_circle(self.image,color,(size[0]/2,size[1]/2),size[0]/2)

    def update(self, force):
        self.acc  = force
        self.vel  = self.acc
        self.rect.move_ip(self.vel)
 

Цикл Sim-карты

 class nbody:
    def __init__(self, n = 3, screensize=(1366,768)):
        self.win = pg.display.set_mode(screensize)
        self.clock = pg.time.Clock()
        self.bodies = Group()
        sareax = [int(screensize[0]/4), int(screensize[0]*3/4)]
        sareay = [int(screensize[1]/4), int(screensize[1]*3/4)]
        c = [(255,0,0), (0,255,0), (0,0,255)]
        self.bodies = []
        for i in range(n):
            mass = 5   random()*5
            size = [mass*2]*2
            self.bodies.append(Body(randint(sareax[0], sareax[1]),randint(sareay[0],sareay[1]),size,mass, init_val=False, color=c[i]))
    
    def run_sim(self, time=None, fps = 0.5):
        run = True
        while run:
            self.clock.tick(fps)
            self.win.fill((0,0,0))
            for e in pg.event.get():
                if e.type == pg.QUIT:
                    pg.quit()
                    quit()
                elif e.type == pg.KEYDOWN:
                    if e.key == pg.K_ESCAPE:
                        run = False
                elif e.type == pg.KEYUP:
                    NotImplemented
            updates = []
            for b1,i in zip(self.bodies,range(len(self.bodies))):
                df = Vector2(0,0)
                for b2,j in zip(self.bodies,range(len(self.bodies))):
                    if i != j:
                        temp = newton(GRAV_CONSTANT, b1, b2)
                        mag = Vector2(b2.rect.centerx-b1.rect.centerx, b2.rect.centery-b1.rect.centery)
                        mag.scale_to_length(temp)
                        df  = temp*mag
                        #print("Body {} has force {} to body {}".format(i,temp*mag,j))
                        if pg.sprite.collide_mask(b1,b2):
                            b1.kill()
                            b2.kill()
                updates.append(df)
            print(updates)
            for bnum,up in zip(range(len(updates)),updates):
                print("Before:", self.bodies[bnum].rect.center, self.bodies[bnum].vel, self.bodies[bnum].acc)
                self.bodies[bnum].update(up)
                print("Change:", up)
                print("After:", self.bodies[bnum].rect.center, self.bodies[bnum].vel, self.bodies[bnum].acc)
                pg.draw.line(self.win, (255,255,0), self.bodies[bnum].rect.center, (20*up.normalize()) self.bodies[bnum].rect.center, width=2)
                self.win.blit(self.bodies[bnum].image,self.bodies[bnum].rect)
                
            pg.display.flip()
        
        return
 

Комментарии:

1. vel и acc должны быть атрибутами экземпляра вместо атрибутов класса.

Ответ №1:

См. Раздел Переменные класса и экземпляра:

Вообще говоря, переменные экземпляра предназначены для данных, уникальных для каждого экземпляра, а переменные класса-для атрибутов и методов, общих для всех экземпляров класса.

Поскольку val и acc являются атрибутами класса, они являются общими для всех Body объектов, и, как вы упомянули в своем вопросе: «эти совершенно отдельные элементы связаны вместе».

vel и acc должны быть атрибутом экземпляра вместо атрибутов класса:

 class Body(Sprite):
    def __init__(self, x, y, size, mass, init_val = True, color=(255,255,0)):
        super().__init__()
        self.rect = Rect(x-size[0]/2,y-size[1]/2,size[0],size[1])
        self.mass = mass
        self.pos = Vector2(x,y)
       
        self.vel = Vector2(0,0)
        self.acc = Vector2(0,0)
        if init_val:
            self.acc = Vector2(random(),random())

        self.image = Surface(size)
        self.image.fill((255,255,255))
        self.image.set_colorkey((255,255,255))
        draw_circle(self.image,color,(size[0]/2,size[1]/2),size[0]/2)
 

Комментарии:

1. Ах, я не знал, что атрибуты класса-это вещь. Решил, что это то же самое, что и c , где вы объявляете переменные там. Спасибо.