questions (& answers) about object, type, builtin types, class, metaclass and __getattribute__

Steven D'Aprano steve+comp.lang.python at
Mon Aug 22 07:57:31 EDT 2011

Amirouche B. wrote:

> A) type vs object
> -----------------
> 1) object is the base object, it has no bases : len(object.__bases__)
> == 0

Correct, but for reference, a more direct test is:

object.__bases__ == ()

(no need for len).

> 2) every object in python inherit object :
> any_object_except_object.__bases__[-1] is object

Excluding old-style objects, I believe you are correct.

> 3) object's type is type : object.__class__ is type
> 4) type parent object is object : type.__bases__ == (object,)

The relationship between type and object is somewhat special, and needs to
be bootstrapped by the CPython virtual machine.

Arbitrary types (classes) inherit from object. That means the type is a
subclass of object:

>>> class K(object):pass
>>> issubclass(K, object)

What's less obvious is that types are themselves objects, and therefore are
instances of object:

>>> isinstance(K, object)

Since classes are objects, they have a type, namely ``type``.

This includes ``type`` itself:

    * type is an instance of object
    * object is an instance of type
    * type is a subclass of object
    * but object is NOT a subclass of type

> B) type vs metaclass
> --------------------
> 1) type is the first metaclass ?

Excluding old-style classes, yes, all custom classes (those you create with
the class statement) have a default metaclass of type.

> 2) type is its own metaclass : type(type) is type ?

Yes. Another bit of bootstrapping that the compiler does.

>>> type(type) is type

> 3) object's metaclass is type ?


> 4) other metaclasses *MUST* inherit type ?

No. Metaclasses can be anything that mimics type.

>>> def meta(name, bases, dict):
...     class X(object):
...             pass
...     return X
>>> class K(object):
...     __metaclass__ = meta
...     a = 1
>>> K
<class '__main__.X'>

They don't even need to return a type/class. Like decorators, they can
return anything.

>>> def meta(name, bases, dict):
...     return "spam"
>>> class K(object):
...     __metaclass__ = meta
>>> K

> 5) type(any_object) == last_metaclass_..., which is, most of the time,
> type ?

I'm not sure what you mean by "last_metaclass". But no. The type of an
object is its class:

type(42) => int
type("spam") => str
type(1.23) => float

However, the type of a class is *usually* type.

> C) type vs class
> ----------------
> 1) Type is the metaclass of most classes


> 2) The class statement::
>     class MyClass(object):
>         attribute = 1
>         def method(self):
>             pass
>    translates to::
>     MyClass = type('MyClass', (object,), {'attribute': 1, 'method':
> def method: pass })

Except that the syntax won't work, the idea is broadly correct.

> 3) Instantiation of any class ``MyClass(*args, **kwargs)`` translates
> to::
>     type(MyClass).__call__(MyClass, *args, **kwargs)

Like any function call, MyClass(...) becomes

type(MyClass).__call__(self, ...)

with self=MyClass. Since type(MyClass) is usually ``type``, that gives:

type.__call__(MyClass, ...)

>    This is due to __getattribute__ algorithm (see E)
> 4) It's in type.__call__ that happens calls to __new__ and __init__

If type were written in pure Python, it would probably look something like

class Type(object):
    # ...
    # other methods
    # ...
    def __call__(cls, *args, **kwargs):
        instance = cls.__new__(cls, *args, **kwargs)
        if isinstance(instance, cls):
            instance.__init__(*args, **kwargs)
        return instance

But see further on, for more complication.

Note that __new__ is special-cased as a staticmethod, hence it needs the
first argument to be passed directly.

> 5) 3) => classes are instance of type
> 6) Since type.__call__ is used to instantiate instance of instance of
> type
>    (rephrased: __call__ is used to instantiate classes) where is the
> code which
>    is executed when we write ``type(myobject)`` or ``type('MyClass',
> bases, attributes)``

You would need to check the C implementation of type, but if I were doing
this in pure Python, I'd have something like this:

class Type(object):
    def __call__(self, *args, **kwargs):
        if self is Type:
            if kwargs:
                raise TypeError('unexpected keyword arguments')
            # calling type(...) directly
            if len(args) == 1:
                # Single argument call, like type(x)
                return x.__class__
                # Like type(name, bases, dict)
                name, bases, dict = *args
                cls = Type.__new__(Type, name, bases, dict)
                if isinstance(cls, Type):
                    cls.__init__(name, bases, dict)
            # called from MyClass(...)
            # which becomes type(MyClass).__call__(MyClass, ...)
            # self here equals MyClass, which is an instance of type but 
            # not type itself
            instance = self.__new__(self, *args, **kwargs)
            if isinstance(instance, self):
                instance.__init__(*args, **kwargs)
            return instance
    def __new__(cls, *args):
        # magic to actually create a new type object

Note that this may not be how ``type`` actually does it. See the source
code, and I hope you have better luck reading it than I did!


More information about the Python-list mailing list