改变类内的变量
我正在为一个游戏编写一个文本框的类。这个文本框的功能是把外部传入的字符串,变成用户输入的字符串。下面是伪代码,但你应该能理解我的问题 :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 个回答
看起来你真正想要的是一个回调函数:
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'
你不能这样做。Python 只支持值传递,而你想要的是引用传递。要详细解释这个模型以及它的基础知识会很复杂,官方文档里已经有详细说明(如果你想了解更多,可以去看看),但简单来说,你代码中发生的事情是这样的:
a
被赋值为一个指向字符串对象("NAME"
)的指针/引用(注意,这里的“引用”跟“引用传递”不是同一个意思!)- 当类的构造函数被调用时,这个指针/引用会被复制(值传递)。
- 这个引用又被复制到
self
的一个字段里。
你看,存储在 self
字段里的引用和 a
里的引用是完全不相关的。它们指向同一个对象,但它们是完全独立的副本。用一个新的引用去覆盖其中一个(这就是你能做的)不会改变其他的副本。当然不会,怎么可能呢?
如果是可变对象的引用,那就另当别论了。你仍然不能在另一个作用域中改变引用,但你可以改变其他人看到的内容(比如修改字段)。不过字符串是不可变的,所以这对你没有帮助。而且一般来说,模拟引用传递并不实用(你需要把所有东西都通过一个代理对象的字段来传递),而只是为了共享状态。所以最简单的解决办法是:问问自己,为什么你需要这样做,然后想出一个更简单、更可行的解决方案。
正如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")