Really great presentation! Another thing I took away from this: The change in the list super class is a perfect example, why -- especially in a dynamic language like python -- you want unit tests. The tests serve as a specification and document intent and make it easy to detect errors like this, where the code works perfectly well, but is behaving in a weird way. Rules of thumb don't solve problems in communication; documentation and discussions do (whereas well written tests are a form of both)
When a language has only single inheritance and no multiple inheritance, it's often advisable to use composition in order to roll your own multiple inheritance. In fact it is better to not use inheritance at all in that case.
I've found that, given that Robert C. Martin phrased it in terms of statically typed languages such as Java and C++, it can be non-trivial to interpret how the principles apply to Python. Applying the mental models of those other languages to Python is what causes the perils listed in this talk. For instance, Python has a strong culture of duck typing, where the type of an object is determined by what you can ask it to do rather than what it ‘inherits’, so there's no good reason to apply the Liskov Substitution Principle, and the idea of ‘is-a’, to Python subclasses. Instead, something is a subtype if it responds to methods that people will expect to do the same thing-so make sure that if you implement .__len__() on a class, it takes no further arguments and returns an integer that's semantically the ‘length’ of the instance.
To avoid the actual pitfalls of inheritance in Python, without the boilerplate code suggested in this talk, you just have to keep a few things in mind when you write a class that might ever be reused as a base class: `self` and `super()` don't refer to the class you're writing right now, but to (the ancestors of) your subclassers. For one, this means that when you call methods on them you'll probably want to forward arguments you don't recognise with `*args` and `**kwargs`. And secondly, whenever you do something with `self` you should think about whether the relation is part of the external interface, or an internal detail. In the talk, the relation between `.__init__()` and `.header` was part of the interface, but the relation between `.insert()` and `.insert_all()` should not have been. That last situation is the (sole) reason why double-underscore name mangling exists in Python: all it takes to resolve the problem is adding an internal reference to the base class with `__insert_all = insert_all`, and calling that instead of deferring to the bottom of the inheritance chain.
With these double-underscore class internal references, Python's inheritance supports the encapsulation that this talk lists as one of the advantages of composition. The other advantages listed are supported with multiple inheritance-although number 3, the ripple effect of interface changes, comes back to the Single-Responsibility Principle: if these interface changes actually involve the one concern for which the subclass chose to re-use the code of the base class, reflecting them may actually be desirable. For more explanation of how to use subclassing in Python effectively, I recommend a few talks by Raymond Hettinger, long-time core developer of the language: ‘Python's class development toolkit’, ‘The art of subclassing’ and ‘Super considered super!’.
@@devaggarwal1220 Not true. This is trivially done in many static languages. Most notably this can be done in 4 different ways in D languages. 1) Using builtin 'alias foo this;' in the class. 2) Using metaprograming and code generation using mixins, to automatically generate all forwarding methods, but it is somehow messy, complex and messy. 3) Using metaprograming, with templates and traits, even more messy imho. 4) Using 'opDispatch' forwarding and variadic templates and automatic types, which is more generic than 'alias foo this', and also pretty easy to do. About 3 lines in a simplest case. Usage of such classes can be streamlined by using interfaces if you want to use them in many places, or using generic template metaprograming in D, which would devirtualize most of the code, at the cost of extra code generated.
Awesome talk! A must watch for anyone doing OOP
Really great presentation! Another thing I took away from this: The change in the list super class is a perfect example, why -- especially in a dynamic language like python -- you want unit tests. The tests serve as a specification and document intent and make it easy to detect errors like this, where the code works perfectly well, but is behaving in a weird way. Rules of thumb don't solve problems in communication; documentation and discussions do (whereas well written tests are a form of both)
When a language has only single inheritance and no multiple inheritance, it's often advisable to use composition in order to roll your own multiple inheritance. In fact it is better to not use inheritance at all in that case.
Thanks for the talk! A very effective way to demonstrate one of the surprising disadvantage of inheritance.
Talk starts at 2:48 .
I think many of the disadvantages of OOP presented here most often occur when not following SOLID principles of OOP. (Robert Martin)
I've found that, given that Robert C. Martin phrased it in terms of statically typed languages such as Java and C++, it can be non-trivial to interpret how the principles apply to Python. Applying the mental models of those other languages to Python is what causes the perils listed in this talk. For instance, Python has a strong culture of duck typing, where the type of an object is determined by what you can ask it to do rather than what it ‘inherits’, so there's no good reason to apply the Liskov Substitution Principle, and the idea of ‘is-a’, to Python subclasses. Instead, something is a subtype if it responds to methods that people will expect to do the same thing-so make sure that if you implement .__len__() on a class, it takes no further arguments and returns an integer that's semantically the ‘length’ of the instance.
To avoid the actual pitfalls of inheritance in Python, without the boilerplate code suggested in this talk, you just have to keep a few things in mind when you write a class that might ever be reused as a base class: `self` and `super()` don't refer to the class you're writing right now, but to (the ancestors of) your subclassers.
For one, this means that when you call methods on them you'll probably want to forward arguments you don't recognise with `*args` and `**kwargs`. And secondly, whenever you do something with `self` you should think about whether the relation is part of the external interface, or an internal detail.
In the talk, the relation between `.__init__()` and `.header` was part of the interface, but the relation between `.insert()` and `.insert_all()` should not have been. That last situation is the (sole) reason why double-underscore name mangling exists in Python: all it takes to resolve the problem is adding an internal reference to the base class with `__insert_all = insert_all`, and calling that instead of deferring to the bottom of the inheritance chain.
With these double-underscore class internal references, Python's inheritance supports the encapsulation that this talk lists as one of the advantages of composition. The other advantages listed are supported with multiple inheritance-although number 3, the ripple effect of interface changes, comes back to the Single-Responsibility Principle: if these interface changes actually involve the one concern for which the subclass chose to re-use the code of the base class, reflecting them may actually be desirable. For more explanation of how to use subclassing in Python effectively, I recommend a few talks by Raymond Hettinger, long-time core developer of the language: ‘Python's class development toolkit’, ‘The art of subclassing’ and ‘Super considered super!’.
Great talk !
Gracias por el tuto
Nice talk! Wonder how the "forwardable" way from counter3.py work in a language like C++.
Also, great slides!
forwardable's implementation seems to solely rely on the dynamic behaviour of languages like Ruby and Python.
@@devaggarwal1220 Not true. This is trivially done in many static languages. Most notably this can be done in 4 different ways in D languages. 1) Using builtin 'alias foo this;' in the class. 2) Using metaprograming and code generation using mixins, to automatically generate all forwarding methods, but it is somehow messy, complex and messy. 3) Using metaprograming, with templates and traits, even more messy imho. 4) Using 'opDispatch' forwarding and variadic templates and automatic types, which is more generic than 'alias foo this', and also pretty easy to do. About 3 lines in a simplest case. Usage of such classes can be streamlined by using interfaces if you want to use them in many places, or using generic template metaprograming in D, which would devirtualize most of the code, at the cost of extra code generated.
Nice video