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