We know that this creates a class:
class X:
a = 1
And, in Python 3, due to new style classes, X
automatically inherits from object
, but in Python 2, it doesn't:
>>> X.__bases__ # Python 2
()
>>> X.__bases__ # Python 3
(<class 'object'>,)
And we also know that we can dynamically create such a class using type factory:
X = type("X",(object,), {"a":1})
name^ ^bases ^ "class" body
However, if we omit the object
in the bases tuple just as we did with class
syntax, we inherit from object
in python3 and, unexpectedly, in python2 as well:
X = type("X", ( ), {"a":1})
^ empty bases tuple
>>> X.__bases__ # Python 2
(<type 'object'>,)
>>> X.__bases__ # Python 3
(<class 'object'>,)
I expected X.__bases__
to be empty in python 2.
And it's not such a documented feature, I hardly find something about this on internet.
In fact, python's official documentation contradictory states that:
the bases tuple itemizes the base classes and becomes the
__bases__
attribute
But, as shown above, ()
still leads to (<type 'object'>,)
in Python 2, so it's not really true that it becomes the __bases__
attribute.
Can anyone explain this behavior?
Python Code can be dynamically imported and classes can be dynamically created at run-time. Classes can be dynamically created using the type() function in Python. The type() function is used to return the type of the object. The above syntax returns the type of object.
__class__ is an attribute on the object that refers to the class from which the object was created. a. __class__ # Output: <class 'int'> b. __class__ # Output: <class 'float'> After simple data types, let's now understand the type function and __class__ attribute with the help of a user-defined class, Human .
Python is an object-oriented (OO) programming language. Unlike some other object-oriented languages, Python doesn't force you to use the object-oriented paradigm exclusively: it also supports procedural programming with modules and functions, so you can select the best paradigm for each part of your program.
First, extract the class body as string. Second, create a class dictionary for the class namespace. Third, execute the class body to fill up the class dictionary. Finally, create a new instance of type using the above type() constructor.
Let's look into this Python 2 shell.
>>> class X1:
... a = 1
...
... X2 = type('X2', (), {'a': 1})
>>> type(X1)
0: <type 'classobj'>
>>> type(X2)
1: <type 'type'>
>>> import types
>>> assert types.ClassType is type(X1)
types.ClassType
is described as:
The type of user-defined old-style classes.
Basically type
in the new-style classes' default metaclass. If you want metaprogramming in the old style, you can use types.ClassType
in the same way.
>>> X3 = types.ClassType('X3', (), {'a': 1})
>>> X3.__bases__
2: ()
For the reference, excerpt from Python 2's New-style and classic classes:
Classes and instances come in two flavors: old-style (or classic) and new-style.
Up to Python 2.1 the concept of
class
was unrelated to the concept oftype
, and old-style classes were the only flavor available. For an old-style class, the statementx.__class__
provides the class of x, buttype(x)
is always<type 'instance'>
. This reflects the fact that all old-style instances, independent of their class, are implemented with a single built-in type, calledinstance
.New-style classes were introduced in Python 2.2 to unify the concepts of
class
andtype
. A new-style class is simply a user-defined type, no more, no less. If x is an instance of a new-style class, thentype(x)
is typically the same asx.__class__
(although this is not guaranteed -- a new-style class instance is permitted to override the value returned forx.__class__
).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With