Reputation: 2703
Hi i have a class with a list as attribute. When i instanciate this class i want to reorder this list for this instance. Eg:
class A(object):
my_list = [1,2,3,4,5]
def __init__(self, first):
self.reorder_list(first)
def reorder_list(self, first):
self.my_list.insert(
0,
self.my_list.pop(
self.my_list.index(first)
)
)
This should do the following:
a = A(3)
a.my_list
[Out]: 3,1,2,4,5
b = A(4)
b.my_list
[Out]: 4,1,2,3,5
etc.
This seems to work.... But not always... As in the result is not consistent. When i do it like this:
def __init__(self, first):
self.my_list = self.reorder_list(first)
def reorder_list(self, first):
my_copy = copy.copy(self.my_list)
... do reordering on my_copy ...
return my_copy
Then it works consistently.
Can someone explain to me why my first piece of code is not the way to do it, and if my second way of doing it (with a copy and then reassinging self.my_list) is the correct way of doing this?
Upvotes: 0
Views: 127
Reputation: 473
In order for it to work as you espected you could do also something like this:
class A(object):
my_list = []
def __init__(self, first):
self.my_list = [1, 2, 3, 4, 5]
self.reorder_list(first)
def reorder_list(self, first):
self.my_list.insert(
0,
self.my_list.pop(
self.my_list.index(first)
)
)
Upvotes: 0
Reputation: 186
This is due to the scope of my_list within the class. As defined my_list is a class level attribute. Thus it can be accessed via A.my_list or a.my_list after the first instantiation. So as a side effect of the initial reorder A.my_list has been changed to what ever the last instantiation set it to. The copy works because a new list is created and initialized with the values from the class level attribute and used instead.
>>> A.my_list
[1, 2, 3, 4, 5]
>>> a = A(4)
>>> a.my_list
[4, 2, 1, 5, 3]
>>> A.my_list
[4, 2, 1, 5, 3]
Upvotes: 1
Reputation: 377
When you assign a value to a class attribute in Python, that value is shared across all instances of that class. For this reason, when you insert
an item to self.my_list
in any instance of A
, it is reflected across all instances of A
- they all share the same list.
To create an instance attribute, you should set self.some_attribute
instead of setting some_attribute
in the class scope. In your second example, you're re-assigning self.my_list
to a new list object in __init__
, so my_list
does not refer to the class-wide attribute anymore. It is specific to that instance.
Your solution is correct. In order to avoid the confusion, it's better to not use the same name for the class and instance attributes. For example:
class A(object):
default_list = [1, 2, 3, 4, 5]
def __init__(self, first):
self.my_list = self.reorder_list(first)
def reorder_list(self, first):
my_copy = copy.copy(self.default_list)
# ... do reordering on my_copy ...
return my_copy
Upvotes: 1