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