Проблема симуляции орбиты Pygame

Недавно я делал симулятор орбиты, используя это уравнение: Изображение уравнения, которое я использую

Вот мой код:

import pygame, math
from pygame.locals import *
from random import randint
pygame.init()
screen = pygame.display.set_mode([500,500])
clock = pygame.time.Clock()

class Planet():
    def __init__(self, vel = [1, 1], mass = 100000, pos = [100, 100], pathLength = 100000):
        self.v = vel
        self.m = mass
        self.size = mass/1000000
        self.pos = pos
        self.pL = pathLength
        self.path = [[pos[0], pos[1]]]

    def update(self):
        self.pos[0] += self.v[0]
        self.pos[1] += self.v[1]
        self.path.append([self.pos[0], self.pos[1]])
        if len(self.path) == self.pL:
            self.path.pop(0)

class World():
    def __init__(self, planetList, iterations, mass = 10000000, gravityConstant = (6 * 10 ** -9)):
        self.plnt = planetList
        self.iter = iterations
        self.mass = mass
        self.size = int(mass/1000000)
        self.gC = gravityConstant
    def draw(self):
        pygame.draw.circle(screen, [0, 0, 0], [250, 250], self.size)
        for p in self.plnt:
            pygame.draw.rect(screen, [0, 0, 0], [p.pos[0], p.pos[1], p.size, p.size])
            pygame.draw.lines(screen, [0, 0, 0], False, p.path)
    def update(self):
        for i in range(self.iter):
            for p in self.plnt:
                d = math.sqrt((p.pos[0] - 250) ** 2 + (p.pos[1] - 250) ** 2)
                f = (self.gC * self.mass * p.m)/(d ** 2)
                vect = [((250 - p.pos[0]) / d) * f, ((250 - p.pos[1]) / d) * f]
                p.v[0] += vect[0]
                p.v[1] += vect[1]
                p.update()
        self.draw()


a = Planet([4,0])
b = Planet([4, 0])
w = World([b], 100)
while 1:
    screen.fill([255, 255, 255])

    w.update()

    for event in pygame.event.get():
        if event.type == QUIT:
            pygame.quit()

    pygame.display.update()
    clock.tick(60)

Если у меня есть только 1 планета в симуляции, она работает как положено, но с этим у нее есть проблемы

a = Planet([4,0])
b = Planet([4, 0])
w = World([a, b], 100)

Планеты вылетают с экрана и продолжают вечно, я не вижу, где я допустил ошибку.

1 ответ

Решение

Вы попали в давнюю ловушку Python объявления изменяемых аргументов по умолчанию.:)

Чтобы продолжить, чтобы ваш код работал, скопируйте сделанные мной замены ниже в свой собственный код:

class Planet():
    def __init__(self, vel = [1, 1], mass = 100000, pos = [100, 100], pathLength = 100000):
        self.v = vel[:]  # Added [:] to ensure the list is copied
        self.m = mass
        self.size = mass/1000000
        self.pos = pos[:]  # Added [:] here for the same reason
        self.pL = pathLength
        self.path = [[pos[0], pos[1]]]

объяснение

В Python списки изменчивы - вы можете изменить один и тот же экземпляр списка. Одна распространенная ошибка, которую допускают люди при использовании Python, - это объявление изменяемых аргументов в качестве значений по умолчанию в сигнатурах функций.

Проблема состоит в том, что Python назначит это значение по умолчанию один раз параметру во время обработки определения функции, а затем повторно использует это назначенное значение каждый раз, когда функция вызывается и вызывается аргумент по умолчанию.

В вашем Planet конструктор класса, вы объявляете два изменяемых аргумента по умолчанию:

  • vel = [1, 1]
  • pos = [100, 100]

Каждый экземпляр Planet вы создадите, будет хранить ссылку на эти списки, но учтите, что из-за того, что я сказал выше, каждая планета будет иметь одинаковые vel список и то же pos список. Это означает, что каждый экземпляр будет мешать данным о скорости и местоположении других.

Вы можете прочитать больше об этой ошибке здесь.

Альтернативным и предпочтительным способом обработки подобных ситуаций было бы установить значение по умолчанию как None и затем назначьте "реальное" значение по умолчанию, если вызывающая сторона не предоставляет для него явного значения:

class Planet():
    def __init__(self, vel = None, mass = 100000, pos = None, pathLength = 100000):
        self.v = vel or [1, 1]
        self.m = mass
        self.size = mass/1000000
        self.pos = pos or [100, 100]
        self.pL = pathLength
        self.path = [[self.pos[0], self.pos[1]]]

Затем можно было бы ожидать документирования такого поведения функции, так как в противном случае оно не будет очевидно для вызывающей стороны.

Другие вопросы по тегам