ch15: draft examples
This commit is contained in:
parent
5312d4f824
commit
1689eec623
41
15-more-types/cafeteria/cafeteria.py
Normal file
41
15-more-types/cafeteria/cafeteria.py
Normal file
@ -0,0 +1,41 @@
|
||||
from typing import TypeVar, Generic
|
||||
|
||||
|
||||
class Beverage:
|
||||
"""Any beverage"""
|
||||
|
||||
|
||||
class Juice(Beverage):
|
||||
"""Any fruit juice"""
|
||||
|
||||
|
||||
class OrangeJuice(Juice):
|
||||
"""Delicious juice Brazilian oranges"""
|
||||
|
||||
|
||||
class Coak(Beverage):
|
||||
"""Secret formula with lots of sugar"""
|
||||
|
||||
|
||||
BeverageT = TypeVar('BeverageT', bound=Beverage)
|
||||
JuiceT = TypeVar('JuiceT', bound=Juice)
|
||||
|
||||
|
||||
class BeverageDispenser(Generic[BeverageT]):
|
||||
|
||||
beverage: BeverageT
|
||||
|
||||
def __init__(self, beverage: BeverageT) -> None:
|
||||
self.beverage = beverage
|
||||
|
||||
def dispense(self) -> BeverageT:
|
||||
return self.beverage
|
||||
|
||||
|
||||
class JuiceDispenser(BeverageDispenser[JuiceT]):
|
||||
pass
|
||||
|
||||
|
||||
class Cafeteria:
|
||||
def __init__(self, dispenser: BeverageDispenser[JuiceT]):
|
||||
self.dispenser = dispenser
|
23
15-more-types/cafeteria/cafeteria_demo.py
Normal file
23
15-more-types/cafeteria/cafeteria_demo.py
Normal file
@ -0,0 +1,23 @@
|
||||
from cafeteria import (
|
||||
Cafeteria,
|
||||
BeverageDispenser,
|
||||
JuiceDispenser,
|
||||
Juice,
|
||||
OrangeJuice,
|
||||
Coak,
|
||||
)
|
||||
|
||||
orange = OrangeJuice()
|
||||
|
||||
orange_dispenser: JuiceDispenser[OrangeJuice] = JuiceDispenser(orange)
|
||||
|
||||
juice: Juice = orange_dispenser.dispense()
|
||||
|
||||
soda = Coak()
|
||||
|
||||
## Value of type variable "JuiceT" of "JuiceDispenser" cannot be "Coak"
|
||||
# soda_dispenser = JuiceDispenser(soda)
|
||||
|
||||
soda_dispenser = BeverageDispenser(soda)
|
||||
|
||||
arnold_hall = Cafeteria(soda_dispenser)
|
16
15-more-types/collections_variance.py
Normal file
16
15-more-types/collections_variance.py
Normal file
@ -0,0 +1,16 @@
|
||||
from collections.abc import Collection, Sequence
|
||||
|
||||
col_int: Collection[int]
|
||||
|
||||
seq_int: Sequence[int] = (1, 2, 3)
|
||||
|
||||
## Incompatible types in assignment
|
||||
## expression has type "Collection[int]"
|
||||
## variable has type "Sequence[int]"
|
||||
# seq_int = col_int
|
||||
|
||||
col_int = seq_int
|
||||
|
||||
## List item 0 has incompatible type "float"
|
||||
## expected "int"
|
||||
# col_int = [1.1]
|
@ -1,5 +1,4 @@
|
||||
import random
|
||||
from typing import Iterable, TYPE_CHECKING, List
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from erp import EnterpriserRandomPopper
|
||||
import randompop
|
||||
@ -9,7 +8,6 @@ def test_issubclass() -> None:
|
||||
assert issubclass(EnterpriserRandomPopper, randompop.RandomPopper)
|
||||
|
||||
|
||||
|
||||
def test_isinstance_untyped_items_argument() -> None:
|
||||
items = [1, 2, 3]
|
||||
popper = EnterpriserRandomPopper(items) # [int] is not required
|
59
15-more-types/gen_contra.py
Normal file
59
15-more-types/gen_contra.py
Normal file
@ -0,0 +1,59 @@
|
||||
"""
|
||||
In ``Generator[YieldType, SendType, ReturnType]``,
|
||||
``SendType`` is contravariant.
|
||||
The other type variables are covariant.
|
||||
|
||||
This is how ``typing.Generator`` is declared::
|
||||
|
||||
class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]):
|
||||
|
||||
(from https://docs.python.org/3/library/typing.html#typing.Generator)
|
||||
|
||||
"""
|
||||
|
||||
from typing import Generator
|
||||
|
||||
|
||||
# Generator[YieldType, SendType, ReturnType]
|
||||
|
||||
def gen_float_take_int() -> Generator[float, int, str]:
|
||||
received = yield -1.0
|
||||
while received:
|
||||
received = yield float(received)
|
||||
return 'Done'
|
||||
|
||||
|
||||
def gen_float_take_float() -> Generator[float, float, str]:
|
||||
received = yield -1.0
|
||||
while received:
|
||||
received = yield float(received)
|
||||
return 'Done'
|
||||
|
||||
|
||||
def gen_float_take_complex() -> Generator[float, complex, str]:
|
||||
received = yield -1.0
|
||||
while received:
|
||||
received = yield abs(received)
|
||||
return 'Done'
|
||||
|
||||
# Generator[YieldType, SendType, ReturnType]
|
||||
|
||||
g0: Generator[float, float, str] = gen_float_take_float()
|
||||
|
||||
g1: Generator[complex, float, str] = gen_float_take_float()
|
||||
|
||||
## Incompatible types in assignment
|
||||
## expression has type "Generator[float, float, str]"
|
||||
## variable has type "Generator[int, float, str]")
|
||||
# g2: Generator[int, float, str] = gen_float_take_float()
|
||||
|
||||
|
||||
# Generator[YieldType, SendType, ReturnType]
|
||||
|
||||
g3: Generator[float, int, str] = gen_float_take_float()
|
||||
|
||||
## Incompatible types in assignment
|
||||
## expression has type "Generator[float, float, str]"
|
||||
## variable has type "Generator[float, complex, str]")
|
||||
## g4: Generator[float, complex, str] = gen_float_take_float()
|
||||
|
47
15-more-types/petbox/petbox.py
Normal file
47
15-more-types/petbox/petbox.py
Normal file
@ -0,0 +1,47 @@
|
||||
from typing import TypeVar, Generic, Any
|
||||
|
||||
|
||||
class Pet:
|
||||
"""Domestic animal kept for companionship."""
|
||||
|
||||
|
||||
class Dog(Pet):
|
||||
"""Canis familiaris"""
|
||||
|
||||
|
||||
class Cat(Pet):
|
||||
"""Felis catus"""
|
||||
|
||||
|
||||
class Siamese(Cat):
|
||||
"""Cat breed from Thailand"""
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
class Box(Generic[T]):
|
||||
def put(self, item: T) -> None:
|
||||
self.contents = item
|
||||
|
||||
def get(self) -> T:
|
||||
return self.contents
|
||||
|
||||
|
||||
T_contra = TypeVar('T_contra', contravariant=True)
|
||||
|
||||
|
||||
class InBox(Generic[T_contra]):
|
||||
def put(self, item: T) -> None:
|
||||
self.contents = item
|
||||
|
||||
|
||||
T_co = TypeVar('T_co', covariant=True)
|
||||
|
||||
|
||||
class OutBox(Generic[T_co]):
|
||||
def __init__(self, contents: Any):
|
||||
self.contents = contents
|
||||
|
||||
def get(self) -> Any:
|
||||
return self.contents
|
42
15-more-types/petbox/petbox_demo.py
Normal file
42
15-more-types/petbox/petbox_demo.py
Normal file
@ -0,0 +1,42 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from petbox import *
|
||||
|
||||
|
||||
cat_box: Box[Cat] = Box()
|
||||
|
||||
si = Siamese()
|
||||
|
||||
cat_box.put(si)
|
||||
|
||||
animal = cat_box.get()
|
||||
|
||||
#if TYPE_CHECKING:
|
||||
# reveal_type(animal) # Revealed: petbox.Cat*
|
||||
|
||||
|
||||
################### Covariance
|
||||
|
||||
out_box: OutBox[Cat] = OutBox(Cat())
|
||||
|
||||
out_box_si: OutBox[Siamese] = OutBox(Siamese())
|
||||
|
||||
## Incompatible types in assignment
|
||||
## expression has type "OutBox[Cat]"
|
||||
# variable has type "OutBox[Siamese]"
|
||||
# out_box_si = out_box
|
||||
|
||||
out_box = out_box_si
|
||||
|
||||
################### Contravariance
|
||||
|
||||
in_box: InBox[Cat] = InBox()
|
||||
|
||||
in_box_si: InBox[Siamese] = InBox()
|
||||
|
||||
in_box_si = in_box
|
||||
|
||||
## Incompatible types in assignment
|
||||
## expression has type "InBox[Siamese]"
|
||||
## variable has type "InBox[Cat]"
|
||||
# in_box = in_box_si
|
@ -4,7 +4,7 @@ from typing import Iterable, TYPE_CHECKING
|
||||
from randompick_generic import GenericRandomPicker
|
||||
|
||||
|
||||
class LottoPicker():
|
||||
class LottoPicker:
|
||||
def __init__(self, items: Iterable[int]) -> None:
|
||||
self._items = list(items)
|
||||
random.shuffle(self._items)
|
@ -1,4 +1,4 @@
|
||||
from typing import Protocol, TypeVar, runtime_checkable, Any
|
||||
from typing import Protocol, runtime_checkable, Any
|
||||
|
||||
|
||||
@runtime_checkable
|
@ -3,7 +3,7 @@ import random
|
||||
from typing import Any, Iterable, TYPE_CHECKING
|
||||
|
||||
|
||||
class SimplePopper():
|
||||
class SimplePopper:
|
||||
def __init__(self, items: Iterable) -> None:
|
||||
self._items = list(items)
|
||||
random.shuffle(self._items)
|
32
15-more-types/typeddict/books.py
Normal file
32
15-more-types/typeddict/books.py
Normal file
@ -0,0 +1,32 @@
|
||||
# tag::BOOKDICT[]
|
||||
from typing import TypedDict, List
|
||||
import json
|
||||
|
||||
class BookDict(TypedDict):
|
||||
isbn: str
|
||||
title: str
|
||||
authors: List[str]
|
||||
pagecount: int
|
||||
# end::BOOKDICT[]
|
||||
|
||||
# tag::TOXML[]
|
||||
AUTHOR_EL = '<AUTHOR>{}</AUTHOR>'
|
||||
|
||||
def to_xml(book: BookDict) -> str: # <1>
|
||||
elements: List[str] = [] # <2>
|
||||
for key, value in book.items():
|
||||
if isinstance(value, list): # <3>
|
||||
elements.extend(
|
||||
AUTHOR_EL.format(n) for n in value) # <4>
|
||||
else:
|
||||
tag = key.upper()
|
||||
elements.append(f'<{tag}>{value}</{tag}>')
|
||||
xml = '\n\t'.join(elements)
|
||||
return f'<BOOK>\n\t{xml}\n</BOOK>'
|
||||
# end::TOXML[]
|
||||
|
||||
# tag::FROMJSON[]
|
||||
def from_json(data: str) -> BookDict:
|
||||
whatever: BookDict = json.loads(data) # <1>
|
||||
return whatever # <2>
|
||||
# end::FROMJSON[]
|
32
15-more-types/typeddict/books_any.py
Normal file
32
15-more-types/typeddict/books_any.py
Normal file
@ -0,0 +1,32 @@
|
||||
# tag::BOOKDICT[]
|
||||
from typing import TypedDict, List
|
||||
import json
|
||||
|
||||
class BookDict(TypedDict):
|
||||
isbn: str
|
||||
title: str
|
||||
authors: List[str]
|
||||
pagecount: int
|
||||
# end::BOOKDICT[]
|
||||
|
||||
# tag::TOXML[]
|
||||
AUTHOR_EL = '<AUTHOR>{}</AUTHOR>'
|
||||
|
||||
def to_xml(book: BookDict) -> str: # <1>
|
||||
elements: List[str] = [] # <2>
|
||||
for key, value in book.items():
|
||||
if isinstance(value, list): # <3>
|
||||
elements.extend(AUTHOR_EL.format(n)
|
||||
for n in value)
|
||||
else:
|
||||
tag = key.upper()
|
||||
elements.append(f'<{tag}>{value}</{tag}>')
|
||||
xml = '\n\t'.join(elements)
|
||||
return f'<BOOK>\n\t{xml}\n</BOOK>'
|
||||
# end::TOXML[]
|
||||
|
||||
# tag::FROMJSON[]
|
||||
def from_json(data: str) -> BookDict:
|
||||
whatever = json.loads(data) # <1>
|
||||
return whatever # <2>
|
||||
# end::FROMJSON[]
|
20
15-more-types/typeddict/demo_books.py
Normal file
20
15-more-types/typeddict/demo_books.py
Normal file
@ -0,0 +1,20 @@
|
||||
from books import BookDict
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
def demo() -> None: # <1>
|
||||
book = BookDict( # <2>
|
||||
isbn='0134757599',
|
||||
title='Refactoring, 2e',
|
||||
authors=['Martin Fowler', 'Kent Beck'],
|
||||
pagecount=478
|
||||
)
|
||||
authors = book['authors'] # <3>
|
||||
if TYPE_CHECKING: # <4>
|
||||
reveal_type(authors) # <5>
|
||||
authors = 'Bob' # <6>
|
||||
book['weight'] = 4.2
|
||||
del book['title']
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
demo()
|
23
15-more-types/typeddict/demo_not_book.py
Normal file
23
15-more-types/typeddict/demo_not_book.py
Normal file
@ -0,0 +1,23 @@
|
||||
from books import to_xml, from_json
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
def demo() -> None:
|
||||
NOT_BOOK_JSON = """
|
||||
{"title": "Andromeda Strain",
|
||||
"flavor": "pistachio",
|
||||
"authors": true}
|
||||
"""
|
||||
not_book = from_json(NOT_BOOK_JSON) # <1>
|
||||
if TYPE_CHECKING: # <2>
|
||||
reveal_type(not_book)
|
||||
reveal_type(not_book['authors'])
|
||||
|
||||
print(not_book) # <3>
|
||||
print(not_book['flavor']) # <4>
|
||||
|
||||
xml = to_xml(not_book) # <5>
|
||||
print(xml) # <6>
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
demo()
|
112
15-more-types/typeddict/test_books.py
Normal file
112
15-more-types/typeddict/test_books.py
Normal file
@ -0,0 +1,112 @@
|
||||
import json
|
||||
from typing import cast
|
||||
|
||||
from books import BookDict, to_xml, from_json
|
||||
|
||||
XML_SAMPLE = """
|
||||
<BOOK>
|
||||
\t<ISBN>0134757599</ISBN>
|
||||
\t<TITLE>Refactoring, 2e</TITLE>
|
||||
\t<AUTHOR>Martin Fowler</AUTHOR>
|
||||
\t<AUTHOR>Kent Beck</AUTHOR>
|
||||
\t<PAGECOUNT>478</PAGECOUNT>
|
||||
</BOOK>
|
||||
""".strip()
|
||||
|
||||
|
||||
# using plain dicts
|
||||
|
||||
def test_1() -> None:
|
||||
xml = to_xml({
|
||||
'isbn': '0134757599',
|
||||
'title': 'Refactoring, 2e',
|
||||
'authors': ['Martin Fowler', 'Kent Beck'],
|
||||
'pagecount': 478,
|
||||
})
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
def test_2() -> None:
|
||||
xml = to_xml(dict(
|
||||
isbn='0134757599',
|
||||
title='Refactoring, 2e',
|
||||
authors=['Martin Fowler', 'Kent Beck'],
|
||||
pagecount=478))
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
def test_5() -> None:
|
||||
book_data: BookDict = dict(
|
||||
isbn='0134757599',
|
||||
title='Refactoring, 2e',
|
||||
authors=['Martin Fowler', 'Kent Beck'],
|
||||
pagecount=478
|
||||
)
|
||||
xml = to_xml(book_data)
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
def test_6() -> None:
|
||||
book_data = dict(
|
||||
isbn='0134757599',
|
||||
title='Refactoring, 2e',
|
||||
authors=['Martin Fowler', 'Kent Beck'],
|
||||
pagecount=478
|
||||
)
|
||||
xml = to_xml(cast(BookDict, book_data)) # cast needed
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
def test_4() -> None:
|
||||
xml = to_xml(BookDict(
|
||||
isbn='0134757599',
|
||||
title='Refactoring, 2e',
|
||||
authors=['Martin Fowler', 'Kent Beck'],
|
||||
pagecount=478))
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
def test_7() -> None:
|
||||
book_data = BookDict(
|
||||
isbn='0134757599',
|
||||
title='Refactoring, 2e',
|
||||
authors=['Martin Fowler', 'Kent Beck'],
|
||||
pagecount=478
|
||||
)
|
||||
xml = to_xml(book_data)
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
def test_8() -> None:
|
||||
book_data: BookDict = {
|
||||
'isbn': '0134757599',
|
||||
'title': 'Refactoring, 2e',
|
||||
'authors': ['Martin Fowler', 'Kent Beck'],
|
||||
'pagecount': 478,
|
||||
}
|
||||
xml = to_xml(book_data)
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
BOOK_JSON = """
|
||||
{"isbn": "0134757599",
|
||||
"title": "Refactoring, 2e",
|
||||
"authors": ["Martin Fowler", "Kent Beck"],
|
||||
"pagecount": 478}
|
||||
"""
|
||||
|
||||
def test_load_book_0() -> None:
|
||||
book_data: BookDict = json.loads(BOOK_JSON) # typed var
|
||||
xml = to_xml(book_data)
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
def test_load_book() -> None:
|
||||
book_data = from_json(BOOK_JSON)
|
||||
xml = to_xml(book_data)
|
||||
assert xml == XML_SAMPLE
|
||||
|
||||
|
||||
NOT_BOOK_JSON = """
|
||||
{"isbn": 3.141592653589793
|
||||
"title": [1, 2, 3],
|
||||
"authors": ["Martin Fowler", "Kent Beck"],
|
||||
"flavor": "strawberry"}
|
||||
"""
|
||||
|
||||
def test_load_not_book() -> None:
|
||||
book_data: BookDict = json.loads(BOOK_JSON) # typed var
|
||||
xml = to_xml(book_data)
|
||||
assert xml == XML_SAMPLE
|
20
15-more-types/typeddict/test_books_check_fails.py
Normal file
20
15-more-types/typeddict/test_books_check_fails.py
Normal file
@ -0,0 +1,20 @@
|
||||
from books import BookDict, to_xml
|
||||
|
||||
XML_SAMPLE = """
|
||||
<BOOK>
|
||||
\t<ISBN>0134757599</ISBN>
|
||||
\t<TITLE>Refactoring, 2e</TITLE>
|
||||
\t<AUTHOR>Martin Fowler</AUTHOR>
|
||||
\t<AUTHOR>Kent Beck</AUTHOR>
|
||||
\t<PAGECOUNT>478</PAGECOUNT>
|
||||
</BOOK>
|
||||
""".strip()
|
||||
|
||||
def test_3() -> None:
|
||||
xml = to_xml(BookDict(dict([ # Expected keyword arguments, {...}, or dict(...) in TypedDict constructor
|
||||
('isbn', '0134757599'),
|
||||
('title', 'Refactoring, 2e'),
|
||||
('authors', ['Martin Fowler', 'Kent Beck']),
|
||||
('pagecount', 478),
|
||||
])))
|
||||
assert xml == XML_SAMPLE
|
60
README.md
60
README.md
@ -1,6 +1,6 @@
|
||||
# Fluent Python 2e example code
|
||||
|
||||
Example code for the book **Fluent Python, 2<sup>nd</sup> edition** by Luciano Ramalho (O'Reilly, 2021).
|
||||
Example code for the book **Fluent Python, 2<sup>nd</sup> edition** by Luciano Ramalho (O'Reilly, 2020).
|
||||
|
||||
> **BEWARE**: This is a work in progress!
|
||||
>
|
||||
@ -14,40 +14,40 @@ Example code for the book **Fluent Python, 2<sup>nd</sup> edition** by Luciano R
|
||||
|
||||
All chapters are undergoing review and updates, including significant rewrites in the chapters about concurrency in **Part V**.
|
||||
|
||||
New chapters in **Fluent Python 2<sup>nd</sup> edition** are marked with 🆕.
|
||||
New chapters in **Fluent Python 2e** are marked with 🆕.
|
||||
|
||||
🚨 This table of contents is subject to change at any time until the book goes to the printer.
|
||||
🚨 This table of contents is subject to change at any time until the book goes to the printer.
|
||||
|
||||
Part / Chapter #|Title|Directory|1<sup>st</sup> edition Chapter #
|
||||
---:|---|---|:---:
|
||||
Part / Chapter #|Title|Directory|Notebook|1<sup>st</sup> ed. Chapter #
|
||||
---:|---|---|---|:---:
|
||||
**I – Prologue**|
|
||||
1|The Python Data Model|[01-data-model](01-data-model)|1
|
||||
1|The Python Data Model|[01-data-model](01-data-model)|[data-model.ipynb](01-data-model/data-model.ipynb)|1
|
||||
**II – Data Structures**|
|
||||
2|An Array of Sequences|[02-array-seq](02-array-seq)|2
|
||||
3|Dictionaries and Sets|[03-dict-set](03-dict-set)|3
|
||||
4|Text versus Bytes|[04-text-byte](04-text-byte)|4
|
||||
5|Record-like Data Structures|[05-record-like](05-record-like)|🆕
|
||||
6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)|8
|
||||
2|An Array of Sequences|[02-array-seq](02-array-seq)|[array-seq.ipynb](02-array-seq/array-seq.ipynb)|2
|
||||
3|Dictionaries and Sets|[03-dict-set](03-dict-set)||3
|
||||
4|Text versus Bytes|[04-text-byte](04-text-byte)||4
|
||||
🆕 5|Record-like Data Structures|[05-record-like](05-record-like)||–
|
||||
6|Object References, Mutability, and Recycling|[06-obj-ref](06-obj-ref)||8
|
||||
**III – Functions as Objects**|
|
||||
7|First-Class Funcions|[07-1class-func](07-1class-func)|5
|
||||
8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)|🆕
|
||||
9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)|7
|
||||
10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)|6
|
||||
7|First-Class Funcions|[07-1class-func](07-1class-func)||5
|
||||
🆕 8|Type Hints in Function Definitions|[08-def-type-hints](08-def-type-hints)||–
|
||||
9|Function Decorators and Closures|[09-closure-deco](09-closure-deco)||7
|
||||
10|Design Patterns with First-Class Functions|[10-dp-1class-func](10-dp-1class-func)||6
|
||||
**IV – Object-Oriented Idioms**|
|
||||
11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)|9
|
||||
12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)|10
|
||||
13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)|11
|
||||
14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)|12
|
||||
15|More About Type Hints|15-more-typing|🆕
|
||||
16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)|13
|
||||
11|A Pythonic Object|[11-pythonic-obj](11-pythonic-obj)||9
|
||||
12|Sequence Hacking, Hashing, and Slicing|[12-seq-hacking](12-seq-hacking)||10
|
||||
13|Interfaces, Protocols, and ABCs|[13-protocl-abc](13-protocol-abc)||11
|
||||
14|Inheritance: For Good or For Worse|[14-inheritance](14-inheritance)||12
|
||||
🆕 15|More About Type Hints|[15-more-types](15-more-types)||–
|
||||
16|Operator Overloading: Doing It Right|[16-op-overloading](16-op-overloading)||13
|
||||
**V – Control Flow**|
|
||||
17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)|14
|
||||
18|Context Managers and else Blocks|[18-context-mngr](18-context-mngr)|15
|
||||
19|Classic Coroutines|[19-coroutine](19-coroutine)|16
|
||||
20|Concurrency Models in Python|[20-concurrency](20-concurrency)|🆕
|
||||
21|Concurrency with Futures|[21-futures](21-futures)|17
|
||||
22|Asynchronous Programming|[22-async](22-async)|18
|
||||
17|Iterables, Iterators, and Generators|[17-it-generator](17-it-generator)||14
|
||||
18|Context Managers and else Blocks|[18-context-mngr](18-context-mngr)||15
|
||||
19|Classic Coroutines|[19-coroutine](19-coroutine)||16
|
||||
🆕 20|Concurrency Models in Python|[20-concurrency](20-concurrency)||-
|
||||
21|Concurrency with Futures|[21-futures](21-futures)||17
|
||||
22|Asynchronous Programming|[22-async](22-async)||18
|
||||
**VI – Metaprogramming**|
|
||||
23|Dynamic Attributes and Properties|[23-dyn-attr-prop](23-dyn-attr-prop)|19
|
||||
24|Attribute Descriptors|[24-descriptor](24-descriptor)|20
|
||||
25|Class Metaprogramming|[25-class-metaprog](25-class-metaprog)|21
|
||||
23|Dynamic Attributes and Properties|[22-dyn-attr-prop](22-dyn-attr-prop)||19
|
||||
24|Attribute Descriptors|[23-descriptor](23-descriptor)||20
|
||||
25|Class Metaprogramming|[24-class-metaprog](24-class-metaprog)||21
|
||||
|
Loading…
Reference in New Issue
Block a user