user2776708
user2776708

Reputation: 31

Why does the class variable change?

So I was programming something in python that had to do with assigning a class variable to an instance variable, and then changing the instance variable inside the init method. I simplified the code a bit, when I run it the class variable gets changed as well:

class Map():
    map = [1,2,3]

    def __init__(self): 
        self.map = Map.map
        for i in range(len(self.map)):
            self.map[i] = self.map[i] * 2

        print("self.map =", self.map)
        print("Map.map =", Map.map)

new_map = Map()     

When I run it, I get the following output:

self.map = [1, 4, 9]
Map.map = [1, 4, 9]

Basically I was wondering why Map.map gets changed even though I only changed self.map?

Upvotes: 1

Views: 230

Answers (4)

Mark Ransom
Mark Ransom

Reputation: 308111

In Python, assignment is not copying! It just gives you another name to access the same object.

If the object is immutable, such as a number or string, you won't notice the difference. But when you do an assignment to a mutable object such as a list, any changes made to that object get reflected in all the other assigned names, since they're still the same object.

To make a copy of a list you can use slice notation (the slice does a copy) or you can use the copy module.

Upvotes: 1

Lennart Regebro
Lennart Regebro

Reputation: 172209

As others have noted, it's because you are using a mutable class variable.

As you are using the same list for all instances, having a class variable is pointless.

class Map():
    map = [1,2,3]
    def __init__(self):
        self.map = Map.map[:]

Is completely functionally equivalent with:

class Map():
    def __init__(self):
        self.map = [1,2,3]

Which is much easier to understand.

In general, mutable class variables is a bad idea, except for some very specific circumstances.

Upvotes: -1

Kyle Kelley
Kyle Kelley

Reputation: 14144

self.map and Map.map are both pointing to the same list. In Python parlance, self.map and Map.map are both names for the same list.

Check to see if id(self.map) == id(Map.map), and you'll find that to be True.

To get the behavior you want, assign a copy of Map.map to self.map.

Use either list()

self.map = list(Map.map)

or the slice syntax

self.map = Map.map[:]

The builtin copy module can be used for this as well, especially if you have nested lists.

Upvotes: 0

Claudiu
Claudiu

Reputation: 229301

What you want is this:

    self.map = Map.map[:]

The reason is that when you do self.map = Map.map, self.map is pointing to the same object as Map.map. So when you mutate self.map you are also mutating Map.map because it's the same object. It's the same phenomenon as here:

>>> a = [1, 2, 3]
>>> b = a
>>> b[0] = 4
>>> a
[4, 2, 3]
>>> b is a
True

What you instead want to do is copy the object. You can copy lists by doing list(a) or a[:]:

>>> a = [1, 2, 3]
>>> b = a[:]
>>> b[0] = 4
>>> a
[1, 2, 3]
>>> b
[4, 2, 3]
>>> b is a
False

Upvotes: 5

Related Questions