Python:分配给变量也分配给类属性?

时间:2021-07-09 17:38:43

标签: python class pygame variable-assignment class-attributes

我没有发现任何类似的问题,我正在努力理解我的代码现在在做什么。

我正在用 Pygame 编写一个小型益智游戏,这也是我第一次涉足面向对象编程。 Pygame 提供 Rect 对象来定义屏幕的一部分(具有 xywidthheight 等属性)。由于我的游戏文本较多,因此我创建了一个对象 Region,将一个 Rect 对象与一个 font 对象和一些颜色信息组合在一起,以便在我需要 blit 屏幕时使用:

    def __init__(self, rect, bg_color, font, font_color):
        self.rect = rect
        self.bg_color = bg_color
        self.font = font
        self.font_color = font_color

然后我创建了一个 Layout 类,该类将包含 Region 对象的集合,以及一个集中的调色板和一些重绘屏幕元素的方法。由于这个游戏只有一个“棋盘”,我只需要一个Layout对象,所以我制作了Region对象类属性:

   ...
   progress_box: region(pygame.Rect(5, 340, 275, 10),
                                   colors["white"],
                                   pygame.font.SysFont('arialblack', 12),
                                   colors["black"])

(为了可读性,剪掉了其他 Region 对象。)

在代码的后面,我有一个在 progress_box 中绘制进度条的函数,如下所示:

def display_progress(score, viz, max_score):
    progress = round(score/max_score, 4)
    progress_bar = region(viz.boxes["progress_box"].rect, "gold", None, None)
    progress_bar.rect.width = int(progress * progress_box.rect.width)
    viz.box_blit(progress_bar) ##viz is my layout object

据我所知,应该发生的是 progress_barrect 对象复制 progress_box 属性,但随后我将 width 属性重置为progress_box.rect 宽度。我没有为 progress_box 本身分配任何东西,progress_bar 是一个 Region 对象,没有连接到 Layout 类,所以我认为 progress_box.rect.width 会保留在每个循环中保持不变。

相反,我分配给 progress_bar.rect.width 的任何值也分配给 progress_box.rect.width。这当然意味着,在第一次通过循环时,这两个值都设置为 0,因为玩家的分数为 0,此后它们将永远不会改变。

如果我将 progress_bar.rect.width = int(progress * progress_box.rect.width) 更改为 progress_bar.rect.width = int(progress * 275),该函数会按预期工作,但这并不能向我解释为什么当我没有直接为其分配任何内容时变量会发生变化。我知道这些属性只初始化一次,因此对类属性的任何更改都会在每个循环中持续存在——我只是不确定为什么它首先会发生变化。

1 个答案:

答案 0 :(得分:0)

看起来 Cory Kramer 已经在他的评论中指出了这一点,但想为遇到此问题的任何人扩展一点。

官方文档的 Data Model 部分很好地解释了对象和对象引用在 Python 中的工作原理。

<块引用>

一个对象的身份一旦被创建就永远不会改变;您可以将其视为对象在内存中的地址。 “is”运算符比较两个对象的身份; id() 函数返回一个表示其身份的整数。

因此您可以考虑,当您将对象分配给不同的变量或属性时,您实际上是在分配对对象的引用,而不是对象的副本。


你可以在 repl 中做一个简单的例子来展示这一点:

class Region:
    def __init__(self, width, height):
        self.width = width
        self.height = height

class Layout:
    def __init__(self, region):
        self.region = region
my_region = Region(width=5, height=5)
layout1 = Layout(my_region)
layout2 = Layout(my_region)

所以我创建了一个 Region 实例并将其传递给两个单独的 Layout 实例。正如预期的那样,宽度最初是相同的:

layout1.region.width
Out: 5

layout2.region.width
Out: 5

然后改变layout1中区域的宽度:

layout1.region.width = 10

再次检查宽度,您会看到两者都发生了变化:

layout1.region.width
Out: 10

layout2.region.width
Out: 10

您还可以检查每个布局区域的内存地址,以确认它们实际上指向内存中的同一个插槽(它们本质上是同一个对象):

id(layout1.region)
Out: 140631994345120

id(layout2.region)
Out: 140631994345120

复制和相等can be a big topic,但简而言之,您应该查看copy module

例如,如果您在上面的示例中想要真正独特的对象,您可以为每个 Region 实例创建唯一的 Layout 实例,或者如果您无法创建一个副本实例化新对象,只需复制现有对象:

"""
Unique instances of Region.
"""

layout1 = Layout(Region(width=5, height=5))
layout2 = Layout(Region(width=5, height=5))

# unique references in memory

id(layout1.region)
Out: 140631995857072

id(layout2.region)
Out: 140631995436384
"""
Copy's of an existing Region instance.
"""

import copy

region = Region(width=5, height=5)

layout1 = Layout(copy.copy(region))
layout2 = Layout(copy.copy(region))

# unique references in memory

id(region)
Out: 140631993964480

id(layout1.region)
Out: 140631995585920

id(layout2.region)
Out: 140631995588176
相关问题