Robin  van Leeuwen
Robin van Leeuwen

Reputation: 2703

Python reorder a class attribute list on initialization

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

Answers (3)

Rafa He So
Rafa He So

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

John F
John F

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

matangover
matangover

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

Related Questions