Skip to content

Commit aa09947

Browse files
committed
Chapter 15
1 parent 8e167b1 commit aa09947

9 files changed

Lines changed: 444 additions & 0 deletions

Ch15/arbitrary.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from multiprocessing.sharedctypes import Value
2+
from typing import Type
3+
4+
5+
with open('input.dat', 'r') as file:
6+
nums = [value.strip() for value in file if value]
7+
8+
for num in nums:
9+
expression = f"{num} // 2 + 2"
10+
try:
11+
answer = eval(expression)
12+
except (NameError, ValueError, TypeError, SyntaxError) as e:
13+
print(e)
14+
finally:
15+
code = "print('The answer is', answer)"
16+
obj = compile(code, '<string>', mode='exec')
17+
exec(obj)

Ch15/bad_function_attribute.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
def skit():
2+
print(skit.actor)
3+
4+
5+
skit.actor = "John Cleese"
6+
skit() # prints "John Cleese"
7+
8+
sketch = skit
9+
sketch() # prints "John Cleese"
10+
sketch.actor = "Eric Idle"
11+
sketch() # prints "Eric Idle"
12+
13+
skit() # prints "Eric Idle"...yikes!

Ch15/book_club.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import re
2+
3+
4+
class Book:
5+
pattern = re.compile(r'(.+)\((\d+)\)\. (.+)\. (.+)\..*')
6+
7+
def __set_name__(self, owner, name):
8+
self.name = name
9+
10+
def attr(self, attr):
11+
return f"{self.name}.{attr}"
12+
13+
def __set__(self, instance, value):
14+
matches = self.pattern.match(value)
15+
if not matches:
16+
raise ValueError("Book data must be specified in APA 7 format.")
17+
setattr(instance, self.attr('author'), matches.group(1))
18+
setattr(instance, self.attr('year'), matches.group(2))
19+
setattr(instance, self.attr('title'), matches.group(3))
20+
setattr(instance, self.attr('publisher'), matches.group(4))
21+
22+
def __get__(self, instance, owner=None):
23+
try:
24+
title = getattr(instance, self.attr('title'))
25+
author = getattr(instance, self.attr('author'))
26+
except AttributeError:
27+
return "nothing right now"
28+
return f"{title} by {author}"
29+
30+
def __delete__(self, instance):
31+
delattr(instance, self.attr('author'))
32+
delattr(instance, self.attr('year'))
33+
delattr(instance, self.attr('title'))
34+
delattr(instance, self.attr('publisher'))
35+
36+
37+
class BookClub:
38+
reading = Book()
39+
reading_next = Book()
40+
41+
def __init__(self, name):
42+
self.name = name
43+
self.members = []
44+
45+
def new_member(self, member):
46+
self.members.append(member)
47+
print(
48+
"===== - - - - - - - - - =====",
49+
f"Welcome to the {self.name} Book Club, {member}!",
50+
f"We are reading {self.reading}",
51+
"===== - - - - - - - - - =====",
52+
sep='\n'
53+
)
54+
55+
56+
mystery_lovers = BookClub("Mystery Lovers")
57+
lattes_and_lit = BookClub("Lattes and Lit")
58+
59+
mystery_lovers.reading = (
60+
"McDonald, J.C. (2019). "
61+
"Noah Clue, P.I. AJ Charleson Publishing."
62+
)
63+
64+
lattes_and_lit.reading = (
65+
"Christie, A. (1926). "
66+
"The Murder of Roger Ackroyd. William Collins & Sons."
67+
)
68+
69+
print(mystery_lovers.reading) # prints "'The Murder of Roger Ackroyd..."
70+
print(lattes_and_lit.reading) # prints "'The Murder of Roger Ackroyd..."
71+
72+
73+
del lattes_and_lit.reading
74+
75+
lattes_and_lit.new_member("Jaime")
76+
77+
lattes_and_lit.reading = (
78+
"Hillerman, T. (1973). "
79+
"Dance Hall Of The Dead. Harper and Row."
80+
)
81+
82+
lattes_and_lit.new_member("Danny")
83+
84+
mystery_lovers.reading = (
85+
"McDonald, J.C. (2019). "
86+
"Noah Clue, P.I. AJ Charleson Publishing."
87+
)
88+
89+
mystery_lovers.reading_next = (
90+
"Chesterton, G.K. (1911). The Innocence of Father Brown. "
91+
"Cassell and Company, Ltd."
92+
)
93+
print(f"Now: {mystery_lovers.reading}")
94+
print(f"Next: {mystery_lovers.reading_next}")
95+
96+
97+
import pprint
98+
pprint.pprint(dir(mystery_lovers))

