560 likes | 679 Vues
Composability through Multiple Inheritance. A drama in Three Acts by Łukasz Langa. Act I: Exposition. where we meet our characters and the world they live in. Composability. Compositionality. Unix pipes. $ ps aux | grep celery | grep -v grep | awk '{print $2}' | xargs kill -9. Unix pipes.
E N D
Composabilitythrough Multiple Inheritance A drama in Three Actsby Łukasz Langa
Act I: Exposition where we meet our characters and the world they live in
Unix pipes $ ps aux | grep celery | grep -v grep |awk '{print $2}' | xargs kill -9
Unix pipes $ ps aux | grep celery | grep -v grep |awk '{print $2}' | xargs kill -9
Unix pipes $ command1 | command2 | command3 | ...
Joe Armstrong grep REGEX <file >matches
Composability Compositionality Quality
Act II: Rising Action where we learn how inheritance in Python works
Ifyouuseold-style classes You’re gonna have a badtime
Method Resolution Order >>> class A(object): ... pass ... ... >>> A.mro() [<class '__main__.A'>, <type 'object'>]
Method Resolution Order >>> class A(object): pass ... >>> class B(object): pass ... >>> class AB(A, B): pass ...
The Diamond Problem • AB
The Diamond ”Problem” >>> class A(object): pass ... >>> class B(object): pass ... >>> class AB(A, B): pass ... >>> AB.mro() [<class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>]
Method Resolution Order >>> class A(object): ... def say(self, what): ... return what + 'a’ ... >>> class B(object): ... def say(self, what): ... return what + 'b’ ... >>> class AB(A, B): pass ... >>> class BA(B, A): pass ... >>> AB().say('hello:') 'hello:a' >>> BA().say('hey:') 'hey:b'
CooperativeInheritance class A(object): def __init__(self, arg_a): self.arg_a = arg_a class B(object): def __init__(self, arg_b): self.arg_b = arg_b class AB(A, B): def __init__(self, arg_a, arg_b): # ???
CooperativeInheritance class A(object): def __init__(self, arg_a): self.arg_a = arg_a class B(object): def __init__(self, arg_b): self.arg_b = arg_b class AB(A, B): def __init__(self, arg_a, arg_b): A.__init__(self, arg_a) B.__init__(self, arg_b)
CooperativeInheritance class AB(A, B): def __init__(self, arg_a, arg_b): A.__init__(self, arg_a) B.__init__(self, arg_b) >>> ab= AB('a', 'b') >>> ab.arg_a 'a' >>> ab.arg_b 'b'
CooperativeInheritance class AB(A, B): def __init__(self, arg_a, arg_b): A.__init__(self, arg_a) B.__init__(self, arg_b) class C(D, AB): def __init__(self, arg_c): D.__init__(self) AB.__init__(self, *arg_c.split('.', 1)) >>> c=C('1.0') >>> c.arg_a '1' >>> c.arg_b '0’
CooperativeInheritance class AB(A, B): def __init__(self, arg_a, arg_b): A.__init__(self, arg_a) B.__init__(self, arg_b) class C(D, AB): def __init__(self, arg_c): D.__init__(self) AB.__init__(self, *arg_c.split('.', 1)) >>> C.mro() [<class '__main__.C'>, <class '__main__.D'>, <class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>]
CooperativeInheritance class AB(A, B): def __init__(self, arg_a, arg_b): A.__init__(self, arg_a) B.__init__(self, arg_b) class D(A): def __init__(self): A.__init__(self, 'd') class C(D, AB): def __init__(self, arg_c): D.__init__(self) AB.__init__(self, *arg_c.split('.', 1))
CooperativeInheritance A.__init__(self, arg_a) super(AB, self).__init__(arg_a) class AB(A, B): def __init__(self, arg_a, arg_b): A.__init__(self, arg_a) B.__init__(self, arg_b) class C(D, AB): def __init__(self, arg_c): D.__init__(self) AB.__init__(self, *arg_c.split('.', 1)) >>> C.mro() [<class '__main__.C'>, <class '__main__.D'>, <class '__main__.AB'>, <class '__main__.A'>, <class '__main__.B'>, <type 'object'>]
CooperativeInheritance class AB(A, B): def __init__(self, arg_a, arg_b): super(AB, self).__init__(arg_a=arg_a, arg_b=arg_b) class D(A): def __init__(self): super(D, self).__init__(arg_a='d') class C(D, AB): def __init__(self, arg_c): super(C, self).__init__( *arg_c.split('.', 1))
CooperativeInheritance class D(A): def __init__(self): super(D, self).__init__(arg_a='d') class C(D, AB): def __init__(self, arg_c): super(C, self).__init__( *arg_c.split('.', 1)) >>> C('1.0') Traceback (most recent call last): File "<input>", line 1, in <module> File "mrosuper.py", line 27, in __init__ super(C, self).__init__(*arg_c.split('.', 1)) TypeError: __init__() takes exactly 1 argument (3 given)
CooperativeInheritance class D(A): def __init__(self): super(D, self).__init__(arg_a='d') class C(D, AB): def __init__(self, arg_c): super(C, self).__init__( *arg_c.split('.', 1)) >>> C('1.0') Traceback (most recent call last): File "<input>", line 1, in <module> File "mrosuper.py", line 27, in __init__ super(C, self).__init__(*arg_c.split('.', 1)) TypeError: __init__() takes exactly 1 argument (3 given)
class A(object): def __init__(self, arg_a, **kwargs): self.arg_a = arg_a super(A, self).__init__(**kwargs) class B(object): def __init__(self, arg_b, **kwargs): self.arg_b = arg_b super(B, self).__init__(**kwargs) class AB(A, B): def __init__(self, arg_a, arg_b, **kwargs): super(AB, self).__init__(arg_a=arg_a, arg_b=arg_b, **kwargs) class D(A): def __init__(self, **kwargs): super(D, self).__init__(arg_a='d', **kwargs) class C(D, AB): def __init__(self, arg_c, **kwargs): super(C, self).__init__(*arg_c.split('.', 1), **kwargs)
class A(object): def __init__(self, arg_a, **kwargs): self.arg_a = arg_a super(A, self).__init__(**kwargs) class B(object): def __init__(self, arg_b, **kwargs): self.arg_b = arg_b super(B, self).__init__(**kwargs) class AB(A, B): def __init__(self, arg_a, arg_b, **kwargs): super(AB, self).__init__(arg_a=arg_a, arg_b=arg_b, **kwargs) class D(A): def __init__(self, **kwargs): super(D, self).__init__(arg_a='d', **kwargs) class C(D, AB): def __init__(self, arg_c, **kwargs): super(C, self).__init__(*arg_c.split('.', 1), **kwargs)
class A(object): def __init__(self, arg_a, **kwargs): self.arg_a = arg_a super(A, self).__init__(**kwargs) class B(object): def __init__(self, arg_b, **kwargs): self.arg_b = arg_b super(B, self).__init__(**kwargs) class AB(A, B): def __init__(self, arg_a, arg_b, **kwargs): super(AB, self).__init__(arg_a=arg_a, arg_b=arg_b, **kwargs) class D(A): def __init__(self, **kwargs): super(D, self).__init__(arg_a='d', **kwargs) class C(D, AB): def __init__(self, arg_c, **kwargs): super(C, self).__init__(*arg_c.split('.', 1), **kwargs)
class A(object): def __init__(self, arg_a, **kwargs): self.arg_a = arg_a super(A, self).__init__(**kwargs) class B(object): def __init__(self, arg_b, **kwargs): self.arg_b = arg_b super(B, self).__init__(**kwargs) class AB(A, B): def __init__(self, arg_a, arg_b, **kwargs): kwargs['arg_a'], kwargs['arg_b'] = arg_a, arg_b super(AB, self).__init__(**kwargs) class D(A): def __init__(self, **kwargs): kwargs['arg_a'] = 'd' super(D, self).__init__(**kwargs) class C(D, AB): def __init__(self, arg_c, **kwargs): kwargs['arg_a'], kwargs['arg_b'] = arg_c.split('.', 1) super(C, self).__init__(**kwargs)
It’sstilldifferent! >>> c=C('1.0') >>> c.arg_a u'd' >>> c.arg_b u'0'
CooperativeInheritance • Don’tomitsuper(C, self).__init__() evenifyourbaseclassisobject • Don’tassumeyouknowwhatargumentsyou’regoing to get • Don’tassumeyouknowwhatargumentsyoushould pass to super • always pass allargumentsyoureceivedon to super • ifclassescantakedifferingarguments, alwaysaccept**kwargs
Ifyou mix Class.__init__ and super() You’re gonna have a badtime
Mixins Not meant for instantiation on theirown Enhanceclasses with independent functionality Not a form of specialisation but collection of functionality Likeinterfaces with built-in implementation Veryreusableiforthogonal to the maintype
Interlude Django ORM inheritance model sucks
The Diamond Problem • M4