Skip to content Skip to sidebar Skip to footer

Python: Unwanted Change Of Variable Occurs

Possible Duplicate: “Least Astonishment” in Python: The Mutable Default Argument Okay, so basically I have this code: # Our Rule type. class Rule(object): '''An object

Solution 1:

I'm not 100% sure about this but I suspect the problem is that variables in Python are references and not values. That is to say, when you set seq[i] = self.rule, seq[i] points to where self.rule is stored in memory and any changes made to seq[i]'s value results in a change to self.rule since their values are stored at the same location in memory.

What you probably should do is deep copy self.rule's value into seq[i] rather than simply assign it so that they use two different memory addresses to store their values (see http://docs.python.org/library/copy.html for more information), that way assigning to seq[i] won't affect self.rule.


Solution 2:

Default arguments are evaluated once when the function is defined, and reused later.

def f(x, a = [])
    a.append(x)
    return a
print f(0)
print f(1)

prints

[0]
[0, 1]

The usual way to avoid this problem is to use None as default parameter:

def f(x, a = None):
    if a is None:
        a = []
    # ...

See the Python tutorial for a detailed explanation.


Solution 3:

I'm not sure if this is the problem, but you have to be very careful about setting a default list in a function like this:

def __init__(self,bind="F",rule=["F"]):
    self.bind = bind
    self.rule = rule

Each time you call this function, it's going to use the same list every time! Therefore, every time you modify self.rule in one Rule object, you are modifying it in every other object you have.

Instead, you should do this:

def __init__(self,bind="F",rule=None):
    self.bind = bind
    if not rule:
        self.rule = ['F']
    else:
        self.rule = rule

This way, you create a new list object every time, instead of reusing the same one.


For example:

class foo:
  def __init__(self, x=[]):
     self.x = x
     self.x.append(3)

d = foo()
e = foo()
f = foo()
print f.x   # prints [3,3,3]

Solution 4:

It changes because rule is a reference to a list. You then assign seq[i] to refer to the object pointed to by self.rule. So when you modify seq, you're modifying the same object referenced by self.rule.

>>> a = [1,2,3,4]
>>> b = a
>>> b.append(5)
>>> print a
[1, 2, 3, 4, 5]

I believe that what you are looking for is a copy of the list stored in rule. This can be done in a couple ways. In the first, you construct a new list from the current iterable.

seq[i] = list(self.rule) # Makes a new list that is a copy of self.rule

If self.rule contains a list of immutables (integers, strings, etc.), then that should be sufficient. If, however, self.rule contains mutables, like class instances, then what you'll end up with is a new list that references all of the same objects that were referenced in self.rule. What that means can be seen in the following code:

>>> class A(object):
...     def __init__(self, val):
...        self.x = val

>>> a = [A(i) for i in xrange(10)]
>>> print a[0].x
0
>>> print a[1].x
1
>>> b = list(a)
>>> b.append(A(10))
>>> print b[0].x
0
>>> b[0].x = 99
>>> print b[0].x
99
>>> print a[0].x
99
>>> print (len(a), len(b))
(10, 11)

Here we have two different lists, but two different lists that both have 10 references in common. List b has an additional reference, further cementing its difference. To avoid that issue, you can use the second method, the deepcopy.

>>> from copy import deepcopy
>>> a = [A(i) for i in xrange(10)]
>>> b = deepcopy(a)
>>> b[0].x = 99
>>> print b[0].x
99
>>> print a[0].x
0

This makes a copy of the list and all of the objects in that list.

Finally, I have to caution you against using a list as a default value for the rule keyword argument. That almost certainly will not work the way you expect. The keyword argument rule gets an instance of the list ["F"] at definition. All calls to __init__ will get the same list instance, so modifying that list will affect the default behavior as rule changes. See Default value of memberfunction for another example.


Post a Comment for "Python: Unwanted Change Of Variable Occurs"