updates to ch.14
This commit is contained in:
parent
b4ad845d12
commit
cbd13885fc
@ -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/
|
||||
|
@ -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[]
|
||||
|
67
14-inheritance/diamond2.py
Normal file
67
14-inheritance/diamond2.py
Normal 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()
|
||||
|
115
14-inheritance/uppermixin.py
Normal file
115
14-inheritance/uppermixin.py
Normal 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[]
|
Loading…
x
Reference in New Issue
Block a user