改变类内的变量

2 投票
3 回答
5193 浏览
提问于 2025-04-16 15:53

我正在为一个游戏编写一个文本框的类。这个文本框的功能是把外部传入的字符串,变成用户输入的字符串。下面是伪代码,但你应该能理解我的问题 :D

class txtfield(pygame.sprite.Sprite):
    def __init__(self, var, name, x, y):
        self.var=var
        self.value=""  


    def update(self, key):
        #I removed image-changing and the code which changes the string self.value 
        #key is a character from the last pressed button
        #(self.value+=key)
        if key=="return":
            self.var=self.value
            # I want this to be like 'a=self.value'.
            #-> assuming self.name is 'Peter', 'a' should change to 'Peter' 
            #if the user hits enter

a="NAME"
txt=txtfield(a, "popo", 20, 20)

我只是想让 txtfield 改变变量 a,但显然我这样做并没有成功。

3 个回答

0

看起来你真正想要的是一个回调函数:

class txtfield(pygame.sprite.Sprite):
    def __init__(self, ***callback***, name, x, y):
        ***self.callback=callback***
        self.value=""

    def update(self, key):
        #I removed image-changing and the code which changes the string self.value 
        #key is a character from the last pressed button
        #(self.value+=key)
        if key=="return":
            ***self.callback(self.value)***
            # I want this to be like 'a=self.value'.
            #-> assuming self.name is 'Peter', 'a' should change to 'Peter' 
            #if the user hits enter

***def myCallback(text):
    print(text)***
txt=txtfield(***myCallback***, "popo", 20, 20)

你甚至可以通过这种方式来设置变量:

class MyController(object):
    def __init__(self):
        self.A = ''

    def makeCallback(self):
        """
            Makes a callback which modifies self.A
        """
        def myCallback(text):
            self.A = text
        return myCallback

controller = MyController()
txt = txtfield(controller.makeCallback(), "popo", 20, 20)
assert controller.A=='popo'  #--> True!

你在最开始的版本中不能“设置变量”的原因是因为你告诉函数“把'popo'设置为'popo'”;这个函数需要知道你要设置的对象的名字。在我上面给的例子中,你是在说“把self.A设置为'popo'”。下面是一个没有使用类的例子:

A = ''
def setA(text):
    A = text
txt = txtfield(setA, "popo", 20, 20)
assert A=='popo'

这里有一个使用字典(你也可以使用列表)的例子,这样就不需要给出硬编码的名字:

lookupTable = {}
def setA(text):
    lookupTable['A'] = text
txt = txtfield(setA, "popo", 20, 20)
assert lookupTable['A']=='popo'
5

你不能这样做。Python 只支持值传递,而你想要的是引用传递。要详细解释这个模型以及它的基础知识会很复杂,官方文档里已经有详细说明(如果你想了解更多,可以去看看),但简单来说,你代码中发生的事情是这样的:

  • a 被赋值为一个指向字符串对象("NAME")的指针/引用(注意,这里的“引用”跟“引用传递”不是同一个意思!)
  • 当类的构造函数被调用时,这个指针/引用会被复制(值传递)。
  • 这个引用又被复制到 self 的一个字段里。

你看,存储在 self 字段里的引用和 a 里的引用是完全不相关的。它们指向同一个对象,但它们是完全独立的副本。用一个新的引用去覆盖其中一个(这就是你能做的)不会改变其他的副本。当然不会,怎么可能呢?

如果是可变对象的引用,那就另当别论了。你仍然不能在另一个作用域中改变引用,但你可以改变其他人看到的内容(比如修改字段)。不过字符串是不可变的,所以这对你没有帮助。而且一般来说,模拟引用传递并不实用(你需要把所有东西都通过一个代理对象的字段来传递),而只是为了共享状态。所以最简单的解决办法是:问问自己,为什么你需要这样做,然后想出一个更简单、更可行的解决方案。

1

正如delnan的帖子所提到的,如果你能多说说你的目标是什么,我们就能提供更好的帮助。话虽如此,我认为实现你想要的最好方法是把要修改的变量放在一个类里面,然后使用setattr

class UserEnteredData(object):
    def __init__(self):
        self.a = None
        self.b = None

class TextField(pygame.sprite.Sprite):
    def __init__(self, userdata, uservar, name, x, y):
        self.userdata = userdata
        self.var = uservar
        self.value = ""  
    def update(self, key):
        if key == "return":
            setattr(self.userdata, self.var, self.value)

userdata = UserEnteredData
userdata.a = "NAME"
txt = TextField(userdata, "a", "popo", 20, 20)
txt.update("return")
print userdata.a       # prints a null string

你也可以使用字典,正如ninjagecko在他回答的最后提到的,但我觉得这更接近你想要的。

不过,我怀疑你其实应该想要别的东西。如果可能的话,一个更好的解决方案就是让update直接返回一个值,比如:

class TextField(pygame.sprite.Sprite):
    # [... define init here ...]
    def update(self, key):
        if key=="return":
            return self.value

# [... create txt, etc ...]
a = txt.update("return")

撰写回答