michaelpri
michaelpri

Reputation: 3651

How to change an instance variable of a class when that variable is inside a list

I'm writing a tic-tac-toe game in Python and for one part I have a lot of instance variables in a class and they are all inside a list. I'm trying to change one the instance variables, but when it is inside the list, I can only change the list element.

Here some code:

# only a piece of my code

class Board(object):
    def __init__(self):
        self.one = "1"
        self.two = "2"
        self.three = "3"

board = Board()

configs = [board.one, board.two, board.three]
configs[2] = "X"
print board.three
print configs

Expected result:

X
['1', '2', 'X']

Actual result:

3
['1', '2', 'X']

Is there the a way to get my expected result?

Upvotes: 0

Views: 8499

Answers (2)

Ozgur Vatansever
Ozgur Vatansever

Reputation: 52133

Strings are immutable objects, so when you change the item in the list with the given index, list now points to totally separate string X.

Same scenario applies here;

>>> configs[2] += "X"
>>> print configs[2]
'3X'

>>> print board.three
'3'

One alternative would be to execute a callback function whenever an item in the list gets updated. (However, I'd personally discourage that, because it seems an hacky solution.)

class interactive_list(list):
    def __init__(self, *args, **kwargs):
        self.callback = kwargs.pop('callback', None)
        super(interactive_list, self).__init__(*args, **kwargs)

    def __setitem__(self, index, value):
        super(interactive_list, self).__setitem__(index, value)
        if self.callback:
            self.callback(index, value)

>>> board = Board()

>>> def my_callback(index, value):
...    if index == 2:
...        board.three = value

>>> configs = interactive_list([board.one, board.two, board.three], callback=my_callback)
>>> configs[2] = 'X'

>>> print board.three
'X'

Upvotes: 1

ljetibo
ljetibo

Reputation: 3094

Have you thought about using better data structures? Like dictionaries i.e.

class Board(object):
    def __init__(self):
        self.dict = {"one":"1", "two":"2", "three":"3"}

And then you could do something like:

>>> a = Board()
>>> a.dict
{'three': '3', 'two': '2', 'one': '1'}
>>> for element in a.dict:
    a.dict[element] = element+"x"
>>> a.dict
{'three': 'threex', 'two': 'twox', 'one': 'onex'}
>>> a.dict["one"] = "1"
>>> a.dict
{'three': 'threex', 'two': 'twox', 'one': '1'}

The solution you're looking for is also possible (most likely with some very very weird getattrs etc... and I wouldn't really recommend it.

Edit1 It turns out (after checking) that your class attributes will be stored in a object.__dict__ anyhow. SO why not use your own.

Just also to clarify it is possible emulating container objects with your own class by defining __getitem__ and __setitem__ methods like bellow:

class Board(object):
    def __init__(self):
        self.dict = {"one":"1", "two":"2", "three":"3"}
    def __getitem__(self,key):
        return self.dict[key]
    def __setitem__(self, key, value):
        self.dict[key] = value

Which means you don't have to keep writing a.dict everywhere and can pretend your class is the container (dict) like bellow:

>>> a = Board()
>>> a.dict
{'three': '3', 'two': '2', 'one': '1'}
>>> a["one"]
'1'
>>> a["one"] = "x"
>>> a.dict
{'three': '3', 'two': '2', 'one': 'x'}

Upvotes: 1

Related Questions