Ch15/element.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
class Element:
2+
__slots__ = (
3+
'name',
4+
'number',
5+
'symbol',
6+
'family',
7+
'iupac_num',
8+
'__dict__',
9+
'__weakref__'
10+
)
11+
12+
def __init__(self, symbol, number, name, family, numeration):
13+
self.symbol = symbol.title()
14+
self.number = number
15+
self.name = name.lower()
16+
self.family = family.lower()
17+
self.iupac_num = numeration
18+
19+
def __str__(self):
20+
return f"{self.symbol} ({self.name}): {self.number}"
21+
22+
23+
oxygen = Element('O', 8, 'oxygen', 'non-metals', 16)
24+
iron = Element('Fe', 26, 'iron', 'transition metal', 8)
25+
26+
print(oxygen) # prints 'O (Oxygen): 8'
27+
print(iron) # prints 'Fe (Iron): 26'
28+
29+
iron.atomic_mass = 55.845

Ch15/element_generic.py

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
from functools import singledispatchmethod
2+
from typing import overload
3+
4+
5+
class Element:
6+
__slots__ = (
7+
'name',
8+
'number',
9+
'symbol',
10+
'__weakref__',
11+
)
12+
13+
def __init__(self, symbol, number, name):
14+
self.symbol = symbol.title()
15+
self.number = number
16+
self.name = name.lower()
17+
18+
def __repr__(self):
19+
return f"{self.symbol} ({self.name}): {self.number}"
20+
21+
def __str__(self):
22+
return self.symbol
23+
24+
def __hash__(self):
25+
return hash(self.symbol)
26+
27+
@singledispatchmethod
28+
def __eq__(self, other):
29+
return self.symbol == other.symbol
30+
31+
@__eq__.register
32+
def _(self, other: str):
33+
return self.symbol == other
34+
35+
@overload
36+
def _(self, other: float):
37+
...
38+
@__eq__.register
39+
def _(self, other: int):
40+
return self.number == other
41+
42+
@singledispatchmethod
43+
def __lt__(self, other):
44+
return self.symbol < other.symbol
45+
46+
@__lt__.register(str)
47+
def _(self, other):
48+
return self.symbol < other
49+
50+
@__lt__.register(int)
51+
@__lt__.register(float)
52+
def _(self, other):
53+
return self.number < other
54+
55+
@singledispatchmethod
56+
def __le__(self, other):
57+
return self.symbol <= other.symbol
58+
59+
__le__.register(str, lambda self, other: self.symbol <= other)
60+
61+
__le__.register(int, lambda self, other: self.number <= other)
62+
__le__.register(float, lambda self, other: self.number <= other)
63+
64+
def __selfattr__(self, name, value):
65+
if hasattr(self, name):
66+
raise AttributeError(
67+
f"'{type(self)}' object attribute '{name}' is read-only"
68+
)
69+
object.__setattr__(self, name, value)
70+
71+
def __delattr__(self, name):
72+
raise AttributeError(
73+
f"'{type(self)}' object attribute '{name}' is read-only"
74+
)
75+
76+
77+
class Compound:
78+
79+
def __init__(self, name):
80+
self.name = name.title()
81+
self.components = {}
82+
83+
def add_element(self, element, count):
84+
try:
85+
self.components[element] += count
86+
except KeyError:
87+
self.components[element] = count
88+
89+
def __str__(self):
90+
s = ""
91+
formula = self.components.copy()
92+
# Hill system
93+
if 'C' in formula.keys():
94+
s += f"C{formula['C']}"
95+
del formula['C']
96+
if 1 in formula.keys():
97+
s += f"H{formula['H']}"
98+
del formula['H']
99+
for element, count in sorted(formula.items()):
100+
s += f"{element.symbol}{count if count > 1 else ''}"
101+
# substitute subscript digits for normal digits
102+
s = s.translate(str.maketrans("0123456789","₀₁₂₃₄₅₆₇₈₉"))
103+
return s
104+
105+
def __repr__(self):
106+
return f"{self.name}: {self}"
107+
108+
109+
hydrogen = Element('H', 6, 'hydrogen')
110+
carbon = Element('C', 6, 'carbon')
111+
oxygen = Element('O', 8, 'oxygen')
112+
iron = Element('Fe', 26, 'iron')
113+
114+
rust = Compound("iron oxide")
115+
rust.add_element(oxygen, count=3)
116+
rust.add_element(iron, count=2)
117+
print(f"{rust!r}") # prints 'Iron Oxide: Fe₂O₃'
118+
119+
aspirin = Compound("acetylsalicylic acid")
120+
aspirin.add_element(hydrogen, 8)
121+
aspirin.add_element(oxygen, 4)
122+
aspirin.add_element(carbon, 9)
123+
print(f"{aspirin!r}") # prints 'Acetylsalicylic Acid: C₉H₈O₄'
124+
125+
water = Compound("water")
126+
water.add_element(hydrogen, 2)
127+
water.add_element(oxygen, 1)
128+
print(f"{water!r}") # prints 'Water: H₂O'

