Objects and Classes in Python
by Pavel Paulau
Objects, Classes and Types
Everything is an object
class A(object):
pass
>>> isinstance(A, object)
True
>>> isinstance(A(), object)
True
>>> isinstance(None, object)
True
There are special objects though
class A(object): pass
>>> isinstance(A, type)
True
>>> isinstance(A(), type)
False
>>> isinstance(int, type)
True
>>> isinstance(0, type)
False
<type 'type'>
>>> type(object)
<type 'type'>
>>> type(type)
<type 'type'>
>>> type(A)
<type 'type'>
>>> A.__class__
<type 'type'>
Class == type
class A(object):
pass
>>> A().__class__
<class '__main__.A'>
>>> type(A())
<class '__main__.A'>
Creating Classes at Runtime
A = type("A", (object, ), {})
B = type("B", (A, ), {})
>>> B.__name__
'B'
>>> B.__bases__
(<class '__main__.A'>,)
>>> type(B)
<type 'type'>
Simple inheritance example
class A(object):
def whoami(self):
return "I'm A"
class B(A):
pass
>>> B().whoami()
I'm A
A bit more complicated
class A(object):
def whoami(self):
return "I'm A"
class B(A):
def whoami(self):
return "I'm B"
class C(B, A):
pass
>>> C().whoami()
I'm B
And finally broken
class A(object):
def whoami(self):
return "I'm A"
class B(A):
def whoami(self):
return "I'm B"
class C(A, B): pass
TypeError: Error when calling the metaclass bases. Cannot create a consistent method resolution order (MRO) for bases A, B
CLASS HIERARCHY
object
/
/
A ------- B
\ /
\ /
C
MRO Linearization (c3)
A = type("A", (object, ), {})
B = type("B", (A, ), {})
C = type("C", (B, A), {})
L(C) = C, merge(L(B), L(A), [B, A])
Linearization of object
>>> object.__bases__
()
L(object) = object, merge([])
L(object) = object
>>> object.__mro__
(<type 'object'>,)
Linearization of A
A = type("A", (object, ), {})
L(A) = A, merge(L(object), [object])
L(A) = A, merge([object], [object])
L(A) = A, object
>>> A.__mro__
(<class '__main__.A'>, <type 'object'>)
Linearization of B
B = type("B", (A, ), {})
L(B) = B, merge(L(A), [A])
L(B) = B, merge([A, object],
[A])
L(B) = B, A, merge([object], [])
L(B) = B, A, object
Linearization of C
C = type("C", (B, A), {})
L(C)= C, merge(L(B), L(A), [B, A])
L(C) = C, merge([B, A, object],
[A, object],
[B, A])
L(C) = C, B, merge([A, object],
[A, object],
[A])
Linearization of C
L(C) = C, B, merge([A, object],
[A, object],
[A])
L(C) = C, B, A, merge([object],
[object],
[])
L(C) = C, B, A, object
BreaKing it again
A = type("A", (object, ), {})
B = type("B", (A, ), {})
>>> C = type("C", (A, B), {})
TypeError: Error when calling the metaclass bases. Cannot create a consistent method resolution order (MRO) for bases A, B
WHY it fails
C = type("C", (A, B), {})
L(C)= C, merge(L(A), L(B), [A, B])
L(C) = C, merge([A, object],
[B, A, object],
[A, B])
Delegating via super
class A(object):
def whoami(self):
return "A"
class B(A):
def whoami(self):
return "B based on " + \
super(B, self).whoami()
>>> B().whoami()
B based on A
Understanding super
class A(object):
def whoami(self):
return "A based on " + \
super(A, self).whoami()
class B(object):
def whoami(self):
return "B"
class C(A, B): # MRO: C, A, B, object
pass
>>> C().whoami()
A based on B
Really understanding super
class A(object):
@classmethod
def whoami(self): return "A"
class B(object):
@classmethod
def whoami(self): return "B"
class C(B, A): # MRO: C, B, A, object
pass
>>> super(B, C).whoami()
A
__metaclass__
class Meta(type):
def __new__(cls, name, bases, dct):
dct['attr'] = 'After'
return super(Meta, cls).__new__(
cls, name, bases, dct)
class A(object):
__metaclass__ = Meta
attr = 'Before'
>> A.attr
After
__metaclass__
class Meta(type):
def __new__(cls, name, bases, dct):
name = 'B'
return super(Meta, cls).__new__(
cls, name, bases, dct)
class A(object):
__metaclass__ = Meta
>> A
<class '__main__.B'>
Not necessarily a class
def meta(name, bases, dct):
dct['created_by'] = 'meta'
return type(name, bases, dct)
class A(object):
__metaclass__ = meta
>> A.created_by
meta
Where is it used?
class Model(object):
__metaclass__ = ModelBase
class ModelForm(BaseModelForm):
__metaclass__ = ModelFormMetaclass
class Widget(object):
__metaclass__ = MediaDefiningClass
-
Nowhere. This is really uncommon pattern
__new__
class A(object):
def __new__(cls):
return "String A"
def __init__(self):
1 / 0
>>> A()
String A
THANKS for your attention!
@pavelpaulau