Python, импорт переменных между файлами - PullRequest
1 голос
/ 24 марта 2020

Преамбула: я чувствую, что, возможно, я потратил столько времени на простую ситуацию ...

Теперь я делаю игру с Pygame, и в какой-то момент я хотел разделить файлы на два, а именно main.py и configurations.py, чтобы сделать его более читабельным.

Все шло хорошо, пока я не столкнулся с этой проблемой.

Я поделюсь всем код внизу, но я хочу сначала подвести итог:

Теперь, во-первых, в main.py, я импортирую по,

from configurations import *

сейчас, игра l oop на main.py зависит от переменной running от

while running:
    .......
    .......
    .......

А переменная running инициализируется в configurations.py с помощью

# initialize some variables
running = True

Итак, main.py должен получать переменную running, поскольку он не выдает никакой ошибки и использует ее в операторе while running.

В главном l oop есть раздел, где Я проверяю события следующим образом:

for event in pygame.event.get():
        # check for closing window
        if event.type == pygame.QUIT:
            running = False

Эта часть работает так же, как и ожидалось, она изменяет работу переменной, и программа выходит из строя в то время, как l oop.

* 1 038 * Теперь здесь возникает проблематичная c часть.

В одном из классов (класс Player) есть метод как decrease_HP,

def decrease_HP(self):
        self.HP -= 1
        print("-1 HP", "Current HP:", self.HP)
        if self.HP <= 0:
            running = False

Однако, точка Я не мог понять, что он не изменяет переменную бега должным образом, и игра никогда не останавливается (выходит из режима l oop). Вот один пример вывода, который показывает проблему.

pygame 1.9.6
Hello from the pygame community. https://www.pygame.org/contribute.html
-1 HP Current HP: 2
-1 HP Current HP: 1
-1 HP Current HP: 0
-1 HP Current HP: -1
-1 HP Current HP: -2
-1 HP Current HP: -3
-1 HP Current HP: -4
-1 HP Current HP: -5
-1 HP Current HP: -6

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

Кстати, я попытался добавить global running над оператором running = False в функции Player.decrease_HP.

Заранее спасибо .


Точные коды в файлах

main.py

# Pygame template - skeleton for a new pygame project
from configurations import *

# initiate some variables
max_bullet = 10


# initialize pygame and create window

pygame.init()
pygame.mixer.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("CORONA RACE")
clock = pygame.time.Clock()

player = Player()
all_sprites.add(player)


# initialize some variables
running = True

# have to use this because, otherwise, for the first SPACE key pressing, the newest_bullet is not defined yet.
newest_bullet = Bullet(0, 0)

# Game loop

while running:
    # keep loop running at the right speed
    clock.tick(FPS)
    # Process input (events)
    for event in pygame.event.get():
        # check for closing window
        if event.type == pygame.QUIT:
            running = False
        else:
            pass

    while len(mobs) != 5:
        m = Mob()
        all_sprites.add(m)
        mobs.add(m)

    keystate = pygame.key.get_pressed()
    player.speedx = 0
    if keystate[pygame.K_RIGHT]:
        player.speedx += player.SPEED
    if keystate[pygame.K_LEFT]:
        player.speedx -= player.SPEED
    if keystate[pygame.K_SPACE] and player.rect.top - newest_bullet.rect.bottom > BULLET_H + MARGIN and not len(bullets) >= max_bullet:
        newest_bullet = player.shoot()
    # BULLET_H refers to height of the bullet and margin refers to the minimum allowable margin between two consequent b
    # If there are more than 10 bullets at a time on the screen, then no more new bullets can be fired.
    if keystate[pygame.K_ESCAPE]:
        running = False
    if random.randint(0, 14530) > 14470:
        power_up = PowerUp()
        all_sprites.add(power_up)
        powerups.add(power_up)

    hits = pygame.sprite.spritecollide(player, powerups, True)
    for pu in hits:
        power_up_funcs[pu.type](player)

    hits = pygame.sprite.groupcollide(mobs, bullets, True, True)
    for m in hits:
        pass

    hits = pygame.sprite.spritecollide(player, mobs, True)
    if hits:
        player.decrease_HP()

    # print(player.HP)

    # Update

    all_sprites.update()

    # Draw / render
    screen.fill(WHITE)
    all_sprites.draw(screen)
    # *after* drawing everything, flip the display
    pygame.display.flip()

pygame.quit()
raise SystemExit  # to exit python

configurations.py

import pygame
import random

# define constants
WIDTH = 600
HEIGHT = 960
FPS = 30
BULLET_H = 24
BULLET_W = 8
POWERUP_H = 30
POWERUP_W = 30
MOB_W = 50
MOB_H = 80
MARGIN = 10

# define colors
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
MAGENTA = (255, 0, 255)
CYAN = (0, 255, 255)

# create sprite groups
all_sprites = pygame.sprite.Group()
bullets = pygame.sprite.Group()
powerups = pygame.sprite.Group()
mobs = pygame.sprite.Group()


# initialize some variables
running = True


# player sprite