Ch15/element_immutable.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
class Element:
2+
__slots__ = (
3+
'name',
4+
'number',
5+
'symbol',
6+
'__weakref__',
7+
)
8+
9+
def __init__(self, symbol, number, name):
10+
self.symbol = symbol.title()
11+
self.number = number
12+
self.name = name.lower()
13+
14+
def __repr__(self):
15+
return f"{self.symbol} ({self.name}): {self.number}"
16+
17+
def __str__(self):
18+
return self.symbol
19+
20+
def __hash__(self):
21+
return hash(self.symbol)
22+
23+
def __eq__(self, other):
24+
return self.symbol == other.symbol
25+
26+
def __lt__(self, other):
27+
return self.symbol < other.symbol
28+
29+
def __le__(self, other):
30+
return self.symbol <= other.symbol
31+
32+
def __selfattr__(self, name, value):
33+
if hasattr(self, name):
34+
raise AttributeError(
35+
f"'{type(self)}' object attribute '{name}' is read-only"
36+
)
37+
object.__setattr__(self, name, value)
38+
39+
def __delattr__(self, name):
40+
raise AttributeError(
41+
f"'{type(self)}' object attribute '{name}' is read-only"
42+
)
43+
44+
oxygen = Element('O', 8, 'oxygen')
45+
iron = Element('Fe', 26, 'iron')
46+
47+
print(oxygen) # prints 'O (Oxygen): 8'
48+
print(f"{iron!r}") # prints 'Fe (Iron): 26'
49+
50+
iron.atomic_mass = 55.845 # raises AttributeError
51+
iron.symbol = "Ir" # raises AttributeError
52+
del iron.symbol # raises AttributeError

Ch15/function_attribute.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
def multiplier(n):
2+
if not hasattr(multiplier, 'factor'):
3+
multiplier.factor = 0
4+
print(n * multiplier.factor)
5+
6+
7+
multiplier(2) # prints 0
8+
print(multiplier.__dict__) # prints {}
9+
multiplier.factor = 3
10+
print(multiplier.__dict__) # prints {'factor': 3}
11+
multiplier(2) # prints 6

Ch15/input.dat

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
40
2+
(30 + 7)
3+
9 * 3
4+
0xAA & 0xBB
5+
80
6+
exec('import os') or os.system('echo \"`whoami` is DOOMED\"') == 0 or 1

0 commit comments

Comments
 (0)