Is it safe to replace a self object by another object of the same type in a method?

Question:

I would like to replace an object instance by another instance inside a method like this:

class A:
    def method1(self):
        self = func(self)

The object is retrieved from a database.

Asked By: amirouche

||

Answers:

Yes, all that will happen is that you won’t be able to reference the current instance of your class A (unless you set another variable to self before you change it.) I wouldn’t recommend it though, it makes for less readable code.

Note that you’re only changing a variable, just like any other. Doing self = 123 is the same as doing abc = 123. self is only a reference to the current instance within the method. You can’t change your instance by setting self.

What func(self) should do is to change the variables of your instance:

def func(obj):
    obj.var_a = 123
    obj.var_b = 'abc'

Then do this:

class A:
    def method1(self):
        func(self) # No need to assign self here
Answered By: Blixt

It is unlikely that replacing the ‘self’ variable will accomplish whatever you’re trying to do, that couldn’t just be accomplished by storing the result of func(self) in a different variable. ‘self’ is effectively a local variable only defined for the duration of the method call, used to pass in the instance of the class which is being operated upon. Replacing self will not actually replace references to the original instance of the class held by other objects, nor will it create a lasting reference to the new instance which was assigned to it.

Answered By: Amber

It is not a direct answer to the question, but in the posts below there’s a solution for what amirouche tried to do:

And here’s working code sample (Python 3.2.5).

class Men:
    def __init__(self, name):
        self.name = name

    def who_are_you(self):
        print("I'm a men! My name is " + self.name)

    def cast_to(self, sex, name):
        self.__class__ = sex
        self.name = name

    def method_unique_to_men(self):
        print('I made The Matrix')


class Women:
    def __init__(self, name):
        self.name = name

    def who_are_you(self):
        print("I'm a women! My name is " + self.name)

    def cast_to(self, sex, name):
        self.__class__ = sex
        self.name = name

    def method_unique_to_women(self):
        print('I made Cloud Atlas')


men = Men('Larry')
men.who_are_you()
#>>> I'm a men! My name is Larry
men.method_unique_to_men()
#>>> I made The Matrix


men.cast_to(Women, 'Lana')
men.who_are_you()
#>>> I'm a women! My name is Lana
men.method_unique_to_women()
#>>> I made Cloud Atlas

Note the self.__class__ and not self.__class__.__name__. I.e. this technique not only replaces class name, but actually converts an instance of a class (at least both of them have same id()). Also, 1) I don’t know whether it is "safe to replace a self object by another object of the same type in [an object own] method"; 2) it works with different types of objects, not only with ones that are of the same type; 3) it works not exactly like amirouche wanted: you can’t init class like Class(args), only Class() (I’m not a pro and can’t answer why it’s like this).

Answered By: Pugsley

One can use the self assignment in a method, to change the class of instance to a derived class.

Of course one could assign it to a new object, but then the use of the new object ripples through the rest of code in the method. Reassiging it to self, leaves the rest of the method untouched.

class aclass:

    def methodA(self):
        ...
        if condition:
            self = replace_by_derived(self)
            # self is now referencing to an instance of a derived class
            # with probably the same values for its data attributes

        # all code here remains untouched
        ...
        self.methodB() # calls the methodB of derivedclass is condition is True
        ...

    def methodB(self):
        # methodB of class aclass
        ...

class derivedclass(aclass):
    def methodB(self):
        #methodB of class derivedclass
        ...

But apart from such a special use case, I don’t see any advantages to replace self.

Answered By: Ruben Decrop

As far as I understand, If you are trying to replace the current object with another object of same type (assuming func won’t change the object type) from an member function. I think this will achieve that:

class A:
    def method1(self):
        newObj = func(self)
        self.__dict__.update(newObj.__dict__)
Answered By: Nithin

Yes, there is nothing wrong with this. Haters gonna hate. (Looking at you Pycharm with your in most cases imaginable, there's no point in such reassignment and it indicates an error).

A situation where you could do this is:

some_method(self, ...):

    ...

    if(some_condition):
        self = self.some_other_method()

    ...

    return ...

Sure, you could start the method body by reassigning self to some other variable, but if you wouldn’t normally do that with other parametres, why do it with self?

Answered By: oulenz

In many cases, a good way to achieve what you want is to call __init__ again. For example:

class MyList(list):
    def trim(self,n):
        self.__init__(self[:-n])

x = MyList([1,2,3,4])
x.trim(2)
assert type(x) == MyList
assert x == [1,2]

Note that this comes with a few assumptions such as the all that you want to change about the object being set in __init__. Also beware that this could cause problems with inheriting classes that redefine __init__ in an incompatible manner.

Answered By: Wrzlprmft

You can make the instance a singleton element of the class
and mark the methods with @classmethod.

from enum import IntEnum
from collections import namedtuple

class kind(IntEnum):
    circle = 1
    square = 2

def attr(y): return [getattr(y, x) for x in 'k l b u r'.split()]

class Shape(namedtuple('Shape', 'k,l,b,u,r')):
    self = None

    @classmethod
    def __repr__(cls):
        return "<Shape({},{},{},{},{}) object at {}>".format(
            *(attr(cls.self)+[id(cls.self)]))

    @classmethod
    def transform(cls, func):
        cls.self = cls.self._replace(**func(cls.self))

Shape.self = Shape(k=1, l=2, b=3, u=4, r=5)
s = Shape.self

def nextkind(self):
    return {'k': self.k+1}

print(repr(s))  # <Shape(1,2,3,4,5) object at 139766656561792>
s.transform(nextkind)
print(repr(s))  # <Shape(2,2,3,4,5) object at 139766656561888>

Answered By: Roland Puntaier
Categories: questions Tags:
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.