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

Steven D'Aprano steve+comp.lang.python at pearwood.info
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)
True


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

>>> isinstance(K, object)
True


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
True


> 3) object's metaclass is type ?

Yes.


> 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
'spam'



> 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

Yes.


> 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
this:

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__
            else:
                # 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)
        else:
            # 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!

http://hg.python.org/cpython/file/c8e73a89150e/Objects/typeobject.c




-- 
Steven




More information about the Python-list mailing list