Skip to content

Commit 02332a5

Browse files
committed
Chapter 7
1 parent b1bc76f commit 02332a5

10 files changed

Lines changed: 347 additions & 0 deletions

Ch7/coffee_order_decorator.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
class CoffeeOrder:
2+
3+
def __init__(self, recipe, to_go=False):
4+
self.recipe = recipe
5+
self.to_go = to_go
6+
7+
def brew(self):
8+
vessel = "in a paper cup" if self.to_go else "in a mug"
9+
print("Brewing", *self.recipe.parts, vessel)
10+
11+
12+
class CoffeeRecipe:
13+
14+
def __init__(self, parts):
15+
self.parts = parts
16+
17+
18+
special = CoffeeRecipe(["double-shot", "grande", "no-whip", "mocha"])
19+
order = CoffeeOrder(special, to_go=False)
20+
order.brew() # prints "Brewing double-shot grande no-whip mocha in a mug"
21+
22+
23+
import functools
24+
def auto_order(to_go):
25+
def decorator(cls):
26+
@functools.wraps(cls)
27+
def wrapper(*args, **kwargs):
28+
recipe = cls(*args, **kwargs)
29+
return (CoffeeOrder(recipe, to_go), recipe)
30+
return wrapper
31+
return decorator
32+
33+
34+
@auto_order(to_go=True)
35+
class CoffeeShackRecipe(CoffeeRecipe):
36+
pass
37+
38+
39+
order, recipe = CoffeeShackRecipe(["tall", "decaf", "cappuccino"])
40+
order.brew() # prints "Brewing tall decaf cappuccino in a paper cup"

Ch7/global_coordinates.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
from dis import dis
2+
import math
3+
4+
5+
class GlobalCoordinates:
6+
7+
def __init__(self, *, latitude, longitude):
8+
self._lat_deg = latitude[0]
9+
self._lat_min = latitude[1]
10+
self._lat_sec = latitude[2]
11+
self._lat_dir = latitude[3]
12+
13+
self._lon_deg = longitude[0]
14+
self._lon_min = longitude[1]
15+
self._lon_sec = longitude[2]
16+
self._lon_dir = longitude[3]
17+
18+
@staticmethod
19+
def degrees_from_decimal(dec, *, lat):
20+
if lat:
21+
direction = "S" if dec < 0 else "N"
22+
else:
23+
direction = "W" if dec < 0 else "E"
24+
dec = abs(dec)
25+
degrees = int(dec)
26+
dec -= degrees
27+
minutes = int(dec * 60)
28+
dec -= minutes / 60
29+
seconds = round(dec * 3600, 1)
30+
return (degrees, minutes, seconds, direction)
31+
32+
@staticmethod
33+
def decimal_from_degrees(degrees, minutes, seconds, direction):
34+
dec = degrees + minutes / 60 + seconds / 3600
35+
if direction == "S" or direction == "W":
36+
dec = -dec
37+
return round(dec, 6)
38+
39+
@property
40+
def latitude(self):
41+
return self.decimal_from_degrees(
42+
self._lat_deg, self._lat_min, self._lat_sec, self._lat_dir
43+
)
44+
45+
@property
46+
def longitude(self):
47+
return self.decimal_from_degrees(
48+
self._lon_deg, self._lon_min, self._lon_sec, self._lon_dir
49+
)
50+
51+
def __repr__(self):
52+
return (
53+
f"<GlobalCoordinates "
54+
f"lat={self._lat_deg}°{self._lat_min}'"
55+
f"{self._lat_sec}\"{self._lat_dir} "
56+
f"lon={self._lon_deg}°{self._lon_min}'"
57+
f"{self._lon_sec}\"{self._lon_dir}>"
58+
)
59+
60+
def __str__(self):
61+
return (
62+
f"{self._lat_deg}°{self._lat_min}'"
63+
f"{self._lat_sec}\"{self._lat_dir} "
64+
f"{self._lon_deg}°{self._lon_min}'"
65+
f"{self._lon_sec}\"{self._lon_dir}>"
66+
)
67+
68+
def __hash__(self):
69+
return hash((
70+
self._lat_deg, self._lat_min, self._lat_sec, self._lat_dir,
71+
self._lon_deg, self._lon_min, self._lon_sec, self._lon_dir
72+
))
73+
74+
def __eq__(self, other):
75+
if not isinstance(other, GlobalCoordinates):
76+
return NotImplemented
77+
78+
return (
79+
self._lat_deg == other._lat_deg
80+
and self._lat_min == other._lat_min
81+
and self._lat_sec == other._lat_sec
82+
and self._lat_dir == other._lat_dir
83+
and self._lon_deg == other._lon_deg
84+
and self._lon_min == other._lon_min
85+
and self._lon_sec == other._lon_sec
86+
and self._lon_dir == other._lon_dir
87+
)
88+
89+
def __sub__(self, other):
90+
if not isinstance(other, GlobalCoordinates):
91+
return NotImplemented
92+
93+
lat_diff = self.latitude - other.latitude
94+
lon_diff = self.longitude - other.longitude
95+
return (lat_diff, lon_diff)
96+
97+
def __invert__(self):
98+
return GlobalCoordinates(
99+
latitude=self.degrees_from_decimal(-self.latitude, lat=True),
100+
longitude=self.degrees_from_decimal(-self.longitude, lat=False)
101+
)
102+
103+
def __call__(self, other):
104+
EARTH_RADIUS_KM = 6371
105+
106+
distance_lat = math.radians(other.latitude - self.latitude)
107+
distance_lon = math.radians(other.longitude - self.longitude)
108+
lat = math.radians(self.latitude)
109+
lon = math.radians(self.longitude)
110+
a = (
111+
math.sin(distance_lat / 2)
112+
* math.sin(distance_lat / 2)
113+
+ math.sin(distance_lon)
114+
* math.sin(distance_lon / 2)
115+
* math.cos(lat)
116+
* math.cos(lon)
117+
)
118+
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
119+
120+
return c * EARTH_RADIUS_KM

