2

We are struggling with the following problem in python 3: we have defined a class, which inherits from list. We want to incorporate a method by which it can update itself to a new instance of this class.

Some very simple code to illustrate what we mean:

class test(list):
    def update(self):
        self = test(['a','b','c','d'])

x = test([1,2,3])
x.update()
print(x)

This code returns the original list [1,2,3], which shows that the object x indeed hasn't been updated. Is there a way to let an object update itself completely? We need this to work for lists of different lengths.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
helm100
  • 218
  • 2
  • 6
  • `self` is just another reference to the instance. Assigning something different to that name won't change the instance, though. – Martijn Pieters Jun 21 '18 at 14:33
  • Are you trying to replace the `test` instance with another `test` instance, or replace the *contents* of the `test` instance with `['a','b','c','d']`? – Aran-Fey Jun 21 '18 at 14:38
  • Check out the following threads: https://stackoverflow.com/questions/1015592/why-is-self-in-python-objects-immutable https://stackoverflow.com/questions/1216356/is-it-safe-to-replace-a-self-object-by-another-object-of-the-same-type-in-a-meth – GSazheniuk Jun 21 '18 at 14:38

1 Answers1

4

self is just another variable. Method objects cause the current instance to be assigned to that name (just like other values are assigned to other function parameters), but like other names, assigning to them will only change what object the name refers to. Other names that reference the same object, like x, do not change along.

That's because names in Python code are just like tags, and self = <something else> is like taking the tag of the old instance and putting it on another object. Any other tags still attached to the old object are not moved! Because x still references the old instance, it is not going to see that you put the self tag on another object.

Either your update() method needs to return the new object (so that the caller can update their reference to point to the new object), or change the list in-place (at which point all references to the object will see the same change.

Returning the object means the caller is responsible for doing something with the return value:

class test(list):
    def update(self):
        return test(['a','b','c','d'])

x = test([1,2,3])
x = x.update()  # re-bind `x` to point to the new object.
print(x)

Or you can assign to indices of the list itself; this changes what the contents of the list point to. You can use self just like any other reference, so x[0] = 5 would change index 0, so would self[0] = 5. And if you assign to a slice you can change multiple indices at once. The identity slice, [:], lets you change all indices at once, and you can assign more or fewer values to grow or shrink a list. Assigning to self[:] changes all indices, and lets you change all indices to point to any number of different values.

So:

class test(list):
    def update(self):
        self[:] = ['a', 'b', 'c', 'd']


x = test([1,2,3])
x.update()
print(x)  # The x reference to the same list will see the change too

The above replaces all indices with the values in the other list. All references to the instance will see the change; both as self and x, and any other such references.

Demo of what this means:

>>> class test(list):
...     def update(self):
...         self[:] = ['a', 'b', 'c', 'd']
...
>>> x = test([1, 2, 3])
>>> y = x   # another reference to the same object
>>> x
[1, 2, 3]
>>> y
[1, 2, 3]
>>> x.update()  # update the list in-place
>>> x   # visible here
['a', 'b', 'c', 'd']
>>> y   # and here!
['a', 'b', 'c', 'd']
Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343