0

I was reading the answers to Usage of __slots__? and I noticed that one of the examples is :

from collections import namedtuple
class MyNT(namedtuple('MyNT', 'bar baz')):
    """MyNT is an immutable and lightweight object"""
    __slots__ = ()

I saw that the __init__ of namedtuple was called when it was being subclassed by MyNT

I went ahead and tested it for myself and made this code which is my first attempt to understand such behavior:

class objectP():
    def __init__(self,name):
            print('object P inited')

class p(objectP('asd')):
    pass

I got an error stating that 4 objects were "passed" so I changed it to

class objectP():
    def __init__(self,*a):
            print('object P inited')

class p(objectP('asd')):
    pass

which now produces an output of

object P inited
object P inited
  1. What does the line of code above mean? Calling __init__ when subclassing?
  2. Why is object P inited printed twice?
Community
  • 1
  • 1
Carlos Miguel Colanta
  • 2,685
  • 3
  • 31
  • 49
  • 3
    `namedtuple('MyNT', 'bar baz')` returns a *class*, an instance of `type` not of `object`. If you want `p` to inherit from `objectP(...)`, then `objectP` should inherit from `type` not `object`. `namedtuple` itself is a function, not a class - see [the source code](https://hg.python.org/cpython/file/3.5/Lib/collections/__init__.py#l356). There's no `__init__` being called there. – jonrsharpe Nov 17 '16 at 22:39
  • 1
    Further to the above, a class inheriting from `type` is usually referred to as a [*"metaclass"*](http://stackoverflow.com/q/100003/3001761), and is a whole rabbit hole you almost certainly don't need to be going down! – jonrsharpe Nov 17 '16 at 22:48
  • Ohhh alright thank you! – Carlos Miguel Colanta Nov 17 '16 at 23:00
  • Wait... you can inherit from an instance?! – Jared Goguen Nov 17 '16 at 23:09
  • @JaredGoguen: If the instance is an instance of `type`, sure. Every class is an instance already! – Blckknght Nov 18 '16 at 00:01

1 Answers1

0

The example code you list at the top of your question works because the namedtuple function (which isn't actually a class itself) returns a class. You're inheriting from that returned class, not from namedtuple itself.

The same structure doesn't work when you use it in your other code because calling the ObjectP class returns an instance when you call it, and that instance isn't a class that can be inherited from.

You can write a function that returns a class, like namedtuple does. You can also write a class who's instances are other classes. That's called a "metatype" and in Python 3 metatypes need to inherit from the type class.

class MyMeta(type):
    def __new__(meta, name, bases, dct):
        print("creating a new type named", name)
        return super().__new__(meta, name, bases, dct)

class MyClass1(MyMeta("Base", (), {})): # you can inherit from an instance
    pass

class MyClass2(metaclass=MyMeta): # or use the special metaclass syntax
    pass

While metaclasses are neat, they can be a bit confusing if you're new to them. It gets a bit metaphysical, with type being an instance of itself (and a subclass of object for good measure). This stuff is the magical core of Python's type system, and you don't really need to understand it to use classes in ordinary ways.

Blckknght
  • 100,903
  • 11
  • 120
  • 169