34

On this python doc page it says:

Like its identity, an object’s type is also unchangeable.

And I try this script,

#!python3  

class Foo:
  num = 1
  pass

class Bar:
  num = 2
  pass

f1,f2= Foo(), Foo()

f2.__class__ = Bar
print( type(f1), type(f2), f1.num, f2.num )

The result shows:

<class '__main__.Foo'> <class '__main__.Bar'> 1 2

I think I changed the type of f2.
What's wrong, What am I missing?

3
  • Python is said to be for "adults" in the sense you know what you are doing and why. The question would be: "Why would you like to change the object type?" Commented Jul 19, 2016 at 13:33
  • 6
    Just out of curiosity @pepr Commented Aug 11, 2016 at 8:46
  • 2
    This is a fair reason. Dissecting it will make you to understand better the internals. :) Commented Aug 12, 2016 at 12:54

4 Answers 4

21

The footnotes one that page says:

[1] It is possible in some cases to change an object’s type, under certain controlled conditions. It generally isn’t a good idea though, since it can lead to some very strange behaviour if it is handled incorrectly.

If you try to change the __class__ of f2 to list:

f2.__class__ = list

A TypeError raised:

TypeError: __class__ assignment: only for heap types
Sign up to request clarification or add additional context in comments.

3 Comments

Oh, didnt notice that. So I DID changed the type. thank you:)
@dlutxx: Now try adding __slots__ to change the object memory layout. That can make even heap types incompatible.
Would expanding the nominal len of a ctypes array type after a successfull call to resize() be a valid use-case for this?
19

When and how to do this

Changing the type ("casting") makes sense if you want to add functionality to an object created by some code you cannot change.

Assume some statement obj = some_call_to_a_library() gives you an object of class A. You want it to have additional functionality, say, mymethod(). Then you could introduce a subclass MyA like this (Python 3 style):

class MyA(A):
    @classmethod
    def cast(cls, some_a: A):
        """Cast an A into a MyA."""
        assert isinstance(some_a, A)
        some_a.__class__ = cls  # now mymethod() is available
        assert isinstance(some_a, MyA)
        return some_a

    def mymethod(self):
        ...

and then write obj = MyA.cast(some_call_to_a_library()). If MyA relies on additional attributes, cast (which is a factory method) should create them.

I just did something like this when I needed a version of requests.Response that could persist and retrieve responses to/from a file.

2 Comments

Nice. How safe is it to do this though? Is it always safe if MyA inherits from A?
@101: Depends on what you mean by 'safe'. In duck-typing thinking, it is sensible to do it if you do it sensibly (that is, if the addition mymethod is coherent given the overall program design, e.g., there should be no mymethod in A). If you want to call that safe, it's safe.
3

I was asked this question by a colleague today. He had a parent class that wanted to promote itself automatically to be one of its children based on an input at init time. The following script worked as a proof of concept:

class ClassB(object):

    def __init__(self):
        self.__class__ = ClassA

    def blah2(self,t):
        print('I give you',t)
        return 'You are welcome'

class ClassA(ClassB):

   def blah(self, t):
       print('you gave me',t)
       return 'Thankyou'



a = ClassB()
print(type(a))
print(a.blah('sausage'))
print(a.blah2('cabbage'))

The result shows:

<class '__main__.ClassA'>
you gave me sausage
Thankyou
I give you cabbage
You are welcome

Which shows that both the parent and child functions are now available to A.

1 Comment

Promoting to a child class inside __init__ is likely the most valid reason to do this (and I can't think of any other good ones). However, I am still not sure if this cannot backfire somehow.
2

Old question but here's a technique for safely extending the type of a python object.

def extendObject(obj):
    # Our extended type is explicitly derived from our existing type.
    class Extended(obj.__class__):
        # Whatever extensions you want to make.
        def someMethod(self):
            pass
    # Change the type of the object.
    obj.__class__ = Extended
    return obj

2 Comments

What are the benefits of this? Aren't you creating a new class (as in "type") for every object casted?
@ZaarHai the simple past and past participle of "cast" is "cast"

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.