Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens when I inherit from an instance instead of a class in Python?

I'm just curious what will happen when I inherit an instance into a class.

So I tried:

class X:
    def __init__(self, x):
        self.x = x
    def print(self):
        print(self.x)

def inherit(obj):
    class Child(obj): # Line 20
        pass  # or maybe added functionality

    return Child

param = 5
x = X(param)
y = inherit(x) # Line 27
y.print()

I get (at least) the following error:

Traceback (most recent call last):
  File "/test.py", line 27, in <module>
    y = inherit(x)
  File "/test.py", line 20, in inherit
    class Child(obj):
TypeError: __init__() takes 2 positional arguments but 4 were given

I just wonder: Is inheriting an instance anything meaningful/useful or just plain nonsense?

(The question is a bit academic and specifically about the details of inheriting an instance. It's not about alternatives like object delegation or general design practices.)

like image 906
Michael Avatar asked Feb 21 '17 07:02

Michael


People also ask

Do Python classes need to inherit from object?

Inheritance is a required feature of every object oriented programming language. This means that Python supports inheritance, and as you'll see later, it's one of the few languages that supports multiple inheritance.

What does it mean when a class inherits from object Python?

Inheritance is a powerful feature in object oriented programming. It refers to defining a new class with little or no modification to an existing class. The new class is called derived (or child) class and the one from which it inherits is called the base (or parent) class.

Can you inherit a class in Python?

Inheritance allows us to define a class that inherits all the methods and properties from another class. Parent class is the class being inherited from, also called base class. Child class is the class that inherits from another class, also called derived class.

Can you inherit from 2 classes Python?

A class can be derived from more than one base class in Python, similar to C++. This is called multiple inheritance.


1 Answers

Classes are just like instances; they have a type. For instances that type is the class, but for classes, the type is called a metaclass. Inheriting from a class normally would call the metatype of the base class to produce a new class object (using type(base); for multiple bases restrictions apply). The standard metatype is the type object, but you can create your own metaclasses.

By inheriting from an instance, Python tries to create a new class by calling type(obj)(classname, bases, body_namespace). Since type(obj) is X and X.__init__() doesn't support those arguments, the call fails. However, there is nothing stopping you from making that part work!

>>> class X:
...     def __init__(self, classname, parents, namespace):
...         print('Creating {}{}'.format(classname, parents))
...
>>> class Child(X('X', (), {})): pass
...
Creating X()
Creating Child(<__main__.X object at 0x10372b4a8>,)
>>> Child
<__main__.X object at 0x10372b470>
>>> Child()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'X' object is not callable

Of course, type provides a lot more functionality that a class won't offer out of the box; there are a series of descriptors to provide attributes on a class that other parts of Python expect to exist. Your class would have to cover all of those instead; in the above sample output, you'll note that repr(Child) produces <__main__.X object at 0x...> rather than the expected <class '__main__.Child'>, and there is no __call__ method to produce instances for the class. So using an instance as a base class for another can work, you just have to put in the extra work to define all that expected functionality.

In the end, using an instance as a base class may be possible, but has no practical uses, not when any of the use-cases are already covered by metaclasses instead.

like image 199
Martijn Pieters Avatar answered Sep 20 '22 00:09

Martijn Pieters