Ch7/global_coordinates_usage.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from dis import dis
2+
from global_coordinates import GlobalCoordinates
3+
nsp = GlobalCoordinates(latitude=(37, 46, 32.6, "N"),
4+
longitude=(122, 24, 39.4, "W"))
5+
print(repr(nsp))
6+
print(f"No Starch Press's offices are at {nsp}")
7+
8+
nostarch = GlobalCoordinates(latitude=(37, 46, 32.6, "N"),
9+
longitude=(122, 24, 39.4, "W"))
10+
11+
psf = GlobalCoordinates(latitude=(45, 27, 7.7, "N"),
12+
longitude=(122, 47, 30.2, "W"))
13+
14+
distance = nostarch(psf)
15+
print(distance) # 852.6857266443297

Ch7/message.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class Message:
2+
3+
def __init__(self):
4+
self.__format = "UTF-8"
5+
6+
7+
msg = Message()
8+
# print(msg.__format) # AttributeError
9+
print(msg._Message__format)

Ch7/pattern_match_object.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
class Pizza:
2+
3+
def __init__(self, topping, second_topping=None):
4+
self.first = topping
5+
self.second = second_topping
6+
7+
8+
order = Pizza("pepperoni", "mushrooms")
9+
10+
match order:
11+
case Pizza(first='pepperoni', second='mushroom'):
12+
print("ANSI standard pizza")
13+
case Pizza(first='pineapple'):
14+
print("Is this even pizza?")
15+
case Pizza(first=first, second='cheese'):
16+
print(f"Very cheesy pizza with {first}.")
17+
case Pizza(first=first, second=second):
18+
print(f"Pizza with {first} and {second}.")

Ch7/point.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class Point:
2+
__match_args__ = ('x_pos', 'y_pos', 'z_pos')
3+
4+
def __init__(self, x, y, z):
5+
self.x_pos = x
6+
self.y_pos = y
7+
self.z_pos = z
8+
9+
10+
point = Point(0, 100, 0)
11+
12+
match point:
13+
case Point(0, 0, 0):
14+
print("You are here.")
15+
case Point(0, _, 0):
16+
print("Look up!")

