开发者

Changing a variable inside a class

I'm programming a text field class for a game. It's supposed to change a string from outside the class to the string the user enters. The following is pseudo-code, but you should understand my problem :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)

I just want txtfield to change the variable a, but apparently that's not working the way I do it.


You can't. Python is exclusively pass-by-value, you're asking for pass-by-reference. Explaining the full model with everything it builds on etc. would be lengthy and was already done in the official documentation (read: refer to that if you want more details), but long story short, what happens in your code is this:

  • a is assigned a pointer/reference (not the same "reference" as in "pass-by-reference"!) to a string object ("NAME")
  • That pointer/reference is copied (pass-by-value) when the class's constructor is called.
  • This reference is again copied into a field of self.

You see, the reference stored in the field of self is completely unrelated to the one stored in a. They refer to the same object, but they're totally independent copies. Overwriting one with a new reference (that's all you can do) won't change other copies. Of course not, how could it?

Now, references to mutable object would be a different matter. You still can't change references in another scope, but you can obviously change what others see when they follow their copy of the reference (e.g. change fields). Strings are immutable though, so this doesn't help you. And it's not useful to emulate pass-by-reference in general (you'd have to route everything through a field of a proxy object) but only to share state. So the simplest solution is: Ask youself why the heck you'd need this, and come up with a cleaner/more doable solution.


As delnan's post indicates, we could provide better help if you said more about what your goal is. That said, I think the best way to do what you say you want is to encapsulate the variables to be modified in a class, and use 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

You could also use a dictionary, as ninjagecko suggests at the end of his answer, but I think this is a bit closer to what you want.

Still, I suspect that you ought to want something else. A much better solution, if this is possible, would be simply to have update return a value, as in:

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")


It seems what you actually want is a callback:

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)

You can even set variables this way:

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!

The reason you couldn't "set a variable" in your original version was because you told the function "set 'popo' to 'popo'"; the function setting the thing needed to know the name of the object. In the example I gave above, you are saying "set self.A to 'popo'". Here's an example without a class:

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

Here is an example that uses dictionaries (you can also use lists) to avoid having to give a card-coded name:

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

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