SortedList
class SimpleList:
def __init__(self, items):
self._items = list(items)
def add(self, item):
self._items.append(item)
def __getitem__(self, index):
return self._items[index]
def sort(self):
self._items.sort()
def __len__(self):
return len(self._items)
def __repr__(self):
return "SimpleList({!r})".format(self._items)
class SortedList(SimpleList):
def __init__(self, items=()): # optional sequence argument
super().__init__(items)
self.sort()
def add(self, item):
super().add(item)
self.sort()
def __repr__(self):
return "SortedList({!r})".format(list(self))
class IntList(SimpleList):
def __init__(self, items=()):
for x in items: self._validate(x)
super().__init__(items)
@staticmethod
def _validate(x):
if not isinstance(x, int):
raise TypeError('IntList only supports integer values.')
def add(self, item):
self._validate(item)
super().add(item)
def __repr__(self):
return "IntList({!r})".format(list(self))
class SortedIntList(IntList, SortedList):
def __repr__(self):
return "SortedIntList({!r})".format(list(self))
Useful built-in functions:
isinstance()
True
if object is a subclass of specified type# in repl
>>> isinstance(3, int)
#=> True
>>> isinstance('hello!', str)
#=> True
>>> x = []
>>> isinstance(x, (float, dict, list))
#=> True
issubclass()
class BaseClass:
def __init__(self, foo):
self.foo = foo
class SubClass(BaseClass):
def __init__(self, foo, bar):
super().__init__(foo)
self.bar = bar
class SubSubClass(SubClass):
def __init__(self, foo, bar, baz):
super().__init__(foo, bar)
self.baz = baz
issubclass(BaseClass, SubClass)
#=> False
issubclass(SubClass, BaseClass)
#=> True
issubclass(SubSubClass, BaseClass)
#=> True
Single inheritance
class SubClass(BaseClass):
pass
Multiple inheritance
class SubClass(Base1, Base2, . . .):
pass
__bases__
member of class objects
__mro__
member
mro()
to get the order as a list rather than a tupleWhen you call a method on an object in Python...
C3 - algorithm for calculating MRO in Python - ensures that - subclasses come before base classes - base class order from class definition is preserved - first two qualities are preserved no matter where you start in the inheritance graph - because of the above criteria, not all inheritance declarations are allowed!
# Example of multiple inheritance that violates C3
>>> class A:
... pass
>>> class B(A):
... pass
>>> class C(A):
... pass
>>> class D(B, A, C):
... pass
#=> TypeError: Cannot create a consistent method resolution order (MRO) for bases A, C
Explanation of above:
- since B
and C
both inherit from A
, B
and C
must both come before A
in any MRO
- however, since D
's base class declaration puts A
before C
, and since C3 also guarantees that base class declaration order is preserved, C3 cannot produce a consistent MRO, as it can't put A
both before and after C
super()
FunctionShort, albeit incomplete, definition: given a method resolution order and a class C
, super()
gives you an object which resolves methods using only the part of the MRO which comes after C
super()
returns a proxy object which routes method calls
two types of bound proxies:
super(base-class, derived-class)
base-class
is class objectderived-class
is subclass of first argument**Explanation using SortedList
example
>>> from sorted_list import *
>>> from pprint import pprint as pp
>>> pp(SortedIntList.mro())
#=> [
#=> <class 'sorted_list.SortedIntList'>,
#=> <class 'sorted_list.IntList'>,
#=> <class 'sorted_list.SortedList'>,
#=> <class 'sorted_list.SimpleList'>,
#=> <class 'object'>,
#=> ]
>>> super(SortedList, SortedIntList)
#=> <super: <class 'SortedList'>, <SortedIntList object>>
#=> --------- NAME RESOLUTION ---------
#=> 1. gets MRO for 'SortedIntList'
#=> 2. finds 'SortedList' in that MRO
#=> 3. takes everything after 'SortedList', giving us an MRO of containing just 'SimpleList' and 'object'
#=> 4. finds first class in that smaller MRO which has an 'add' method
>>> super(SortedList, SortedIntList).add
#=> <function SimpleList.add at 0x10436a050>
>>> super(SortedList, SortedIntList).add(4)
#=> TypeError: add() missing 1 required positional argument: 'item'
# ^^^^^^^^ raised because our proxy is bound to a class, not an instance
#
# However, we could invoke the method with our proxy if it's a static method or class method, like so:
>>> super(SortedList, SortedIntList)._validate(5)
>>> super(SortedList, SortedIntList)._validate('hello')
#=> TypeError: IntList only supports integer values.
super(class, instance-of-class)
class
is class objectinstance-of-class
must be instance of class
or any class derived from it>>> from sorted_list import *
>>> from pprint import pprint as pp
>>> pp(SortedIntList.mro())
#=> [
#=> <class 'sorted_list.SortedIntList'>,
#=> <class 'sorted_list.IntList'>,
#=> <class 'sorted_list.SortedList'>,
#=> <class 'sorted_list.SimpleList'>,
#=> <class 'object'>,
#=> ]
>>> sil = SortedIntList([5, 15, 10])
>>> sil
#=> SortedIntList([5, 10, 15])
>>> super(SortedList, sil)
#=> <super: <class 'SortedList'>, <SortedIntList object>>
>>> super(SortedList, sil).add(6)
>>> sil
#=> SortedIntList([5, 10, 15, 6]) --- Not quite 🙃
super()
Without ArgumentsWhen calling super()
without arguments, Python will sort out arguments for you
super(class-of-method, self)
super(class-of-method, class)
object
Class