class Player(pygame.sprite.Sprite):

    SPEED = 15

    def __init__(self):
        super().__init__()
        # pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((100, 150))
        self.image.fill(CYAN)
        pygame.draw.circle(self.image, RED, (50, 75), 15, 5)
        self.rect = self.image.get_rect()
        self.rect.centerx = WIDTH / 2
        self.rect.bottom = HEIGHT - 5
        self.speedx = 0
        self.HP = 3

    def update(self):
        self.rect.x += self.speedx
        if self.rect.right > WIDTH:
            self.rect.right = WIDTH
        if self.rect.left < 0:
            self.rect.left = 0

    def shoot(self):
        bullet = Bullet(self.rect.centerx, self.rect.top)
        all_sprites.add(bullet)
        bullets.add(bullet)
        return bullet  # I need this to set the margin in continious fire.

    def change_color(self):
        pass

    def increase_HP(self):
        if self.HP <= 2:
            self.HP += 1
            print("+1 HP", "Current HP:", self.HP)
        else:
            print("HP IS ALREADY FULL", "Current HP:", self.HP)

    def decrease_HP(self):
        self.HP -= 1
        print("-1 HP", "Current HP:", self.HP)
        if self.HP <= 0:
            running = False


class Mob(pygame.sprite.Sprite):
    def __init__(self):
        super().__init__()
        self.image = pygame.Surface((MOB_W, MOB_H))
        self.image.fill(MAGENTA)
        self.rect = self.image.get_rect()
        self.rect.left = random.randint(0, WIDTH - POWERUP_W)
        self.rect.bottom = random.randint(-2 * MOB_H, 0)

    def update(self):
        self.rect.y += 6
        if self.rect.top > HEIGHT:
            self.kill()

# Bullet sprite


class Bullet(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((BULLET_W, BULLET_H))
        self.image.fill(RED)
        self.rect = self.image.get_rect()
        self.rect.centerx = x
        self.rect.bottom = y
        self.speedx = 0
        self.speedy = -20

    def update(self):
        self.rect.y += self.speedy
        # kill it if it moves away from the screen
        if self.rect.bottom < 0:
            self.kill()  # built in method of pygame.sprite


# powerup sprite

power_up_funcs = [Player.increase_HP, print]  # container for to-do functs.


class PowerUp(pygame.sprite.Sprite):

    SPEEDY = 8

    def __init__(self):
        super().__init__()
        self.type = random.randint(0, 1)  # [0,1] integer
        if self.type == 0:  # HP power up
            self.image = pygame.Surface((POWERUP_W, POWERUP_H))
            self.image.fill(GREEN)
            self.rect = self.image.get_rect()
            self.rect.left = random.randint(0, WIDTH - POWERUP_W)
            # self.rect.centerx = player.rect.centerx #debug
            self.rect.bottom = 0
        elif self.type == 1:  # shield
            self.image = pygame.Surface((POWERUP_W, POWERUP_H))
            self.image.fill(BLUE)
            self.rect = self.image.get_rect()
            self.rect.left = random.randint(0, WIDTH - POWERUP_W)
            # self.rect.centerx = player.rect.centerx # debug
            self.rect.bottom = 0
        else:
            pass

    def update(self):
        self.rect.y += self.SPEEDY
        if self.rect.top > HEIGHT:
            self.kill()

1 Ответ

2 голосов
/ 24 марта 2020

TLDR: используйте import configuration и полные имена, например, configuration.running.

Если функция внутри configuration должна изменить значение верхнего уровня, она должна использовать global.

def decrease_HP(self):
    global running
    self.HP -= 1
    print("-1 HP", "Current HP:", self.HP)
    if self.HP <= 0:
        running = False

Использование from configurations import running (или эквивалент через ... import *) в main связывает значение из configurations.running с новым именем main.running. Хотя эти имена изначально имеют одно и то же значение, переназначение либо нарушает эту эквивалентность. Это точно так же, как связывание других имен.

>>> a = 1
>>> b = a  # a and b point to same value
>>> a == b
True
>>> b = 2  # rebind only b
>>> a == b
False

Чтобы сделать изменения видимыми в приложении, нужно использовать объект и изменить его значение. Типичным примером являются контейнеры, такие как списки.

>>> a = [1]
>>> b = a  # a and b point to same value
>>> a == b
True
>>> b[0] = 2  # modify content of value of b
>>> a == b
True
>>> a[0] == b[0]  # content is the same
True

Поскольку модули являются объектами, их можно использовать непосредственно для сохранения состояния.

>>> import configuration
>>> b = configuration  # configuration and b point to same value
>>> configuration == b
True
>>> b.running = False  # modify content of value of b
>>> configuration == b
True
>>> configuration.running == b.running  # content is the same
True

Функции имеют локальные объем. Любое присвоение имени внутри функции неявно объявляет цель как локальную для функции.

>>> running = True
>>> def stop():
...    running = False
...
>>> stop()  # changes only running inside the function
>>> running
True

Это можно сделать видимым, если получить доступ к локальному имени до того, как оно получит значение.

>>> running = True
>>> def stop():
...    print(running)
...    running = False
...
>>> stop()
UnboundLocalError: local variable 'running' referenced before assignment

При объявлении имени global локальное имя не создается. Глобальное значение может быть непосредственно прочитано и записано.

>>> running = True
>>> def stop():
...    global running
...    print(running)
...    running = False
...
>>> stop()  # will print the global value before the change
True
>>> running  # global value was changed
False
...