Duck typing is essential to idiomatic Python. Since Python 3.8, typing.Protocol allows static type checkers to verify APIs based on duck typing, a.k.a. structural typing. Using it is key to write well annotated Pythonic code.
• 100+ pages about type hints, with many examples • New coverage of async/await, with examples in asyncio, FastAPI and Curio • OReilly.com: https://bit.ly/FluPy2e 3
To be considered “file-like”, the object supplied by the application must have a read() method that takes an optional size argument. 6 PEP 3333 – Python Web Server Gateway Interface 6
the literature. Here we assume that type is a set of values and a set of functions that one can apply to these values. 6 Guido van Rossum, Ivan Levkivskyi in PEP 483—The Theory of Type Hints 6
= float(i) >>> f 1e+23 >>> i == f False >>> from decimal import Decimal >>> Decimal(i) Decimal('100000000000000000000000') >>> Decimal(f) Decimal('99999999999999991611392') >>> i | 2 100000000000000000000002 >>> f | 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'float' and 'int'
= float(i) >>> f 1e+23 >>> i == f False >>> from decimal import Decimal >>> Decimal(i) Decimal('100000000000000000000000') >>> Decimal(f) Decimal('99999999999999991611392') >>> i | 2 100000000000000000000002 >>> f | 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'float' and 'int'
= float(i) >>> f 1e+23 >>> i == f False >>> from decimal import Decimal >>> Decimal(i) Decimal('100000000000000000000000') >>> Decimal(f) Decimal('99999999999999991611392') >>> i | 2 100000000000000000000002 >>> f | 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'float' and 'int'
a subtype of float because it implements the same interface, and adds extra methods —not because int is a subset of float* 11 * which it definitely isn’t
= float(i) >>> f 1e+23 >>> i == f False >>> from decimal import Decimal >>> Decimal(i) Decimal('100000000000000000000000') >>> Decimal(f) Decimal('99999999999999991611392') >>> i | 2 100000000000000000000002 >>> f | 2 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: unsupported operand type(s) for |: 'float' and 'int'
the literature. Here we assume that type is a set of values and a set of functions that one can apply to these values. 14 14 Guido van Rossum, Ivan Levkivskyi in PEP 483—The Theory of Type Hints
set of messages, a protocol, that defines the explicit communication to which that object can respond. 15 Dan Ingalls (Xerox PARC) in Design Principles Behind Smalltalk —BYTE Magazine, August 1981 15
duck, walks-like-a duck, etc, depending on exactly what subset of duck-like behavior you need... 18 Alex Martelli in comp-lang-python: ”polymorphism (was Re: Type checking in python?)” 2000-07-26 18
= cars ... def __iter__(self): ... for i in range(self.cars): ... yield f'car #{i+1}' ... >>> t = Train(4) >>> for car in t: ... print(car) ... car #1 car #2 car #3 car #4
what is the minimal interface expected, regardless of class hierarchies Benefits of using typing.Protocol 26 Support static analysis IDEs and linters can verify that an actual argument satisfies the protocol in the formal parameter Reduce coupling Client classes don’t need to subclass anything; just implement the protocol.
what is the minimal interface expected, regardless of class hierarchies Benefits of using typing.Protocol 27 Support static analysis IDEs and linters can verify that an actual argument satisfies the protocol in the formal parameter Reduce coupling Client classes don’t need to subclass anything; just implement the protocol.
what is the minimal interface expected, regardless of class hierarchies Benefits of using typing.Protocol 28 Support static analysis IDEs and linters can verify that an actual argument satisfies the protocol in the formal parameter Reduce coupling Client classes don’t need to subclass anything; just implement the protocol. This also makes testing easier.
a few things happened: • Edge cases were discovered where SupportsGreaterThan was needed • SupportsLessThan was replaced with SupportsGreaterThan, but this exposed symmetric bugs in cases that previously worked • Both were superseded by SupportsDunderLT and SupportsDunderGT • Almost all of their uses were replaced with a new type—the union of both of them—named SupportsRichComparison ◦ This means most functions that involve comparisons in the standard library now have type hints that accept objects implementing either < or >. No need to implement both. • For details, see: https://github.com/python/typeshed/blob/master/stdlib/_typeshed/__init__.pyi
Data Model and standard library Use typing.Protocol to build Pythonic APIs 65 Follow the Interface Segregation Principle Client code should not be forced to depend on methods it does not use Prefer narrow protocols Single method protocols should be the most common. Sometimes, two
Data Model and standard library to build Pythonic APIs 66 Follow the Interface Segregation Principle Client code should not be forced to depend on methods it does not use Prefer narrow protocols Single method protocols should be the most common. Sometimes, two Use typing.Protocol
Data Model and standard library to build Pythonic APIs 67 Follow the Interface Segregation Principle Client code should not be forced to depend on methods it does not use Prefer narrow protocols Single method protocols should be the most common. Sometimes, two methods. Rarely more. Use typing.Protocol
the limitations of static type checkers when submission to the tool would make the code worse or needlessly complicated. 70 70 Me, in Fluent Python Second Edition
the limitations of static type checkers when submission to the tool would make the code worse or needlessly complicated. 70 70 Me, in Fluent Python Second Edition @[email protected]