我在 Pygame 中制作了一个 Toggle Box class,但它没有修改我作为参数传递给它的原始布尔值

I made a Toggle Box class in Pygame, but it doesn't amend the original boolean I pass into it as an argument

我正在做一个简单的 Pygame 项目,在选项菜单中我有几个切换框用于关闭声音和动画。这些的布尔变量存储在单独的设置中 class。

虽然这不是真正必要的,但我想我会尝试制作一个采用以下参数的 ToggleButton class:

ToggleButton(game_instance, boolean, text, xpos, ypos, width, height)

我给了它一个 draw() 方法和一个 toggle() 方法。我想从我的主游戏程序中传入我的 self.settings.sounds_onself.settings.animations_on 布尔值,以便切换打开和关闭切换框将打开和关闭声音和动画。

在测试中,我创建了两个 ToggleButton 实例 class:

ToggleButton(self, self.game_instance, self.settings.sounds_on, "Sounds", 50, 100, 25, 25)
ToggleButton(self, self.game_instance, self.settings.animations_on, "Animations", 50, 150, 25, 25)

切换框绘制屏幕,​​可以与之交互。

Image of toggle boxes on screen

但是,虽然 toggle() 方法更改了 class 实例中的布尔值,但它似乎并没有连接到作为参数传递给实例的原始布尔值(我希望我在这里使用正确的术语,我还在学习)。因此,虽然切换框的外观会根据布尔值是 True 还是 False 而变化,但它实际上对声音和动画的设置根本没有任何影响。

这是按钮代码的 link,还有一个简短的示例程序,它只在屏幕上绘制一个切换框,每次单击它时,它都会打印原始布尔值的状态,以及实例中的布尔值。可以清楚的看到原来的boolean一直是False,而toggle box的self.boolean参数变了

https://github.com/ElJuanito82/ToggleBox/blob/main/example

所以看起来当按钮被实例化时,它会在当前状态下获取布尔值的副本,而不是直接创建 link 这样当一个改变时另一个也会改变。我的想法对吗?

这里有什么我遗漏的东西可以让它工作吗?

布尔参数是按值传递的,不能这样修改。最好的解释可能是这个问答集 - How do I pass a variable by reference? 。相当多的“教程”在这方面的细节上是不正确的,一个甚至说“一切都是通过引用传递的”——这是错误的。通常基本类型按值传递——布尔值、数字、元组、字符串。大多数其他东西,如对象和列表,都是通过引用传递的 - 这意味着它们可以在函数中更改。

考虑这个 python 会话:

Python 3.8.5 (default, Jul 28 2020, 12:59:40) 
>>> x = True
>>> def f( boolval ):
...     boolval = False
... 
>>> f(x)
>>> x
True  # <-- Still the original value

x 不会改变,因为它是作为值传递的。现在考虑同样的事情,但使用列表参数:

>>> y = [ 'apple', 'banana', 'cherry' ]
>>> def f2( fruit_list ):
...     fruit_list.append( 'durian' )
... 
>>> f2( y )
>>> y
['apple', 'banana', 'cherry', 'durian']   # <-- Changed!

这里传递了一个列表作为引用,所以可以在函数中修改。如果您打算像这样编码,了解哪些类型是可变的,哪些不是可变的非常重要。

所以对于您的 ToggleButton,显然现在我们知道布尔参数是不可变的,因此不会更改。但是您可以并且确实会更改对象内部的值。所以添加辅助函数来设置和查询它。

这是在面向对象的 GUI 控件中实现此类功能的常用方法,而不是直接询问成员变量。

例如:

class ToggleButton:

    def __init__(self, game_instance, boolean, text, xpos, ypos, width, height):
        """Initialise the toggle box attributes."""
        self.game_instance = game_instance
        self.boolean = boolean
        # [...]

    def isToggled( self ):
        """ Return True if the toggle is selected """
        return self.boolean

    def setToggled( self, value ):
        """ Set the state of the Toggle """
        self.boolean = value
        ### TODO: cause a re-draw, iff necessary

旁白:boolean 对于变量来说并不是一个好名字,因为它不是描述性的。 .is_set.checked.selected 之类的内容可能更好,因为它描述了内容所代表的内容。