Change class instance inside an instance method
Asked Answered
O

3

7

Any idea if there is a way to make the following code to work

class Test(object):

    def __init__(self, var):
        self.var = var

    def changeme(self):
        self = Test(3)

t = Test(1)
assert t.var == 1
t.changeme()
assert t.var == 3

is something like the following safe to use for more complex objects (like django models, to hot swap the db entry the instance is referring to)

class Test(object):

    def __init__(self, var):
        self.var = var

    def changeme(self):
        new_instance = Test(3)
        self.__dict__ = new_instance.__dict__

t = Test(1)
assert t.var == 1
t.changeme()
assert t.var == 3
Ocasio answered 26/1, 2010 at 6:13 Comment(1)
Why? You clearly want a new instance, why not just make a new instance and throw this one away? Explain the usecase, please.Spleen
G
7

self = Test(3) is re-binding the local name self, with no externally observable effects.

Assigning self.__dict__ (unless you're talking about instances with __slots__ or from classes with non-trivial metaclasses) is usually OK, and so is self.__init__(3) to re-initialize the instance. However I'd prefer to have a specific method self.restart(3) which knows it's being called on an already-initialized instance and does whatever's needed to cater for that specific and unusual case.

Graehme answered 26/1, 2010 at 6:20 Comment(2)
Django's model metaclasses are... messy. I wouldn't be surprised if trying to reassign self.__dict__ blew up spectacularly.Unclad
@Ignacio, with "non-trivial metaclasses" in play (or __slots__, etc), you may want to use a loop of getattr/setattr instead. The advantage of writing the restart method I advocate is that you isolate there the tweaking (if any) needed to support various cases, from normal objects to ones with messy metaclasses and so forth.Graehme
U
2

No, and no.

Having said that, you can change the class, but don't do that either.

Unclad answered 26/1, 2010 at 6:16 Comment(0)
A
2

The former code works, except it won't do much, seeing as it just replaces the object named 'self' within the scope of changeme(). Python names aren't locked to values, they are always relative to their scope or namespace.

To do what you want you'd need to have access to a name outside the class, which you could assign to from within it:

class Test:
  def changeme(self):
    global myclass
    myclass = Test(3)

myclass = Test(2)
myclass.changeme()
print myclass # 3

This basically just overwrites the name 'myclass' to point to the new instance. It doesn't "overwrite" the first instance like you might think. The old instance still lives, and will be garbage collected unless referenced elsewhere.

Alary answered 26/1, 2010 at 6:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.