2

I am using python and have an object, that object has a method. I am looking for a simple way, to replace the entire object from within that function.

E.g

class a():
 def b(self):
 self = other_object

How can you do that?

Thanks

YatShan
4432 gold badges8 silver badges22 bronze badges
asked Feb 10, 2020 at 8:31
11
  • 5
    This is impossible, and smells like an XY problem. Why do you think you need to do this? Commented Feb 10, 2020 at 8:34
  • self is the instance of a, I don't think you can replace it. Why do you want to do it anyway? Commented Feb 10, 2020 at 8:35
  • What's wrong with what you have? Commented Feb 10, 2020 at 8:35
  • You can have a class variable to store the other object and implement __getattr__ and __setattr__ to explicitly use the other object to get and set attributes. Commented Feb 10, 2020 at 8:45
  • You dont need a method for it. You just do obj = other_obj in any part of your code. If you still think you need it, it is impossible. Commented Feb 10, 2020 at 8:47

2 Answers 2

1

You use a proxy/facade object to hold a reference to the actual object, the self if you wish and that proxy (better term than Facade, but not changing my code now) is what the rest of your codebase sees. However, any attribute/method access is forwarded on to the actual object, which is swappable.

Code below should give you a rough idea. Note that you need to be careful about recursion around __the_instance, which is why I am assigning to __dict__ directly. Bit messy, since it's been a while I've written code that wraps getattr and setattr entirely.

class Facade:
 def __init__(self, instance):
 self.set_obj(instance)
 def set_obj(self, instance):
 self.__dict__["__theinstance"] = instance
 def __getattr__(self, attrname):
 if attrname == "__theinstance":
 return self.__dict__["__theinstance"] 
 return getattr(self.__dict__["__theinstance"], attrname)
 def __setattr__(self, attrname, value):
 if attrname == "__theinstance":
 self.set_obj(value)
 return setattr(self.__dict__["__theinstance"], attrname, value)
class Test:
 def __init__(self, name, cntr):
 self.name = name 
 self.cntr = cntr
 def __repr__(self):
 return "%s[%s]" % (self.__class__.__name__, self.__dict__)
obj1 = Test("first object", 1)
obj2 = Test("second", 2)
obj2.message = "greetings"
def pretend_client_code(facade):
 print(id(facade), facade.name, facade.cntr, getattr(facade, "value", None))
facade = Facade(obj1)
pretend_client_code(facade)
facade.set_obj(obj2)
pretend_client_code(facade)
facade.value = 3
pretend_client_code(facade)
facade.set_obj(obj1)
pretend_client_code(facade)

output:

4467187104 first object 1 None
4467187104 second 2 None
4467187104 second 2 3
4467187104 first object 1 None

So basically, the "client code" always sees the same facade object, but what it is actually accessing depends on what your equivalent of def b is has done.

Facade has a specific meaning in Design Patterns terminology and it may not be really applicable here, but close enough. Maybe Proxy would have been better.

Note that if you want to change the class on the same object, that is a different thing, done through assigning self.__class__ . For example, say an RPG game with an EnemyClass who gets swapped to DeadEnemyClass once killed: self.__class__ = DeadEnemyClass

answered Feb 11, 2020 at 22:22
Sign up to request clarification or add additional context in comments.

4 Comments

@kaya3 yes, you can. whether or not that's a good idea is a different question, but there is plenty of code that does it. For example, it's one approach to building State Machines. If you go that route, only use classes that are designed to be used that way - having always the same method names and attribute names, just with different contents.
My mistake; I have tried it in the past out of curiosity and seen an error, but apparently it's possible if both the original and new class are user-defined classes without __slots__.
ah, see, we both learned something. I didn't realize __slots__ would interfere, but I can't say I'm surprised they do. It should be approached with a lot of caution, but it can replace a fair bit of factory design pattern boiler plate, at the cost of potentially being very surprising to the user of the code when their instances change classes on the fly. Definitely be mindful of Liskov Substitution Principle - ideally they would all share the same immediate ancestor class - and use only when appropriate.
I think it could only be safe when the new class is a subclass of the old one, or no other reference to the instance exists. Otherwise e.g. a List[A] might find itself containing something not of type A, through no fault of its own.
0

You can't directly do that. What you can do is save it as an instance variable.

class A():
 def __init__(self, instance=None):
 self.instance = val or self
 # yes, you can make it a property as well.
 def set_val(self, obj):
 self.instance = obj
 def get_val(self):
 return self.instance

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.

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

answered Feb 10, 2020 at 9:11

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.