我在 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_on 或 self.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
之类的内容可能更好,因为它描述了内容所代表的内容。
我正在做一个简单的 Pygame 项目,在选项菜单中我有几个切换框用于关闭声音和动画。这些的布尔变量存储在单独的设置中 class。
虽然这不是真正必要的,但我想我会尝试制作一个采用以下参数的 ToggleButton class:
ToggleButton(game_instance, boolean, text, xpos, ypos, width, height)
我给了它一个 draw() 方法和一个 toggle() 方法。我想从我的主游戏程序中传入我的 self.settings.sounds_on 或 self.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
之类的内容可能更好,因为它描述了内容所代表的内容。