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'>
Subclassing
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
Method resolution order
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])
Super
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
Metaprogramming
__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?
- Everywhere
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
Objects and classes in Python
By Pavel Paulau
Objects and classes in Python
- 704