0

I currently have a class, that has 2 attributes, initial_value, and current_value. Both are lists, and the initial value is set programmatically during run time, and is not always the same. They start off the same, but throughout the code, the current_value changes. I then need to reset it to the initial_value, but I am wondering the best way to do this without my code ever affecting the initial_value.

Here is what does not work due to the pointer changing to the same object as initial value:

class MyClass:
    def __init__(self,v1,v2,v3,v4,v5,v6):
        ## both initial value and current value get set as the same value during run time
        self.initial_value = [[v1,v2,v3],[v4,v5,v6]]
        self.current_value = [[v1,v2,v3],[v4,v5,v6]]

    def Reset(self):
        self.current_value = self.initial_value ### what is the best way to fix this?



myclass = MyClass(1,2,3,4,5,6)

# value of current_value will change throughout the program
myclass.current_value[0][0] = 5

# periodically, need to reset
myclass.Reset()

The challenge that I am having is that the Reset function gets called many many times, so I am trying to come up with the fastest way of achieving this result.

The best I could come up with so far is:

self.current_value = [*self.initial_value]

Being self taught, I just dont know what the most idiomatic, and efficient way of achieving this.

Thanks so much in advanced!

croux0110
  • 1
  • 1
  • You can use `self.current_value=copy.deepcopy(self.initial_value)` to make a copy of your list as explained in the answer to this question: [List changes unexpectedly after assignment. How do I clone or copy it to prevent this?](https://stackoverflow.com/questions/2612802/list-changes-unexpectedly-after-assignment-how-do-i-clone-or-copy-it-to-prevent) – Craig Sep 27 '20 at 16:32
  • Thanks for the link. I tried each of those methods, but are still quite slow. Could just be my computer, but unpacking it seems to be just a touch faster then the ones listed in that link. When I use just a simple `self.current_value = self.init_value`, it is almost twice as fast as every other option. is this more or less unavoidable? – croux0110 Sep 27 '20 at 16:42
  • `self.current_value = self.init_value` is fine as long as you never do anything like `myclass.current_value[0][0] = 5`. Doing that also changes `init_value` and will cause you endless confusion when you try to figure out why your `init_value` keeps changing. There is no point in comparing the speed of approaches that don't actually work for your needs. – Craig Sep 27 '20 at 16:57

3 Answers3

2

There are many options how to handle that. Myself, I would create get_init_values() method that returns initial values for a list:

class MyClass:
    def __init__(self):        
        # self.initial_value = self.get_init_values()  # now the initial value is not needed, all is handled by get_init_values()
        self.current_value = self.get_init_values()

    def get_init_values(self):
        return [[1,2,3],[4,5,6]]

    def Reset(self):
        self.current_value = self.get_init_values()

myclass = MyClass()

# value of current_value will change throughout the program
myclass.current_value[0][0] = 5

# periodically, need to reset
myclass.Reset()

EDIT: Updated for initial values in constructor:

class MyClass:
    def __init__(self,v1,v2,v3,v4,v5,v6):
        self.__values = [v1,v2,v3,v4,v5,v6]
        self.current_value = self.get_init_values()

    def get_init_values(self):
        return [self.__values[0:3], self.__values[3:6]]

    def Reset(self):
        self.current_value = self.get_init_values()



myclass = MyClass(1,2,3,4,5,6)

# value of current_value will change throughout the program
myclass.current_value[0][0] = 5

# periodically, need to reset
myclass.Reset()

print(myclass.current_value)
Andrej Kesely
  • 168,389
  • 15
  • 48
  • 91
  • Thanks so much for the response. I should have clarified in the code, that the initial values are different for each instantiation, so I am unable to hardcode the value. – croux0110 Sep 27 '20 at 16:30
  • @croux0110 I updated my answer. You can store the initial values in list and then do slicing (but there are many other options - deepcopy, list comprehension, etc.) – Andrej Kesely Sep 27 '20 at 17:55
0

The problem is that lists are containers and assignment of containers creates a new reference to the existing container, not a new copy of the container. You have a container that contains other containers (a list of lists), which means that you have to make sure that you are copying all of the lists, not just the top level one. This is the reason that copy.deepcopy() exists.

import copy

class MyClass:
    ...

    def Reset(self):
        self.current_value=copy.deepcopy(self.initial_value)

As an alternative to copy.deepcopy() if you have a fixed structure for your items, for example, you know that they are always lists of lists, then you can use a list comprehension to create a copy:

    def Reset(self):
        self.current_value=[lst[:] for lst in self.initial_value]

Testing these two approaches shows that the comprehension is much faster:

In [1]: import copy
In [2]: iv = [range(100) for _ in range(10)]

In [3]: %timeit copy.deepcopy(iv)
47.8 µs ± 593 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [4]: %timeit [lst[:] for lst in iv]
2.63 µs ± 33.1 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
Craig
  • 4,605
  • 1
  • 18
  • 28
-1

Have you tried self.current_value = self.initial_value?

hd1
  • 33,938
  • 5
  • 80
  • 91