Python, 文件间变量导入

Python, variable importing between files

前言:我觉得我可能在一个简单的情况下浪费了这么多时间...

现在,我正在用 pygame 制作游戏,在某些时候,我想将文件分成两个,即 main.pyconfigurations.py,以便使它更可读。

一切都很顺利,直到我 运行 遇到这个问题。

我会在底部分享整个代码,但我想先总结一下:

现在首先,在 main.py 中,我通过

导入

from configurations import *

现在,main.py 上的游戏循环取决于变量 running by

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

变量runningconfigurations.py中被初始化,

# initialize some variables
running = True

因此,main.py 必须获取变量 running,因为它不会给出任何错误并在 while running 语句中使用它。

在主循环中,有一个部分我检查事件如下,

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

这部分按预期工作,它改变变量 运行 并且程序退出 while 循环。

现在,有问题的部分来了。

在其中一个 classes(Player class) 中有一个方法 decrease_HP,

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

然而,我无法弄清楚的一点是,它没有正确地改变 运行 变量,并且游戏永远不会停止(退出 while 循环)。这是一个显示问题的示例输出。

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

所以,我希望我能说清楚。我可能对导入变量或变量范围有误解。

顺便说一下,我试过在 Player.decrease_HP 函数中的 running = False 语句上方添加 global running

提前致谢。


文件中的确切代码

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()

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

main 中使用 from configurations import running(或通过 ... import * 等效)将 configurations.runningvalue 绑定到新名称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