Python, 文件间变量导入
Python, variable importing between files
前言:我觉得我可能在一个简单的情况下浪费了这么多时间...
现在,我正在用 pygame 制作游戏,在某些时候,我想将文件分成两个,即 main.py
和 configurations.py
,以便使它更可读。
一切都很顺利,直到我 运行 遇到这个问题。
我会在底部分享整个代码,但我想先总结一下:
现在首先,在 main.py
中,我通过
导入
from configurations import *
现在,main.py
上的游戏循环取决于变量 running
by
while running:
.......
.......
.......
变量running
在configurations.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.running
的 value 绑定到新名称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
前言:我觉得我可能在一个简单的情况下浪费了这么多时间...
现在,我正在用 pygame 制作游戏,在某些时候,我想将文件分成两个,即 main.py
和 configurations.py
,以便使它更可读。
一切都很顺利,直到我 运行 遇到这个问题。
我会在底部分享整个代码,但我想先总结一下:
现在首先,在 main.py
中,我通过
from configurations import *
现在,main.py
上的游戏循环取决于变量 running
by
while running:
.......
.......
.......
变量running
在configurations.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.running
的 value 绑定到新名称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