Ch7/secret_agent.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class SecretAgent:
2+
3+
_codeword = ""
4+
5+
def __init__(self, codename):
6+
self.codename = codename
7+
self._secrets = []
8+
9+
def __del__(self):
10+
print(f"Agent {self.codename} has been disavowed!")
11+
12+
def remember(self, secret):
13+
self._secrets.append(secret)
14+
15+
@classmethod
16+
def inform(cls, codeword):
17+
cls._codeword = codeword
18+
19+
@staticmethod
20+
def inquire(question):
21+
print("I know nothing.")

Ch7/secret_agent_disavow.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from secret_agent import SecretAgent
2+
weasel = SecretAgent("Weasel")
3+
del weasel

Ch7/secret_agent_property.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
class SecretAgent:
2+
3+
_codeword = ""
4+
5+
def __init__(self, codename):
6+
self.codename = codename
7+
self._secrets = []
8+
9+
def __del__(self):
10+
print(f"Agent {self.codename} has been disavowed!")
11+
12+
def remember(self, secret):
13+
self._secrets.append(secret)
14+
15+
@classmethod
16+
def inform(cls, codeword):
17+
cls._codeword = codeword
18+
19+
@staticmethod
20+
def inquire(question):
21+
print("I know nothing.")
22+
23+
@classmethod
24+
def _encrypt(cls, message, *, decrypt=False):
25+
code = sum(ord(c) for c in cls._codeword)
26+
if decrypt:
27+
code = -code
28+
return ''.join(chr(ord(m) + code) for m in message)
29+
30+
# METHOD 1
31+
32+
# def _getsecret(self):
33+
# return self._secrets[-1] if self._secrets else None
34+
35+
# def _setsecret(self, value):
36+
# self._secrets.append(self._encrypt(value))
37+
38+
# def _delsecret(self):
39+
# self._secrets = []
40+
41+
# secret = property(fget=_getsecret, fset=_setsecret, fdel=_delsecret)
42+
43+
# METHOD 2
44+
45+
# secret = property()
46+
47+
# @secret.getter
48+
# def secret(self):
49+
# return self._secrets[-1] if self._secrets else None
50+
51+
# @secret.setter
52+
# def secret(self, value):
53+
# self._secrets.append(self._encrypt(value))
54+
55+
# @secret.deleter
56+
# def secret(self):
57+
# self._secrets = []
58+
59+
# METHOD 3
60+
61+
@property
62+
def secret(self):
63+
return self._secrets[-1] if self._secrets else None
64+
65+
@secret.setter
66+
def secret(self, value):
67+
self._secrets.append(self._encrypt(value))
68+
69+
@secret.deleter
70+
def secret(self):
71+
self._secrets = []
72+
73+
74+
mouse = SecretAgent("Mouse")
75+
mouse.inform("Parmesano")
76+
77+
print(mouse.secret) # prints "None"
78+
mouse.secret = "12345 Main Street"
79+
print(mouse.secret) # prints "ϗϘϙϚϛφϳЇЏДφϹКИЋЋК"
80+
mouse.secret = "555-1234"
81+
print(mouse.secret) # prints "ϛϛϛϓϗϘϙϚ"
82+
83+
print(mouse._secrets) # prints two values
84+
del mouse.secret
85+
print(mouse._secrets) # prints empty list

Ch7/secret_agent_usage.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from secret_agent import SecretAgent
2+
mouse = SecretAgent("Mouse")
3+
armadillo = SecretAgent("Armadillo")
4+
fox = SecretAgent("Fox")
5+
6+
# SecretAgent._codeword = "Parseman"
7+
# print(armadillo._codeword) # prints "Parmesan"
8+
# print(mouse._codeword) # prints "Parsesan"
9+
10+
# mouse._codeword = "Cheese"
11+
# print(mouse._codeword) # prints "Cheese"
12+
# print(armadillo._codeword) # prints "Parmesan"
13+
14+
mouse.remember(("42.864025, -72.568511"))
15+
16+
SecretAgent.inform("The goose honks at midnight.")
17+
print(mouse._codeword) # prints "The goose honks at midnight."
18+
19+
fox.inform("The duck quacks at midnight.")
20+
print(mouse._codeword) # prints "The duck quacks at midnight."

0 commit comments

Comments
 (0)