[英]Python: assigning to variable also assigns to class attribute?
I haven't found any similar questions and I'm struggling to understand what my code is doing right now.我没有发现任何类似的问题,我正在努力理解我的代码现在在做什么。
I'm writing a small puzzle game with Pygame, which is also my first foray into object-oriented programming.我正在用 Pygame 编写一个小型益智游戏,这也是我第一次涉足面向对象编程。 Pygame provides
Rect
objects to define a portion of the screen (with attributes like x
, y
, width
, height
, and so on). Pygame 提供
Rect
对象来定义屏幕的一部分(具有x
、 y
、 width
、 height
等属性)。 Since my game is text-heavy, I created an object Region
to group a Rect
object with a font
object and some color information for when I need to blit to screen:由于我的游戏是大量文本,我创建了一个对象
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
I then created a Layout
class that would hold a collection of Region
objects, along with a centralized color palette and some methods for redrawing screen elements.然后我创建了一个
Layout
类,它包含一个Region
对象的集合,以及一个集中的调色板和一些重绘屏幕元素的方法。 Since this game only has one "board," I only need one Layout
object, so I made the Region
objects class attributes:由于这个游戏只有一个“棋盘”,所以我只需要一个
Layout
对象,所以我制作了Region
对象类属性:
...
progress_box: region(pygame.Rect(5, 340, 275, 10),
colors["white"],
pygame.font.SysFont('arialblack', 12),
colors["black"])
(Snipped out the other Region objects for readability.) (为了可读性,剪掉了其他 Region 对象。)
Later in the code, I have a function that draws a progress bar in progress_box
like so:在代码的后面,我有一个函数在
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
What should happen, as I understand it, is that progress_bar
copies the rect
attribute from progress_box
object, but then I reset the width
attribute to a percentage of the progress_box.rect
width.据我了解,应该发生的是
progress_bar
从progress_box
对象复制rect
属性,但随后我将width
属性重置为progress_box.rect
宽度的百分比。 I'm not assigning anything to progress_box
itself, and progress_bar
is a Region
object not connected to the Layout
class, so I assumed that progress_box.rect.width
would remain constant over every loop.我没有为
progress_box
本身分配任何东西, progress_bar
是一个未连接到Layout
类的Region
对象,所以我假设progress_box.rect.width
在每个循环中都保持不变。
Instead, whatever value I assign to progress_bar.rect.width
is also assigned to progress_box.rect.width
.相反,我分配给
progress_bar.rect.width
任何值也分配给progress_box.rect.width
。 That means, of course, that on the first pass through the loop, both those values are set to 0 because the player's score is 0, and after that they can never change.当然,这意味着在第一次通过循环时,这两个值都设置为 0,因为玩家的分数是 0,之后它们就永远不会改变。
The function works as intended if I change progress_bar.rect.width = int(progress * progress_box.rect.width)
to progress_bar.rect.width = int(progress * 275)
, but that doesn't explain to me why the variable is changing when I don't assign anything directly to it.如果我将
progress_bar.rect.width = int(progress * progress_box.rect.width)
更改为progress_bar.rect.width = int(progress * 275)
,该函数将按预期工作,但这并没有向我解释为什么该变量是当我不直接为其分配任何内容时发生变化。 I understand that these attributes are only initialized once, so any change to a class attribute will persist through every loop -- I'm just not sure why it's changing in the first place.我知道这些属性只初始化一次,因此对类属性的任何更改都将在每个循环中持续存在——我只是不确定为什么它首先会发生变化。
Looks like Cory Kramer already pointed this out in his comment, but wanted to expand on it a bit for anyone who comes across this.看起来Cory Kramer已经在他的评论中指出了这一点,但想为遇到此问题的任何人扩展一下。
The Data Model section of the official docs gives a good explanation of how objects and object references work in Python.官方文档的数据模型部分很好地解释了对象和对象引用在 Python 中的工作方式。
An object's identity never changes once it has been created;
一个对象的身份一旦被创建就永远不会改变; you may think of it as the object's address in memory.
您可以将其视为对象在内存中的地址。 The 'is' operator compares the identity of two objects;
“is”运算符比较两个对象的身份; the id() function returns an integer representing its identity.
id() 函数返回一个表示其身份的整数。
So you can think about when you're assigning objects to different variables or attributes, you are actually assigning a reference to the object, not a copy of the object.因此,您可以考虑将对象分配给不同的变量或属性时,您实际上是在分配对对象的引用,而不是对象的副本。
You can make a quick example in the repl to show this off:您可以在 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)
So I created a single Region
instance and passed it into two separate Layout
instances.所以我创建了一个
Region
实例并将它传递给两个单独的Layout
实例。 As expected, the widths are the same initially:正如预期的那样,宽度最初是相同的:
layout1.region.width
Out: 5
layout2.region.width
Out: 5
Then change the width of the region in layout1
:然后更改
layout1
区域的宽度:
layout1.region.width = 10
And checking the widths again, you'll see both changed:再次检查宽度,您会看到两者都发生了变化:
layout1.region.width
Out: 10
layout2.region.width
Out: 10
You can also check the memory addresses of each layout region to confirm that they are actually pointing to the same slot in memory (they are the same object essentially):您还可以检查每个布局区域的内存地址,以确认它们实际上指向内存中的同一个插槽(它们本质上是同一个对象):
id(layout1.region)
Out: 140631994345120
id(layout2.region)
Out: 140631994345120
Copying and equality can be a big topic , but in short you should check out the copy
module .复制和相等可能是一个很大的话题,但简而言之,您应该查看
copy
模块。
For example, if you wanted truly unique objects in that example above, you could either create unique Region
instances for each Layout
instance, or you could make a copy if you aren't able to instantiate new objects, needing to just duplicate an existing one:例如,如果你想要在上面的例子中真正独特的对象,你可以为每个
Layout
实例创建唯一的Region
实例,或者如果你不能实例化新对象,你可以制作一个副本,只需要复制一个现有的对象:
"""
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
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.