More on this book
Community
Kindle Notes & Highlights
Started reading
May 16, 2024
Sequence, Mapping, Set These are the main immutable collection types, and each...
This highlight has been truncated due to consecutive passage length restrictions.
MappingView In Python 3, the objects returned from the mapping methods .items(), .keys(), and .values() implement the interfaces defined in ItemsView, KeysView, and ValuesView, respectively. The first two also implement the rich interface of Set,
Iterator Note that iterator subclasses Iterable.
Callable, Hashable These are not collections, but collections.abc was the first package to define ABCs in the standard library, and these two were deemed important enough to be included. They support type checking objects that must be callable or hashable.
If isinstance(obj, Hashable) returns True, that only means that the class of obj implements or inherits __hash__. But if obj is a tuple containing unhashable items, then obj is not hashable, despite the positive result of the isinstance check.
the most accurate way to determine if an instance is hashable: call hash(obj). That call will raise TypeError if obj is not hashable.
The only reliable way to determine whether an object is iterable is to call iter(obj).
ABCs, like descriptors and metaclasses, are tools for building frameworks. Therefore, only a small minority of Python developers can create ABCs without imposing unreasonable limitations and needless work on fellow programmers.
Concrete methods in an ABC must rely only on the interface defined by the ABC (i.e., other concrete or abstract methods or properties of the ABC).
An abstract method can actually have an implementation. Even if it does, subclasses will still be forced to override it, but they will be able to invoke the abstract method with super(), adding functionality to it instead of implementing from scratch.
Besides the ABC base class, and the @abstractmethod decorator, the abc module defines the @abstractclassmethod, @abstractstaticmethod, and @abstractproperty decorators. However, these last three were deprecated in Python 3.3, when it became possible to stack decorators on top of @abstractmethod, making the others redundant. For example, the preferred way to declare an abstract class method is: class MyABC(abc.ABC): @classmethod @abc.abstractmethod def an_abstract_classmethod(cls, ...): pass
When abstractmethod() is applied in combination with other method descriptors, it should be applied as the innermost decorator…
Subclassing an ABC or registering with an ABC are both explicit ways of making our classes pass issubclass checks—as well as isinstance checks, which also rely on issubclass.
ABCs are mostly used with nominal typing. When a class Sub explicitly inherits from AnABC, or is registered with AnABC, the name of AnABC is linked to the Sub class—and that’s how at runtime, issubclass(AnABC, Sub) returns True.
In contrast, structural typing is about looking at the structure of an object’s public interface to determine its type: an object is consistent-with a type if it implements the methods defined in the type.
The __subclasshook__ for Sized checks whether the class argument has an attribute named __len__. If it does, then it is considered a virtual subclass of Sized.
when defining a typing.Protocol subclass, you can use the @runtime_checkable decorator to make that protocol support isinstance/issubclass checks at runtime. This works because typing.Protocol is an ABC, therefore it supports the __subclasshook__
Thanks to the @runtime_checkable class decorator applied to SupportsComplex, that protocol can also be used with isinstance
The built-in complex type and the NumPy complex64 and complex128 types are all registered as virtual subclasses of numbers.Complex,
If you’re using an external type checker, there is one advantage of explict isinstance checks: when you write an if statement where the condition is isinstance(o, MyType), then Mypy can infer that inside the if block, the type of the o object is consistent-with MyType.
Very often at runtime, duck typing is the best approach for type checking: instead of calling isinstance or hasattr, just try the operations you need to do on the object, and handle exceptions as needed.
After 10 years of experience with static duck typing in Go, it is clear that narrow protocols are more useful—often such protocols have a single method, rarely more than a couple of methods.
sometimes you see a protocol defined near the function that uses it—that is, defined in “client code” instead of being defined in a library. This is makes it easy to create new types to call that function, which is good for extensibility and testing with mocks.
“Clients should not be forced to depend upon interfaces that they do not use.”
Every protocol must explicitly name typing.Protocol as one of its base classes in addition to the protocol we are extending. This is different from the way inheritance works in Python.
Sadly, the numeric tower was not designed for static type checking.
The numbers ABCs are fine for runtime type checking, but unsuitable for static typing.
The numeric static protocols SupportsComplex, SupportsFloat, etc. work well for static typing, but are unreliable for runtime type checking when complex numbers are involved.
This is a main theme of Fluent Python: highlighting the fundamental idioms of the language, so your code is concise, effective, and readable—for a fluent Pythonista.
Multiple inheritance is the ability of a class to have more than one base class. C++ supports it; Java and C# don’t. Many consider multiple inheritance more trouble than it’s worth.
As of 2021, there is a significant backlash against overuse of inheritance in general—not only multiple inheritance—because superclasses and subclasses are tightly coupled. Tight coupling means that changes to one part of the program may have unexpected and far-reaching effects in other parts, making systems brittle and hard to understand.
If you learned object-oriented programming in Java, you may recall that a Java constructor method automatically calls the no-argument constructor of the superclass. Python doesn’t do this.
super implements logic to handle class hierarchies with multiple inheritance.
Both arguments of super are now optional. The Python 3 bytecode compiler automatically provides them by inspecting the surrounding context when super() is invoked in a method.
type The start of the search path for the superclass implementing the desired method. By default, it is the class that owns the method where the super() call appears.
object_or_type The object (for instance method calls) or class (for class method calls) to be the receiver of the method call. By default, it is self if the ...
This highlight has been truncated due to consecutive passage length restrictions.
the super() call returns a dynamic proxy object that finds a method (such as __setitem__ in the example) in a superclass of the type parameter, and binds it to the object_or_type, so that we don’t need to pass the receiver (self) explicitly when invoking the method.
the code of the built-ins (written in C) usually does not call methods overridden by user-defined classes.
Officially, CPython has no rule at all for when exactly overridden method of subclasses of built-in types get implicitly called or not. As an approximation, these methods are never called by other built-in methods of the same object. For example, an overridden __getitem__() in a subclass of dict will not be called by e.g. the built-in get() method.
This is what is called “late binding,” which Alan Kay—of Smalltalk fame—considers a key feature of object-oriented programming: in any call of the form x.method(), the exact method to be called must be determined at runtime, based on the class of the receiver x.
Subclassing built-in types like dict or list or str directly is error-prone because the built-in methods mostly ignore user-defined overrides. Instead of subclassing the built-ins, derive your classes from the collections module using UserDict, UserList, and UserString, which are designed to be easily extended.
Any language implementing multiple inheritance needs to deal with potential naming conflicts when superclasses implement a method by the same name. This is called the “diamond problem,”
The MRO is computed by a published algorithm called C3.
When a method calls super(), it is a cooperative method. Cooperative methods enable cooperative multiple inheritance. These terms are intentional: in order to work, multiple inheritance in Python requires the active cooperation of the methods involved.
A mixin class is designed to be subclassed together with at least one other class in a multiple inheritance arrangement. A mixin is not supposed to be the only base class of a concrete class, because it does not provide all the functionality for a concrete object, but only adds or customizes the behavior of child or sibling classes.
Since every method ot UpperCaseMixin calls super(), this mixin depends on a sibling class that implements or inherits methods with the same signature. To make its contribution, a mixin usually needs to appear before other classes in the MRO of a subclass that uses it. In practice, that means mixins must appear first in the tuple of base classes in a class declaration.
Once you get comfortable with inheritance, it’s too easy to overuse it. Placing objects in a neat hierarchy appeals to our sense of order; programmers do it just for fun.
Favoring composition leads to more flexible designs.
subclassing is a form of tight coupling, and tall inheritance trees tend to be brittle.
Composition and delegation can replace the use of mixins to make behaviors available to different classes, but cannot replace the use of interface inheritance to define a hierarchy of types.