wtfpython icon indicating copy to clipboard operation
wtfpython copied to clipboard

Changing the type of an object at runtime

Open Jongy opened this issue 5 years ago • 3 comments

Surprisingly, Python allows you to change the __class__ attribute of an object:

In [1]: class A:
   ...:     def __init__(self):
   ...:         self.x = 5
   ...:         self.y = "hi"
   ...:
   ...: class B:
   ...:     def doit(self):
   ...:         print(self.x, self.y)
   ...:

In [2]: a = A()

In [3]: a.doit()
AttributeError: 'A' object has no attribute 'doit'

In [4]: a.__class__ = B

In [5]: a.doit()
5 hi

In [6]: a.__class__ = A

In [7]: a.doit()
AttributeError: 'A' object has no attribute 'doit'

This example was rather simple, but things grow more complicated when inheritance is involved. On the other hand, this proves that Python instances are mere dicts... :). With a few exceptions though.

Jongy avatar Sep 20 '20 22:09 Jongy

Wow, yes, it can get complicated with inheritance. This can also be a way to bypass any validations / attribute assignments that might happen in the __init__ method of class B.

class Backdoor:
    def __init__(self, target_class):
        self.__class__ = target_class

class B:
    def __init__(self, email, pwd):
        self.email = self.validate_email()
        self.pwd = self.validate_pwd()
    
    def validate_email(self, email):
        pass
    
    def validate_pwd(self, pwd):
        pass
In [18]: b = Backdoor(B)

In [19]: type(b)
Out[19]: __main__.B

satwikkansal avatar Sep 21 '20 06:09 satwikkansal

I can write an example snippet, and add some caveats of this "feature" (differences across Python versions, handling of __slots__, ...)

Jongy avatar Sep 21 '20 07:09 Jongy

Sure, that'd be awesome, looking forward to your PR :)

satwikkansal avatar Sep 21 '20 13:09 satwikkansal