updates to ch.14

This commit is contained in:
Luciano Ramalho 2021-08-06 19:51:22 -03:00
parent b4ad845d12
commit cbd13885fc
4 changed files with 236 additions and 19 deletions

View File

@ -1,4 +1,4 @@
Sample code for Chapter 14 - "Inheritance: for good or for worse"
Sample code for Chapter 14 - "Inheritance: for better or for worse"
From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2021)
https://learning.oreilly.com/library/view/fluent-python-2nd/9781492056348/

View File

@ -1,27 +1,62 @@
class A:
"""
diamond1.py: Demo of diamond-shaped class graph.
# tag::LEAF_MRO[]
>>> Leaf.__mro__ # doctest:+NORMALIZE_WHITESPACE
(<class 'diamond1.Leaf'>, <class 'diamond1.A'>, <class 'diamond1.B'>,
<class 'diamond1.Root'>, <class 'object'>)
# end::LEAF_MRO[]
# tag::DIAMOND_CALLS[]
>>> leaf1 = Leaf() # <1>
>>> leaf1.ping() # <2>
<instance of Leaf>.ping() in Leaf
<instance of Leaf>.ping() in A
<instance of Leaf>.ping() in B
<instance of Leaf>.ping() in Root
>>> leaf1.pong() # <3>
<instance of Leaf>.pong() in A
<instance of Leaf>.pong() in B
# end::DIAMOND_CALLS[]
"""
# tag::DIAMOND_CLASSES[]
class Root: # <1>
def ping(self):
print('ping:', self)
print(f'{self}.ping() in Root')
class B(A):
def pong(self):
print('pong:', self)
print(f'{self}.pong() in Root')
def __repr__(self):
cls_name = type(self).__name__
return f'<instance of {cls_name}>'
class C(A):
def pong(self):
print('PONG:', self)
class D(B, C):
class A(Root): # <2>
def ping(self):
print(f'{self}.ping() in A')
super().ping()
print('post-ping:', self)
def pingpong(self):
self.ping()
super().ping()
self.pong()
def pong(self):
print(f'{self}.pong() in A')
super().pong()
C.pong(self)
class B(Root): # <3>
def ping(self):
print(f'{self}.ping() in B')
super().ping()
def pong(self):
print(f'{self}.pong() in B')
class Leaf(A, B): # <4>
def ping(self):
print(f'{self}.ping() in Leaf')
super().ping()
# end::DIAMOND_CLASSES[]

View File

@ -0,0 +1,67 @@
"""
unrelated.py: examples with ``super()`` in a sibling class.
``U`` is unrelated (does not subclass ``Root``)
Calling ``ping`` on an instance of ``U`` fails::
# tag::UNRELATED_DEMO_1[]
>>> u = U()
>>> u.ping()
Traceback (most recent call last):
...
AttributeError: 'super' object has no attribute 'ping'
# end::UNRELATED_DEMO_1[]
But if ``U`` is part of a cooperative arrangement of base classes,
its ``ping`` method works::
# tag::UNRELATED_DEMO_2[]
>>> leaf2 = LeafUA()
>>> leaf2.ping()
<instance of LeafUA>.ping() in LeafUA
<instance of LeafUA>.ping() in U
<instance of LeafUA>.ping() in A
<instance of LeafUA>.ping() in Root
>>> LeafUA.__mro__ # doctest:+NORMALIZE_WHITESPACE
(<class 'diamond2.LeafUA'>, <class 'diamond2.U'>,
<class 'diamond.A'>, <class 'diamond.Root'>, <class 'object'>)
# end::UNRELATED_DEMO_2[]
Here ``U.ping`` is never called because ``Root.ping`` does not call ``super``.
>>> o6 = LeafAU()
>>> o6.ping()
<instance of LeafAU>.ping() in LeafAU
<instance of LeafAU>.ping() in A
<instance of LeafAU>.ping() in Root
>>> LeafAU.__mro__ # doctest:+NORMALIZE_WHITESPACE
(<class 'diamond2.LeafAU'>, <class 'diamond.A'>, <class 'diamond.Root'>,
<class 'diamond2.U'>, <class 'object'>)
"""
# tag::DIAMOND_CLASSES[]
from diamond import A # <1>
class U(): # <2>
def ping(self):
print(f'{self}.ping() in U')
super().ping() # <3>
class LeafUA(U, A): # <4>
def ping(self):
print(f'{self}.ping() in LeafUA')
super().ping()
# end::DIAMOND_CLASSES[]
class LeafAU(A, U):
def ping(self):
print(f'{self}.ping() in LeafAU')
super().ping()

View File

@ -0,0 +1,115 @@
"""UpperDict uppercases all string keys.
Test for initializer. `str` keys are uppercased::
>>> d = UpperDict([('a', 'letter A'), ('B', 'letter B'), (2, 'digit two')])
>>> list(d.keys())
['A', 'B', 2]
Tests for item retrieval using `d[key]` notation::
>>> d['A']
'letter A'
>>> d['b']
'letter B'
>>> d[2]
'digit two'
Tests for missing key::
>>> d['z']
Traceback (most recent call last):
...
KeyError: 'Z'
>>> d[99]
Traceback (most recent call last):
...
KeyError: 99
Tests for item retrieval using `d.get(key)` notation::
>>> d.get('a')
'letter A'
>>> d.get('B')
'letter B'
>>> d.get(2)
'digit two'
>>> d.get('z', '(not found)')
'(not found)'
Tests for the `in` operator::
>>> ('a' in d, 'B' in d, 'z' in d)
(True, True, False)
Test for item assignment using lowercase key::
>>> d['c'] = 'letter C'
>>> d['C']
'letter C'
Tests for update using a `dict` or a sequence of pairs::
>>> d.update({'D': 'letter D', 'e': 'letter E'})
>>> list(d.keys())
['A', 'B', 2, 'C', 'D', 'E']
>>> d.update([('f', 'letter F'), ('G', 'letter G')])
>>> list(d.keys())
['A', 'B', 2, 'C', 'D', 'E', 'F', 'G']
>>> d # doctest:+NORMALIZE_WHITESPACE
{'A': 'letter A', 'B': 'letter B', 2: 'digit two',
'C': 'letter C', 'D': 'letter D', 'E': 'letter E',
'F': 'letter F', 'G': 'letter G'}
UpperCounter uppercases all `str` keys.
Test for initializer: keys are uppercased.
>>> d = UpperCounter('AbracAdaBrA')
>>> sorted(d.keys())
['A', 'B', 'C', 'D', 'R']
Tests for count retrieval using `d[key]` notation::
>>> d['a']
5
>>> d['z']
0
"""
# tag::UPPERCASE_MIXIN[]
import collections
def _upper(key): # <1>
try:
return key.upper()
except AttributeError:
return key
class UpperCaseMixin: # <2>
def __setitem__(self, key, item):
super().__setitem__(_upper(key), item)
def __getitem__(self, key):
return super().__getitem__(_upper(key))
def get(self, key, default=None):
return super().get(_upper(key), default)
def __contains__(self, key):
return super().__contains__(_upper(key))
# end::UPPERCASE_MIXIN[]
# tag::UPPERDICT[]
class UpperDict(UpperCaseMixin, collections.UserDict): # <1>
pass
class UpperCounter(UpperCaseMixin, collections.Counter): # <2>
"""Specialized 'Counter' that uppercases string keys""" # <3>
# end::UPPERDICT[]