Skip to content

Commit cbd1388

Browse files
committed
updates to ch.14
1 parent b4ad845 commit cbd1388

File tree

4 files changed

+233
-16
lines changed

4 files changed

+233
-16
lines changed

14-inheritance/README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Sample code for Chapter 14 - "Inheritance: for good or for worse"
1+
Sample code for Chapter 14 - "Inheritance: for better or for worse"
22

33
From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2021)
44
https://learning.oreilly.com/library/view/fluent-python-2nd/9781492056348/

14-inheritance/diamond.py

+50-15
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,62 @@
1-
class A:
2-
def ping(self):
3-
print('ping:', self)
1+
"""
2+
diamond1.py: Demo of diamond-shaped class graph.
43
4+
# tag::LEAF_MRO[]
5+
>>> Leaf.__mro__ # doctest:+NORMALIZE_WHITESPACE
6+
(<class 'diamond1.Leaf'>, <class 'diamond1.A'>, <class 'diamond1.B'>,
7+
<class 'diamond1.Root'>, <class 'object'>)
58
6-
class B(A):
7-
def pong(self):
8-
print('pong:', self)
9+
# end::LEAF_MRO[]
10+
11+
# tag::DIAMOND_CALLS[]
12+
>>> leaf1 = Leaf() # <1>
13+
>>> leaf1.ping() # <2>
14+
<instance of Leaf>.ping() in Leaf
15+
<instance of Leaf>.ping() in A
16+
<instance of Leaf>.ping() in B
17+
<instance of Leaf>.ping() in Root
918
19+
>>> leaf1.pong() # <3>
20+
<instance of Leaf>.pong() in A
21+
<instance of Leaf>.pong() in B
22+
23+
# end::DIAMOND_CALLS[]
24+
"""
25+
26+
# tag::DIAMOND_CLASSES[]
27+
class Root: # <1>
28+
def ping(self):
29+
print(f'{self}.ping() in Root')
1030

11-
class C(A):
1231
def pong(self):
13-
print('PONG:', self)
32+
print(f'{self}.pong() in Root')
1433

34+
def __repr__(self):
35+
cls_name = type(self).__name__
36+
return f'<instance of {cls_name}>'
1537

16-
class D(B, C):
1738

39+
class A(Root): # <2>
1840
def ping(self):
41+
print(f'{self}.ping() in A')
1942
super().ping()
20-
print('post-ping:', self)
2143

22-
def pingpong(self):
23-
self.ping()
24-
super().ping()
25-
self.pong()
44+
def pong(self):
45+
print(f'{self}.pong() in A')
2646
super().pong()
27-
C.pong(self)
47+
48+
49+
class B(Root): # <3>
50+
def ping(self):
51+
print(f'{self}.ping() in B')
52+
super().ping()
53+
54+
def pong(self):
55+
print(f'{self}.pong() in B')
56+
57+
58+
class Leaf(A, B): # <4>
59+
def ping(self):
60+
print(f'{self}.ping() in Leaf')
61+
super().ping()
62+
# end::DIAMOND_CLASSES[]

