ch08, 09, 10: example files
This commit is contained in:
4
09-closure-deco/README.rst
Normal file
4
09-closure-deco/README.rst
Normal file
@@ -0,0 +1,4 @@
|
||||
Sample code for Chapter 8 - "Closures and decorators"
|
||||
|
||||
From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2020)
|
||||
http://shop.oreilly.com/product/0636920273196.do
|
||||
33
09-closure-deco/average.py
Normal file
33
09-closure-deco/average.py
Normal file
@@ -0,0 +1,33 @@
|
||||
"""
|
||||
>>> avg = make_averager()
|
||||
>>> avg(10)
|
||||
10.0
|
||||
>>> avg(11)
|
||||
10.5
|
||||
>>> avg(12)
|
||||
11.0
|
||||
>>> avg.__code__.co_varnames
|
||||
('new_value', 'total')
|
||||
>>> avg.__code__.co_freevars
|
||||
('series',)
|
||||
>>> avg.__closure__ # doctest: +ELLIPSIS
|
||||
(<cell at 0x...: list object at 0x...>,)
|
||||
>>> avg.__closure__[0].cell_contents
|
||||
[10, 11, 12]
|
||||
"""
|
||||
|
||||
DEMO = """
|
||||
>>> avg.__closure__
|
||||
(<cell at 0x107a44f78: list object at 0x107a91a48>,)
|
||||
"""
|
||||
|
||||
|
||||
def make_averager():
|
||||
series = []
|
||||
|
||||
def averager(new_value):
|
||||
series.append(new_value)
|
||||
total = sum(series)
|
||||
return total/len(series)
|
||||
|
||||
return averager
|
||||
21
09-closure-deco/average_oo.py
Normal file
21
09-closure-deco/average_oo.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
>>> avg = Averager()
|
||||
>>> avg(10)
|
||||
10.0
|
||||
>>> avg(11)
|
||||
10.5
|
||||
>>> avg(12)
|
||||
11.0
|
||||
|
||||
"""
|
||||
|
||||
|
||||
class Averager():
|
||||
|
||||
def __init__(self):
|
||||
self.series = []
|
||||
|
||||
def __call__(self, new_value):
|
||||
self.series.append(new_value)
|
||||
total = sum(self.series)
|
||||
return total/len(self.series)
|
||||
21
09-closure-deco/clockdeco.py
Normal file
21
09-closure-deco/clockdeco.py
Normal file
@@ -0,0 +1,21 @@
|
||||
import time
|
||||
import functools
|
||||
|
||||
|
||||
def clock(func):
|
||||
@functools.wraps(func)
|
||||
def clocked(*args, **kwargs):
|
||||
t0 = time.perf_counter()
|
||||
result = func(*args, **kwargs)
|
||||
elapsed = time.perf_counter() - t0
|
||||
name = func.__name__
|
||||
arg_lst = []
|
||||
if args:
|
||||
arg_lst.append(', '.join(repr(arg) for arg in args))
|
||||
if kwargs:
|
||||
pairs = [f'{k}={v!r}' for k, v in kwargs.items()]
|
||||
arg_lst.append(', '.join(pairs))
|
||||
arg_str = ', '.join(arg_lst)
|
||||
print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}')
|
||||
return result
|
||||
return clocked
|
||||
13
09-closure-deco/clockdeco0.py
Normal file
13
09-closure-deco/clockdeco0.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import time
|
||||
|
||||
|
||||
def clock(func):
|
||||
def clocked(*args): # <1>
|
||||
t0 = time.perf_counter()
|
||||
result = func(*args) # <2>
|
||||
elapsed = time.perf_counter() - t0
|
||||
name = func.__name__
|
||||
arg_str = ', '.join(repr(arg) for arg in args)
|
||||
print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}')
|
||||
return result
|
||||
return clocked # <3>
|
||||
44
09-closure-deco/clockdeco_cls.py
Normal file
44
09-closure-deco/clockdeco_cls.py
Normal file
@@ -0,0 +1,44 @@
|
||||
# clockdeco_class.py
|
||||
|
||||
"""
|
||||
>>> snooze(.1) # doctest: +ELLIPSIS
|
||||
[0.101...s] snooze(0.1) -> None
|
||||
>>> clock('{name}: {elapsed}')(time.sleep)(.2) # doctest: +ELLIPSIS
|
||||
sleep: 0.20...
|
||||
>>> clock('{name}({args}) dt={elapsed:0.3f}s')(time.sleep)(.2)
|
||||
sleep(0.2) dt=0.201s
|
||||
"""
|
||||
|
||||
# tag::CLOCKDECO_CLS[]
|
||||
import time
|
||||
|
||||
DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
|
||||
|
||||
class clock: # <1>
|
||||
|
||||
def __init__(self, fmt=DEFAULT_FMT): # <2>
|
||||
self.fmt = fmt
|
||||
|
||||
def __call__(self, func): # <3>
|
||||
def clocked(*_args):
|
||||
t0 = time.perf_counter()
|
||||
_result = func(*_args) # <4>
|
||||
elapsed = time.perf_counter() - t0
|
||||
name = func.__name__
|
||||
args = ', '.join(repr(arg) for arg in _args)
|
||||
result = repr(_result)
|
||||
print(self.fmt.format(**locals()))
|
||||
return _result
|
||||
return clocked
|
||||
# end::CLOCKDECO_CLS[]
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@clock()
|
||||
def snooze(seconds):
|
||||
time.sleep(seconds)
|
||||
|
||||
for i in range(3):
|
||||
snooze(.123)
|
||||
|
||||
|
||||
19
09-closure-deco/clockdeco_demo.py
Normal file
19
09-closure-deco/clockdeco_demo.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import time
|
||||
from clockdeco import clock
|
||||
|
||||
|
||||
@clock
|
||||
def snooze(seconds):
|
||||
time.sleep(seconds)
|
||||
|
||||
|
||||
@clock
|
||||
def factorial(n):
|
||||
return 1 if n < 2 else n*factorial(n-1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print('*' * 40, 'Calling snooze(.123)')
|
||||
snooze(.123)
|
||||
print('*' * 40, 'Calling factorial(6)')
|
||||
print('6! =', factorial(6))
|
||||
40
09-closure-deco/clockdeco_param.py
Normal file
40
09-closure-deco/clockdeco_param.py
Normal file
@@ -0,0 +1,40 @@
|
||||
# clockdeco_param.py
|
||||
|
||||
"""
|
||||
>>> snooze(.1) # doctest: +ELLIPSIS
|
||||
[0.101...s] snooze(0.1) -> None
|
||||
>>> clock('{name}: {elapsed}')(time.sleep)(.2) # doctest: +ELLIPSIS
|
||||
sleep: 0.20...
|
||||
>>> clock('{name}({args}) dt={elapsed:0.3f}s')(time.sleep)(.2)
|
||||
sleep(0.2) dt=0.201s
|
||||
"""
|
||||
|
||||
# tag::CLOCKDECO_PARAM[]
|
||||
import time
|
||||
|
||||
DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
|
||||
|
||||
def clock(fmt=DEFAULT_FMT): # <1>
|
||||
def decorate(func): # <2>
|
||||
def clocked(*_args): # <3>
|
||||
t0 = time.perf_counter()
|
||||
_result = func(*_args) # <4>
|
||||
elapsed = time.perf_counter() - t0
|
||||
name = func.__name__
|
||||
args = ', '.join(repr(arg) for arg in _args) # <5>
|
||||
result = repr(_result) # <6>
|
||||
print(fmt.format(**locals())) # <7>
|
||||
return _result # <8>
|
||||
return clocked # <9>
|
||||
return decorate # <10>
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@clock() # <11>
|
||||
def snooze(seconds):
|
||||
time.sleep(seconds)
|
||||
|
||||
for i in range(3):
|
||||
snooze(.123)
|
||||
|
||||
# end::CLOCKDECO_PARAM[]
|
||||
9
09-closure-deco/clockdeco_param_demo1.py
Normal file
9
09-closure-deco/clockdeco_param_demo1.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import time
|
||||
from clockdeco_param import clock
|
||||
|
||||
@clock('{name}: {elapsed}s')
|
||||
def snooze(seconds):
|
||||
time.sleep(seconds)
|
||||
|
||||
for i in range(3):
|
||||
snooze(.123)
|
||||
9
09-closure-deco/clockdeco_param_demo2.py
Normal file
9
09-closure-deco/clockdeco_param_demo2.py
Normal file
@@ -0,0 +1,9 @@
|
||||
import time
|
||||
from clockdeco_param import clock
|
||||
|
||||
@clock('{name}({args}) dt={elapsed:0.3f}s')
|
||||
def snooze(seconds):
|
||||
time.sleep(seconds)
|
||||
|
||||
for i in range(3):
|
||||
snooze(.123)
|
||||
17
09-closure-deco/fibo_compare.py
Normal file
17
09-closure-deco/fibo_compare.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from clockdeco import clock
|
||||
import fibo_demo
|
||||
import fibo_demo_lru
|
||||
|
||||
|
||||
@clock
|
||||
def demo1():
|
||||
fibo_demo.fibonacci(30)
|
||||
|
||||
|
||||
@clock
|
||||
def demo2():
|
||||
fibo_demo_lru.fibonacci(30)
|
||||
|
||||
|
||||
demo1()
|
||||
demo2()
|
||||
12
09-closure-deco/fibo_demo.py
Normal file
12
09-closure-deco/fibo_demo.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from clockdeco import clock
|
||||
|
||||
|
||||
@clock
|
||||
def fibonacci(n):
|
||||
if n < 2:
|
||||
return n
|
||||
return fibonacci(n-2) + fibonacci(n-1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(fibonacci(6))
|
||||
15
09-closure-deco/fibo_demo_lru.py
Normal file
15
09-closure-deco/fibo_demo_lru.py
Normal file
@@ -0,0 +1,15 @@
|
||||
import functools
|
||||
|
||||
from clockdeco import clock
|
||||
|
||||
|
||||
@functools.lru_cache # <1>
|
||||
@clock # <2>
|
||||
def fibonacci(n):
|
||||
if n < 2:
|
||||
return n
|
||||
return fibonacci(n-2) + fibonacci(n-1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(fibonacci(6))
|
||||
124
09-closure-deco/global_x_local.rst
Normal file
124
09-closure-deco/global_x_local.rst
Normal file
@@ -0,0 +1,124 @@
|
||||
>>> def f1(a):
|
||||
... print(a)
|
||||
... print(b)
|
||||
...
|
||||
>>> f1(3)
|
||||
3
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "<stdin>", line 3, in f1
|
||||
NameError: name 'b' is not defined
|
||||
>>> b = 6
|
||||
>>> f1(3)
|
||||
3
|
||||
6
|
||||
|
||||
>>> def f2(a):
|
||||
... print(a)
|
||||
... print(b)
|
||||
... b = 9
|
||||
...
|
||||
>>> f2(3)
|
||||
3
|
||||
Traceback (most recent call last):
|
||||
File "<stdin>", line 1, in <module>
|
||||
File "<stdin>", line 3, in f2
|
||||
UnboundLocalError: local variable 'b' referenced before assignment
|
||||
|
||||
|
||||
# tag::F1_DIS[]
|
||||
>>> from dis import dis
|
||||
>>> dis(f1)
|
||||
2 0 LOAD_GLOBAL 0 (print) <1>
|
||||
3 LOAD_FAST 0 (a) <2>
|
||||
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
9 POP_TOP
|
||||
|
||||
3 10 LOAD_GLOBAL 0 (print)
|
||||
13 LOAD_GLOBAL 1 (b) <3>
|
||||
16 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
19 POP_TOP
|
||||
20 LOAD_CONST 0 (None)
|
||||
23 RETURN_VALUE
|
||||
# end::F1_DIS[]
|
||||
# tag::F2_DIS[]
|
||||
>>> dis(f2)
|
||||
2 0 LOAD_GLOBAL 0 (print)
|
||||
3 LOAD_FAST 0 (a)
|
||||
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
9 POP_TOP
|
||||
|
||||
3 10 LOAD_GLOBAL 0 (print)
|
||||
13 LOAD_FAST 1 (b) <1>
|
||||
16 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
19 POP_TOP
|
||||
|
||||
4 20 LOAD_CONST 1 (9)
|
||||
23 STORE_FAST 1 (b)
|
||||
26 LOAD_CONST 0 (None)
|
||||
29 RETURN_VALUE
|
||||
# end::F2_DIS[]
|
||||
>>> def f3(a):
|
||||
... global b
|
||||
... print(a)
|
||||
... print(b)
|
||||
... b = 9
|
||||
...
|
||||
>>> f3(3)
|
||||
3
|
||||
6
|
||||
>>> b
|
||||
9
|
||||
# tag::F3_DIS[]
|
||||
>>> dis(f3)
|
||||
3 0 LOAD_GLOBAL 0 (print)
|
||||
3 LOAD_FAST 0 (a)
|
||||
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
9 POP_TOP
|
||||
|
||||
4 10 LOAD_GLOBAL 0 (print)
|
||||
13 LOAD_GLOBAL 1 (b)
|
||||
16 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
19 POP_TOP
|
||||
|
||||
5 20 LOAD_CONST 1 (9)
|
||||
23 STORE_GLOBAL 1 (b)
|
||||
26 LOAD_CONST 0 (None)
|
||||
29 RETURN_VALUE
|
||||
# end::F3_DIS[]
|
||||
|
||||
>>> def f4(b):
|
||||
... def f5(a):
|
||||
... nonlocal b
|
||||
... print(a)
|
||||
... print(b)
|
||||
... b = 7
|
||||
... return f5
|
||||
...
|
||||
>>> f5 = f4(8)
|
||||
>>> f5(2)
|
||||
2
|
||||
8
|
||||
>>> b
|
||||
9
|
||||
>>> f5(3)
|
||||
3
|
||||
7????
|
||||
|
||||
>>> dis(f5)
|
||||
4 0 LOAD_GLOBAL 0 (print)
|
||||
3 LOAD_FAST 0 (a)
|
||||
6 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
9 POP_TOP
|
||||
|
||||
5 10 LOAD_GLOBAL 0 (print)
|
||||
13 LOAD_DEREF 0 (b)
|
||||
16 CALL_FUNCTION 1 (1 positional, 0 keyword pair)
|
||||
19 POP_TOP
|
||||
|
||||
6 20 LOAD_CONST 1 (7)
|
||||
23 STORE_DEREF 0 (b)
|
||||
26 LOAD_CONST 0 (None)
|
||||
29 RETURN_VALUE
|
||||
|
||||
|
||||
76
09-closure-deco/htmlizer.py
Normal file
76
09-closure-deco/htmlizer.py
Normal file
@@ -0,0 +1,76 @@
|
||||
r"""
|
||||
htmlize(): generic function example
|
||||
|
||||
# tag::HTMLIZE_DEMO[]
|
||||
|
||||
>>> htmlize({1, 2, 3}) # <1>
|
||||
'<pre>{1, 2, 3}</pre>'
|
||||
>>> htmlize(abs)
|
||||
'<pre><built-in function abs></pre>'
|
||||
>>> htmlize('Heimlich & Co.\n- a game') # <2>
|
||||
'<p>Heimlich & Co.<br>\n- a game</p>'
|
||||
>>> htmlize(42) # <3>
|
||||
'<pre>42 (0x2a)</pre>'
|
||||
>>> print(htmlize(['alpha', 66, {3, 2, 1}])) # <4>
|
||||
<ul>
|
||||
<li><p>alpha</p></li>
|
||||
<li><pre>66 (0x42)</pre></li>
|
||||
<li><pre>{1, 2, 3}</pre></li>
|
||||
</ul>
|
||||
>>> htmlize(True) # <5>
|
||||
'<pre>True</pre>'
|
||||
>>> htmlize(fractions.Fraction(2, 3)) # <6>
|
||||
'<pre>2/3</pre>'
|
||||
>>> htmlize(2/3) # <7>
|
||||
'<pre>0.6666666666666666 (2/3)</pre>'
|
||||
>>> htmlize(decimal.Decimal('0.02380952'))
|
||||
'<pre>0.02380952 (1/42)</pre>'
|
||||
|
||||
# end::HTMLIZE_DEMO[]
|
||||
"""
|
||||
|
||||
# tag::HTMLIZE[]
|
||||
|
||||
from functools import singledispatch
|
||||
from collections import abc
|
||||
import fractions
|
||||
import decimal
|
||||
import html
|
||||
import numbers
|
||||
|
||||
@singledispatch # <1>
|
||||
def htmlize(obj: object) -> str:
|
||||
content = html.escape(repr(obj))
|
||||
return f'<pre>{content}</pre>'
|
||||
|
||||
@htmlize.register # <2>
|
||||
def _(text: str) -> str: # <3>
|
||||
content = html.escape(text).replace('\n', '<br>\n')
|
||||
return f'<p>{content}</p>'
|
||||
|
||||
@htmlize.register # <4>
|
||||
def _(seq: abc.Sequence) -> str:
|
||||
inner = '</li>\n<li>'.join(htmlize(item) for item in seq)
|
||||
return '<ul>\n<li>' + inner + '</li>\n</ul>'
|
||||
|
||||
@htmlize.register # <5>
|
||||
def _(n: numbers.Integral) -> str:
|
||||
return f'<pre>{n} (0x{n:x})</pre>'
|
||||
|
||||
@htmlize.register # <6>
|
||||
def _(n: bool) -> str:
|
||||
return f'<pre>{n}</pre>'
|
||||
|
||||
@htmlize.register(fractions.Fraction) # <7>
|
||||
def _(x) -> str:
|
||||
frac = fractions.Fraction(x)
|
||||
return f'<pre>{frac.numerator}/{frac.denominator}</pre>'
|
||||
|
||||
@htmlize.register(decimal.Decimal) # <8>
|
||||
@htmlize.register(float)
|
||||
def _(x) -> str:
|
||||
frac = fractions.Fraction(x).limit_denominator()
|
||||
return f'<pre>{x} ({frac.numerator}/{frac.denominator})</pre>'
|
||||
|
||||
# end::HTMLIZE[]
|
||||
|
||||
31
09-closure-deco/registration.py
Normal file
31
09-closure-deco/registration.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# tag::REGISTRATION[]
|
||||
|
||||
registry = [] # <1>
|
||||
|
||||
def register(func): # <2>
|
||||
print(f'running register({func})') # <3>
|
||||
registry.append(func) # <4>
|
||||
return func # <5>
|
||||
|
||||
@register # <6>
|
||||
def f1():
|
||||
print('running f1()')
|
||||
|
||||
@register
|
||||
def f2():
|
||||
print('running f2()')
|
||||
|
||||
def f3(): # <7>
|
||||
print('running f3()')
|
||||
|
||||
def main(): # <8>
|
||||
print('running main()')
|
||||
print('registry ->', registry)
|
||||
f1()
|
||||
f2()
|
||||
f3()
|
||||
|
||||
if __name__=='__main__':
|
||||
main() # <9>
|
||||
|
||||
# end::REGISTRATION[]
|
||||
16
09-closure-deco/registration_abridged.py
Normal file
16
09-closure-deco/registration_abridged.py
Normal file
@@ -0,0 +1,16 @@
|
||||
# tag::REGISTRATION_ABRIDGED[]
|
||||
registry = []
|
||||
|
||||
def register(func):
|
||||
print(f'running register({func})')
|
||||
registry.append(func)
|
||||
return func
|
||||
|
||||
@register
|
||||
def f1():
|
||||
print('running f1()')
|
||||
|
||||
print('running main()')
|
||||
print('registry ->', registry)
|
||||
f1()
|
||||
# end::REGISTRATION_ABRIDGED[]
|
||||
28
09-closure-deco/registration_param.py
Normal file
28
09-closure-deco/registration_param.py
Normal file
@@ -0,0 +1,28 @@
|
||||
# tag::REGISTRATION_PARAM[]
|
||||
|
||||
registry = set() # <1>
|
||||
|
||||
def register(active=True): # <2>
|
||||
def decorate(func): # <3>
|
||||
print('running register'
|
||||
f'(active={active})->decorate({func})')
|
||||
if active: # <4>
|
||||
registry.add(func)
|
||||
else:
|
||||
registry.discard(func) # <5>
|
||||
|
||||
return func # <6>
|
||||
return decorate # <7>
|
||||
|
||||
@register(active=False) # <8>
|
||||
def f1():
|
||||
print('running f1()')
|
||||
|
||||
@register() # <9>
|
||||
def f2():
|
||||
print('running f2()')
|
||||
|
||||
def f3():
|
||||
print('running f3()')
|
||||
|
||||
# end::REGISTRATION_PARAM[]
|
||||
36
09-closure-deco/stacked.py
Normal file
36
09-closure-deco/stacked.py
Normal file
@@ -0,0 +1,36 @@
|
||||
def first(f):
|
||||
print(f'apply first({f.__name__})')
|
||||
|
||||
def inner1st(n):
|
||||
result = f(n)
|
||||
print(f'inner1({n}): called {f.__name__}({n}) -> {result}')
|
||||
return result
|
||||
return inner1st
|
||||
|
||||
|
||||
def second(f):
|
||||
print(f'apply second({f.__name__})')
|
||||
|
||||
def inner2nd(n):
|
||||
result = f(n)
|
||||
print(f'inner2({n}): called {f.__name__}({n}) -> {result}')
|
||||
return result
|
||||
return inner2nd
|
||||
|
||||
|
||||
@first
|
||||
@second
|
||||
def double(n):
|
||||
return n * 2
|
||||
|
||||
|
||||
print(double(3))
|
||||
|
||||
|
||||
def double_(n):
|
||||
return n * 2
|
||||
|
||||
|
||||
double_ = first(second(double_))
|
||||
|
||||
print(double_(3))
|
||||
Reference in New Issue
Block a user