14-inheritance/diamond2.py

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
unrelated.py: examples with ``super()`` in a sibling class.
3+
4+
``U`` is unrelated (does not subclass ``Root``)
5+
6+
Calling ``ping`` on an instance of ``U`` fails::
7+
8+
# tag::UNRELATED_DEMO_1[]
9+
>>> u = U()
10+
>>> u.ping()
11+
Traceback (most recent call last):
12+
...
13+
AttributeError: 'super' object has no attribute 'ping'
14+
15+
# end::UNRELATED_DEMO_1[]
16+
17+
18+
But if ``U`` is part of a cooperative arrangement of base classes,
19+
its ``ping`` method works::
20+
21+
# tag::UNRELATED_DEMO_2[]
22+
23+
>>> leaf2 = LeafUA()
24+
>>> leaf2.ping()
25+
<instance of LeafUA>.ping() in LeafUA
26+
<instance of LeafUA>.ping() in U
27+
<instance of LeafUA>.ping() in A
28+
<instance of LeafUA>.ping() in Root
29+
>>> LeafUA.__mro__ # doctest:+NORMALIZE_WHITESPACE
30+
(<class 'diamond2.LeafUA'>, <class 'diamond2.U'>,
31+
<class 'diamond.A'>, <class 'diamond.Root'>, <class 'object'>)
32+
33+
# end::UNRELATED_DEMO_2[]
34+
35+
36+
Here ``U.ping`` is never called because ``Root.ping`` does not call ``super``.
37+
38+
>>> o6 = LeafAU()
39+
>>> o6.ping()
40+
<instance of LeafAU>.ping() in LeafAU
41+
<instance of LeafAU>.ping() in A
42+
<instance of LeafAU>.ping() in Root
43+
>>> LeafAU.__mro__ # doctest:+NORMALIZE_WHITESPACE
44+
(<class 'diamond2.LeafAU'>, <class 'diamond.A'>, <class 'diamond.Root'>,
45+
<class 'diamond2.U'>, <class 'object'>)
46+
47+
"""
48+
49+
# tag::DIAMOND_CLASSES[]
50+
from diamond import A # <1>
51+
52+
class U(): # <2>
53+
def ping(self):
54+
print(f'{self}.ping() in U')
55+
super().ping() # <3>
56+
57+
class LeafUA(U, A): # <4>
58+
def ping(self):
59+
print(f'{self}.ping() in LeafUA')
60+
super().ping()
61+
# end::DIAMOND_CLASSES[]
62+
63+
class LeafAU(A, U):
64+
def ping(self):
65+
print(f'{self}.ping() in LeafAU')
66+
super().ping()
67+

14-inheritance/uppermixin.py

+115
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
"""UpperDict uppercases all string keys.
2+
3+
Test for initializer. `str` keys are uppercased::
4+
5+
>>> d = UpperDict([('a', 'letter A'), ('B', 'letter B'), (2, 'digit two')])
6+
>>> list(d.keys())
7+
['A', 'B', 2]
8+
9+
Tests for item retrieval using `d[key]` notation::
10+
11+
>>> d['A']
12+
'letter A'
13+
>>> d['b']
14+
'letter B'
15+
>>> d[2]
16+
'digit two'
17+
18+
19+
Tests for missing key::
20+
21+
>>> d['z']
22+
Traceback (most recent call last):
23+
...
24+
KeyError: 'Z'
25+
>>> d[99]
26+
Traceback (most recent call last):
27+
...
28+
KeyError: 99
29+
30+
31+
Tests for item retrieval using `d.get(key)` notation::
32+
33+
>>> d.get('a')
34+
'letter A'
35+
>>> d.get('B')
36+
'letter B'
37+
>>> d.get(2)
38+
'digit two'
39+
>>> d.get('z', '(not found)')
40+
'(not found)'
41+
42+
Tests for the `in` operator::
43+
44+
>>> ('a' in d, 'B' in d, 'z' in d)
45+
(True, True, False)
46+
47+
Test for item assignment using lowercase key::
48+
49+
>>> d['c'] = 'letter C'
50+
>>> d['C']
51+
'letter C'
52+
53+
Tests for update using a `dict` or a sequence of pairs::
54+
55+
>>> d.update({'D': 'letter D', 'e': 'letter E'})
56+
>>> list(d.keys())
57+
['A', 'B', 2, 'C', 'D', 'E']
58+
>>> d.update([('f', 'letter F'), ('G', 'letter G')])
59+
>>> list(d.keys())
60+
['A', 'B', 2, 'C', 'D', 'E', 'F', 'G']
61+
>>> d # doctest:+NORMALIZE_WHITESPACE
62+
{'A': 'letter A', 'B': 'letter B', 2: 'digit two',
63+
'C': 'letter C', 'D': 'letter D', 'E': 'letter E',
64+
'F': 'letter F', 'G': 'letter G'}
65+
66+
UpperCounter uppercases all `str` keys.
67+
68+
Test for initializer: keys are uppercased.
69+
70+
>>> d = UpperCounter('AbracAdaBrA')
71+
>>> sorted(d.keys())
72+
['A', 'B', 'C', 'D', 'R']
73+
74+
Tests for count retrieval using `d[key]` notation::
75+
76+
>>> d['a']
77+
5
78+
>>> d['z']
79+
0
80+
81+
"""
82+
# tag::UPPERCASE_MIXIN[]
83+
import collections
84+
85+
86+
def _upper(key): # <1>
87+
try:
88+
return key.upper()
89+
except AttributeError:
90+
return key
91+
92+
93+
class UpperCaseMixin: # <2>
94+
def __setitem__(self, key, item):
95+
super().__setitem__(_upper(key), item)
96+
97+
def __getitem__(self, key):
98+
return super().__getitem__(_upper(key))
99+
100+
def get(self, key, default=None):
101+
return super().get(_upper(key), default)
102+
103+
def __contains__(self, key):
104+
return super().__contains__(_upper(key))
105+
# end::UPPERCASE_MIXIN[]
106+
107+
# tag::UPPERDICT[]
108+
class UpperDict(UpperCaseMixin, collections.UserDict): # <1>
109+
pass
110+
111+
112+
class UpperCounter(UpperCaseMixin, collections.Counter): # <2>
113+
"""Specialized 'Counter' that uppercases string keys""" # <3>
114+
115+
# end::UPPERDICT[]

0 commit comments

Comments
 (0)