updated contents from Atlas repo

This commit is contained in:
Luciano Ramalho
2014-10-14 14:26:55 -03:00
parent 40688c038d
commit 981d5bc473
157 changed files with 71134 additions and 1 deletions

24
classes/mem_test.py Normal file
View File

@@ -0,0 +1,24 @@
import importlib
import sys
import resource
NUM_VECTORS = 10**7
if len(sys.argv) == 2:
module_name = sys.argv[1].replace('.py', '')
module = importlib.import_module(module_name)
else:
print('Usage: {} <vector-module-to-test>'.format())
sys.exit(1)
fmt = 'Selected Vector type: {.__name__}.{.__name__}'
print(fmt.format(module, module.Vector))
mem_init = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
print('Creating {:,} Vector instances'.format(NUM_VECTORS))
vectors = [module.Vector(3.0, 4.0) for i in range(NUM_VECTORS)]
mem_final = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
print('Initial RAM usage: {:14,}'.format(mem_init))
print(' Final RAM usage: {:14,}'.format(mem_final))

60
classes/vector_v0.py Normal file
View File

@@ -0,0 +1,60 @@
"""
A 2-dimensional vector class
# BEGIN VECTOR_V0_DEMO
>>> v1 = Vector(3, 4)
>>> x, y = v1 #<1>
>>> x, y
(3.0, 4.0)
>>> v1 #<2>
Vector(3.0, 4.0)
>>> v1_clone = eval(repr(v1)) #<3>
>>> v1 == v1_clone
True
>>> print(v1) #<4>
(3.0, 4.0)
>>> octets = bytes(v1) #<5>
>>> octets
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1) #<6>
5.0
>>> bool(v1), bool(Vector(0, 0)) #<7>
(True, False)
# END VECTOR_V0_DEMO
"""
# BEGIN VECTOR_V0
from array import array
import math
class Vector:
typecode = 'd' # <1>
def __init__(self, x, y):
self.x = float(x) # <2>
self.y = float(y)
def __iter__(self):
return (i for i in (self.x, self.y)) # <3>
def __repr__(self):
return 'Vector({!r}, {!r})'.format(*self) # <4>
def __str__(self):
return str(tuple(self)) # <5>
def __bytes__(self):
return bytes(array(Vector.typecode, self)) # <6>
def __eq__(self, other):
return tuple(self) == tuple(other) # <7>
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self)) # <8>
# END VECTOR_V0

86
classes/vector_v1.py Normal file
View File

@@ -0,0 +1,86 @@
"""
A 2-dimensional vector class
>>> v1 = Vector(3, 4)
>>> x, y = v1 #<1>
>>> x, y
(3.0, 4.0)
>>> v1 #<2>
Vector(3.0, 4.0)
>>> v1_clone = eval(repr(v1)) #<3>
>>> v1 == v1_clone
True
>>> print(v1) #<4>
(3.0, 4.0)
>>> octets = bytes(v1) #<5>
>>> octets
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1) #<6>
5.0
>>> bool(v1), bool(Vector(0, 0)) #<7>
(True, False)
Test of .frombytes() class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector(3.0, 4.0)
>>> v1 == v1_clone
True
So far, Vector instances are unhashable:
# BEGIN VECTOR_V1_UNHASHABLE_DEMO
>>> v1 = Vector(3, 4)
>>> hash(v1)
Traceback (most recent call last):
...
TypeError: unhashable type: 'Vector'
>>> set([v1])
Traceback (most recent call last):
...
TypeError: unhashable type: 'Vector'
# END VECTOR_V1_UNHASHABLE_DEMO
"""
from array import array
import math
class Vector:
typecode = 'd'
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
def __iter__(self):
return (i for i in (self.x, self.y))
def __repr__(self):
return 'Vector({!r}, {!r})'.format(*self)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return bytes(array(Vector.typecode, self))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
# BEGIN VECTOR_V1
@classmethod # <1>
def frombytes(cls, octets): # <2>
arr = array(Vector.typecode) # <3>
arr.frombytes(octets) # <4>
return cls(*arr) # <5>
# END VECTOR_V1

113
classes/vector_v2.py Normal file
View File

@@ -0,0 +1,113 @@
"""
A 2-dimensional vector class
>>> v1 = Vector(3, 4)
>>> x, y = v1
>>> x, y
(3.0, 4.0)
>>> v1
Vector(3.0, 4.0)
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0)
>>> octets = bytes(v1)
>>> octets
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector(0, 0))
(True, False)
Test of ``.frombytes()`` class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector(3.0, 4.0)
>>> v1 == v1_clone
True
Tests of ``format()`` with rectangular coordinates:
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'
Tests of the ``angle`` method::
>>> Vector(0, 0).angle()
0.0
>>> Vector(1, 0).angle()
0.0
>>> epsilon = 10**-8
>>> abs(Vector(0, 1).angle() - math.pi/2) < epsilon
True
>>> abs(Vector(1, 1).angle() - math.pi/4) < epsilon
True
Tests of ``format()`` with polar coordinates:
>>> format(Vector(1, 1), 'p') # doctest:+ELLIPSIS
'<1.414213..., 0.785398...>'
>>> format(Vector(1, 1), '.3ep')
'<1.414e+00, 7.854e-01>'
>>> format(Vector(1, 1), '0.5fp')
'<1.41421, 0.78540>'
"""
from array import array
import math
class Vector:
typecode = 'd'
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
def __iter__(self):
return (i for i in (self.x, self.y))
def __repr__(self):
return 'Vector({!r}, {!r})'.format(*self)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return bytes(array(Vector.typecode, self))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def angle(self):
return math.atan2(self.y, self.x)
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('p'):
fmt_spec = fmt_spec[:-1]
coords = (abs(self), self.angle())
outer_fmt = '<{}, {}>'
else:
coords = self
outer_fmt = '({}, {})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(*components)
@classmethod
def frombytes(cls, octets):
arr = array(Vector.typecode)
arr.frombytes(octets)
return cls(*arr)

View File

@@ -0,0 +1,115 @@
"""
A 2-dimensional vector class
>>> v1 = Vector(3, 4)
>>> x, y = v1
>>> x, y
(3.0, 4.0)
>>> v1
Vector(3.0, 4.0)
>>> v1_clone = eval(repr(v1))
>>> v1 == v1_clone
True
>>> print(v1)
(3.0, 4.0)
>>> octets = bytes(v1)
>>> octets
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1)
5.0
>>> bool(v1), bool(Vector(0, 0))
(True, False)
Test of ``.frombytes()`` class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector(3.0, 4.0)
>>> v1 == v1_clone
True
Tests of ``format()`` with rectangular coordinates:
>>> format(v1)
'(3.0, 4.0)'
>>> format(v1, '.2f')
'(3.00, 4.00)'
>>> format(v1, '.3e')
'(3.000e+00, 4.000e+00)'
Tests of the ``angle`` method::
>>> Vector(0, 0).angle()
0.0
>>> Vector(1, 0).angle()
0.0
>>> epsilon = 10**-8
>>> abs(Vector(0, 1).angle() - math.pi/2) < epsilon
True
>>> abs(Vector(1, 1).angle() - math.pi/4) < epsilon
True
Tests of ``format()`` with polar coordinates:
>>> format(Vector(1, 1), 'p') # doctest:+ELLIPSIS
'<1.414213..., 0.785398...>'
>>> format(Vector(1, 1), '.3ep')
'<1.414e+00, 7.854e-01>'
>>> format(Vector(1, 1), '0.5fp')
'<1.41421, 0.78540>'
"""
from array import array
import math
class Vector:
typecode = 'd'
def __init__(self, x, y):
self.x = float(x)
self.y = float(y)
def __iter__(self):
return (i for i in (self.x, self.y))
def __repr__(self):
return 'Vector({!r}, {!r})'.format(*self)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return bytes(array(Vector.typecode, self))
def __eq__(self, other):
return tuple(self) == tuple(other)
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def angle(self):
return math.atan2(self.y, self.x)
# BEGIN VECTOR_V2_FORMAT
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('p'): # <1>
fmt_spec = fmt_spec[:-1] # <2>
coords = (abs(self), self.angle()) # <3>
outer_fmt = '<{}, {}>' # <4>
else:
coords = self # <5>
outer_fmt = '({}, {})' # <6>
components = (format(c, fmt_spec) for c in coords) # <7>
return outer_fmt.format(*components) # <8>
# END VECTOR_V2_FORMAT
@classmethod
def frombytes(cls, octets):
arr = array(Vector.typecode)
arr.frombytes(octets)
return cls(*arr)

123
classes/vector_v3.py Normal file
View File

@@ -0,0 +1,123 @@
"""
A 2-dimensional vector class
>>> v1 = Vector(3, 4)
>>> x, y = v1 #<1>
>>> x, y
(3.0, 4.0)
>>> v1 #<2>
Vector(3.0, 4.0)
>>> v1_clone = eval(repr(v1)) #<3>
>>> v1 == v1_clone
True
>>> print(v1) #<4>
(3.0, 4.0)
>>> octets = bytes(v1) #<5>
>>> octets
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1) #<6>
5.0
>>> bool(v1), bool(Vector(0, 0)) #<7>
(True, False)
Test of .frombytes() class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector(3.0, 4.0)
>>> v1 == v1_clone
True
# BEGIN VECTOR_V3_DEMO
Test of `x` and `y` read-only properties:
>>> v1.x, v1.y
(3.0, 4.0)
>>> v1.x = 123
Traceback (most recent call last):
...
AttributeError: can't set attribute
# END VECTOR_V3_HASH_DEMO
# BEGIN VECTOR_V3_HASH_DEMO
>>> v1 = Vector(3, 4)
>>> v2 = Vector(3.1, 4.2)
>>> hash(v1), hash(v2)
(7, 384307168202284039)
>>> set([v1, v2])
{Vector(3.1, 4.2), Vector(3.0, 4.0)}
# END VECTOR_V3_DEMO
"""
from array import array
import math
# BEGIN VECTOR_V3
class Vector:
typecode = 'd'
def __init__(self, x, y):
self.__x = float(x) # <1>
self.__y = float(y)
@property # <2>
def x(self): # <3>
return self.__x # <4>
@property # <5>
def y(self):
return self.__y
def __iter__(self):
return (i for i in (self.x, self.y)) # <6>
# remaining methods follow (omitted in book listing)
# END VECTOR_V3
def __repr__(self):
return 'Vector({!r}, {!r})'.format(*self)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return bytes(array(Vector.typecode, self))
def __eq__(self, other):
return tuple(self) == tuple(other)
# BEGIN VECTOR_V3_HASH
def __hash__(self):
return hash(self.x) ^ hash(self.y)
# END VECTOR_V3_HASH
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def angle(self):
return math.atan2(self.y, self.x)
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('p'):
fmt_spec = fmt_spec[:-1]
coords = (abs(self), self.angle())
outer_fmt = '<{}, {}>'
else:
coords = self
outer_fmt = '({}, {})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(*components)
@classmethod
def frombytes(cls, octets):
arr = array(Vector.typecode)
arr.frombytes(octets)
return cls(*arr)

125
classes/vector_v3_slots.py Normal file
View File

@@ -0,0 +1,125 @@
"""
A 2-dimensional vector class
>>> v1 = Vector(3, 4)
>>> x, y = v1 #<1>
>>> x, y
(3.0, 4.0)
>>> v1 #<2>
Vector(3.0, 4.0)
>>> v1_clone = eval(repr(v1)) #<3>
>>> v1 == v1_clone
True
>>> print(v1) #<4>
(3.0, 4.0)
>>> octets = bytes(v1) #<5>
>>> octets
b'\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@'
>>> abs(v1) #<6>
5.0
>>> bool(v1), bool(Vector(0, 0)) #<7>
(True, False)
Test of .frombytes() class method:
>>> v1_clone = Vector.frombytes(bytes(v1))
>>> v1_clone
Vector(3.0, 4.0)
>>> v1 == v1_clone
True
# BEGIN VECTOR_V3_DEMO
Test of `x` and `y` read-only properties:
>>> v1.x, v1.y
(3.0, 4.0)
>>> v1.x = 123
Traceback (most recent call last):
...
AttributeError: can't set attribute
# END VECTOR_V3_HASH_DEMO
# BEGIN VECTOR_V3_HASH_DEMO
>>> v1 = Vector(3, 4)
>>> v2 = Vector(3.1, 4.2)
>>> hash(v1), hash(v2)
(7, 384307168202284039)
>>> set([v1, v2])
{Vector(3.1, 4.2), Vector(3.0, 4.0)}
# END VECTOR_V3_DEMO
"""
from array import array
import math
# BEGIN VECTOR_V3_SLOTS
class Vector:
__slots__ = ('__x', '__y')
typecode = 'd'
# methods follow (omitted in book listing)
# END VECTOR_V3_SLOTS
def __init__(self, x, y):
self.__x = float(x)
self.__y = float(y)
@property
def x(self):
return self.__x
@property
def y(self):
return self.__y
def __iter__(self):
return (i for i in (self.x, self.y)) # <6>
def __repr__(self):
return 'Vector({!r}, {!r})'.format(*self)
def __str__(self):
return str(tuple(self))
def __bytes__(self):
return bytes(array(Vector.typecode, self))
def __eq__(self, other):
return tuple(self) == tuple(other)
# BEGIN VECTOR_V3_HASH
def __hash__(self):
return hash(self.x) ^ hash(self.y)
# END VECTOR_V3_HASH
def __abs__(self):
return math.hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def angle(self):
return math.atan2(self.y, self.x)
def __format__(self, fmt_spec=''):
if fmt_spec.endswith('p'):
fmt_spec = fmt_spec[:-1]
coords = (abs(self), self.angle())
outer_fmt = '<{}, {}>'
else:
coords = self
outer_fmt = '({}, {})'
components = (format(c, fmt_spec) for c in coords)
return outer_fmt.format(*components)
@classmethod
def frombytes(cls, octets):
arr = array(Vector.typecode)
arr.frombytes(octets)
return cls(*arr)

124
closures/global_x_local.rst Normal file
View 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
# BEGIN 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
# BEGIN 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
# BEGIN 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

View File

@@ -0,0 +1,54 @@
>>> from frenchdeck import FrenchDeck, Card
>>> beer_card = Card('7', 'diamonds')
>>> beer_card
Card(rank='7', suit='diamonds')
>>> deck = FrenchDeck()
>>> len(deck)
52
>>> deck[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
>>> deck[12::13]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
>>> Card('Q', 'hearts') in deck
True
>>> Card('Z', 'clubs') in deck
False
>>> for card in deck: # doctest: +ELLIPSIS
... print(card)
Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...
>>> for card in reversed(deck): # doctest: +ELLIPSIS
... print(card)
Card(rank='A', suit='hearts')
Card(rank='K', suit='hearts')
Card(rank='Q', suit='hearts')
...
>>> for n, card in enumerate(deck, 1): # doctest: +ELLIPSIS
... print(n, card)
1 Card(rank='2', suit='spades')
2 Card(rank='3', suit='spades')
3 Card(rank='4', suit='spades')
...
>>> def alt_color_rank(card):
... rank_value = FrenchDeck.ranks.index(card.rank)
... suits = 'diamonds clubs hearts spades'.split()
... return rank_value * len(suits) + suits.index(card.suit)
Rank test:
>>> alt_color_rank(Card('2', 'diamonds'))
0
>>> alt_color_rank(Card('A', 'spades'))
51
>>> for card in sorted(deck, key=alt_color_rank): # doctest: +ELLIPSIS
... print(card)
Card(rank='2', suit='diamonds')
Card(rank='2', suit='clubs')
Card(rank='2', suit='hearts')
...
Card(rank='A', suit='clubs')
Card(rank='A', suit='hearts')
Card(rank='A', suit='spades')

17
datamodel/frenchdeck.py Normal file
View File

@@ -0,0 +1,17 @@
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]

4
datamodel/tox.ini Normal file
View File

@@ -0,0 +1,4 @@
[pep8]
ignore = E127,E302
# E127: continuation line over-indented for visual indent
# E302: expected 2 blank lines, found 1

24
datamodel/vector2d.py Normal file
View File

@@ -0,0 +1,24 @@
from math import hypot
class Vector:
def __init__(self, x=0, y=0):
self.x = x
self.y = y
def __repr__(self):
return 'Vector(%r, %r)' % (self.x, self.y)
def __abs__(self):
return hypot(self.x, self.y)
def __bool__(self):
return bool(abs(self))
def __add__(self, other):
x = self.x + other.x
y = self.y + other.y
return Vector(x, y)
def __mul__(self, scalar):
return Vector(self.x * scalar, self.y * scalar)

33
decorators/average.py Normal file
View 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

View File

@@ -0,0 +1,20 @@
"""
>>> avg = make_averager()
>>> avg(10)
Traceback (most recent call last):
...
UnboundLocalError: local variable 'num_items' referenced before assignment
"""
def make_averager():
num_items = 0
total = 0
def averager(new_value):
num_items += 1
total += new_value
return total / num_items
return averager

View File

@@ -0,0 +1,45 @@
"""
>>> avg = make_averager()
>>> other_avg = make_averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0
>>> avg.__code__.co_varnames
('new_value',)
>>> avg.__code__.co_freevars
('num_items', 'total')
>>> avg.__closure__ # doctest: +ELLIPSIS
(<cell at 0x...: int object at 0x...>, <cell at 0x...: int object at 0x...>)
>>> avg.__closure__[0].cell_contents
3
>>> avg.__closure__[1].cell_contents
33
>>> other_avg(5)
5.0
>>> other_avg(10)
7.5
>>> other_avg(15)
10.0
"""
DEMO = """
>>> avg.__closure__
(<cell at 0x10fd24f78: int object at 0x10f6d3db0>,
<cell at 0x10fd24d38: int object at 0x10f6d4170>)
"""
def make_averager():
num_items = 0
total = 0
def averager(new_value):
nonlocal num_items, total
num_items += 1
total += new_value
return total / num_items
return averager

View File

@@ -0,0 +1,39 @@
"""
>>> avg = make_averager()
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0
>>> avg.__code__.co_varnames
('new_value',)
>>> avg.__code__.co_freevars
('ns',)
>>> avg.__closure__ # doctest: +ELLIPSIS
(<cell at 0x...: Namespace object at 0x...>,)
>>> avg.__closure__[0].cell_contents.__dict__
{'total': 33, 'num_items': 3}
"""
DEMO = """
>>> avg.__closure__
(<cell at 0x108df5980: Namespace object at 0x108e06790>,)
"""
class Namespace(object):
pass
def make_averager():
ns = Namespace()
ns.num_items = 0
ns.total = 0
def averager(new_value):
ns.num_items += 1
ns.total += new_value
return float(ns.total) / ns.num_items
return averager

21
decorators/average_oo.py Normal file
View 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)

View File

@@ -0,0 +1,34 @@
"""
>>> import functools
>>> avg = functools.partial(averager, series=[])
>>> avg(10)
10.0
>>> avg(11)
10.5
>>> avg(12)
11.0
>>> avg.args
()
>>> avg.keywords
{'series': [10, 11, 12]}
>>> avg.func # doctest: +ELLIPSIS
<function averager at 0x...>
>>> avg.func.__code__.co_varnames
('new_value', 'series', 'total')
"""
DEMO = """
>>> avg.func
<function averager at 0x1010c5560>
>>> avg.func.__code__.co_varnames
('new_value',)
>>> avg.__code__.co_freevars
('num_items', 'total')
>>> avg.__closure__
"""
def averager(new_value, series):
series.append(new_value)
total = sum(series)
return float(total)/len(series)

28
decorators/average_py2.py Normal file
View File

@@ -0,0 +1,28 @@
"""
>>> 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]
"""
def make_averager():
series = []
def averager(new_value):
series.append(new_value)
total = sum(series)
return float(total)/len(series)
return averager

15
decorators/clockdeco.py Normal file
View File

@@ -0,0 +1,15 @@
# clockdeco.py
import time
def clock(func):
def clocked(*args):
t0 = time.time()
result = func(*args)
elapsed = time.time() - t0
name = func.__name__
arg_str = ', '.join(repr(arg) for arg in args)
print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
return result
return clocked

23
decorators/clockdeco2.py Normal file
View File

@@ -0,0 +1,23 @@
# clockdeco2.py
import time
import functools
def clock(func):
@functools.wraps(func)
def clocked(*args, **kwargs):
t0 = time.time()
result = func(*args, **kwargs)
elapsed = time.time() - t0
name = func.__name__
arg_lst = []
if args:
arg_lst.append(', '.join(repr(arg) for arg in args))
if kwargs:
pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
arg_lst.append(', '.join(pairs))
arg_str = ', '.join(arg_lst)
print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
return result
return clocked

View File

@@ -0,0 +1,31 @@
# clockdec2o_demo.py
"""
>>> pythagoras(3, 4) # doctest: +ELLIPSIS
[0.0...s] pythagoras(3, 4) -> 5.0
5.0
>>> pythagoras(9, h=15) # doctest: +ELLIPSIS
[0.0...s] pythagoras(9, h=15) -> 12.0
12.0
"""
import time
import math
from clockdeco2 import clock
@clock
def pythagoras(a, b=None, h=None):
if b is None and h is None:
raise TypeError('must provide second leg (b) or hypotenuse (h)')
if h is None:
return math.sqrt(a*a + b*b)
else:
return math.sqrt(h*h - a*a)
if __name__=='__main__':
print('*' * 40, 'Calling pythagoras(3, 4)')
pythagoras(3, 4)
print('*' * 40, 'Calling pythagoras(9, h=15)')
pythagoras(9, h=15)

View File

@@ -0,0 +1,95 @@
"""
>>> f_empty()
[0.0...s] f_empty() -> None
>>> f_args('spam', 3)
[0.0...s] f_args('spam', 3) -> 'spamspamspam'
'spamspamspam'
>>> snooze(1234)
[1...s] snooze(1234) -> None
>>> average(1, 2, 3)
[0.0...s] average(1, 2, 3) -> 2.0
2.0
>>> average(*range(10**3))
[0.0...s] average(0, 1, ..., 999) -> 499.5
499.5
>>> factorial(10)
[0.000...s] factorial(1) -> 1
[0.000...s] factorial(2) -> 2
[0.000...s] factorial(3) -> 6
[0.000...s] factorial(4) -> 24
[0.000...s] factorial(5) -> 120
[0.000...s] factorial(6) -> 720
[0.000...s] factorial(7) -> 5040
[0.000...s] factorial(8) -> 40320
[0.000...s] factorial(9) -> 362880
[0.000...s] factorial(10) -> 3628800
3628800
>>> fibonacci(1)
[0.000...s] fibonacci(1) -> 1
1
>>> fibonacci(5)
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(0) -> 0
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(2) -> 1
[0.000...s] fibonacci(3) -> 2
[0.000...s] fibonacci(0) -> 0
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(2) -> 1
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(0) -> 0
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(2) -> 1
[0.000...s] fibonacci(3) -> 2
[0.000...s] fibonacci(4) -> 3
[0.000...s] fibonacci(5) -> 5
5
>>> f_kwargs(3, 5, d='spam', c='eggs')
[0.0...s] f_kwargs(3, 5, c='eggs', d='spam') -> 15
15
>>> f_args.__name__
'f_args'
>>> f_kwargs.__name__
'f_kwargs'
"""
import time
from clockdeco2 import clock
@clock
def f_empty():
pass
@clock
def f_args(a, b):
return a*b
@clock
def snooze(milis):
time.sleep(milis/1000)
@clock
def average(*args):
return sum(args) / len(args)
@clock
def factorial(n):
return 1 if n < 2 else n*factorial(n-1)
@clock
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
@clock
def f_kwargs(a, b, c=1, d='eggs'):
from time import sleep
sleep(0.001)
return a*b
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)

View File

@@ -0,0 +1,18 @@
# clockdeco_demo.py
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))

View File

@@ -0,0 +1,22 @@
from clockdeco import clock
import time
@clock
def snooze(milis):
time.sleep(milis/1000)
@clock
def factorial(n):
return 1 if n < 2 else n*factorial(n-1)
@clock
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
snooze(123)
print(factorial(6))
print(fibonacci(4))

View File

@@ -0,0 +1,20 @@
import functools
from clockdeco import clock
@functools.lru_cache()
@clock
def factorial(n):
return 1 if n < 2 else n*factorial(n-1)
@functools.lru_cache()
@clock
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
print(factorial(6))
print(fibonacci(6))

View 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
"""
# BEGIN 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.time()
_result = func(*_args) # <4>
elapsed = time.time() - 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

View 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)

View 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)

View File

@@ -0,0 +1,82 @@
"""
>>> f_empty()
[0.0...s] f_empty() -> None
>>> f_args('spam', 3)
[0.0...s] f_args('spam', 3) -> 'spamspamspam'
'spamspamspam'
>>> snooze(1234)
[1...s] snooze(1234) -> None
>>> average(1, 2, 3)
[0.0...s] average(1, 2, 3) -> 2.0
2.0
>>> average(*range(10**3))
[0.0...s] average(0, 1, ..., 999) -> 499.5
499.5
>>> factorial(10)
[0.000...s] factorial(1) -> 1
[0.000...s] factorial(2) -> 2
[0.000...s] factorial(3) -> 6
[0.000...s] factorial(4) -> 24
[0.000...s] factorial(5) -> 120
[0.000...s] factorial(6) -> 720
[0.000...s] factorial(7) -> 5040
[0.000...s] factorial(8) -> 40320
[0.000...s] factorial(9) -> 362880
[0.000...s] factorial(10) -> 3628800
3628800
>>> fibonacci(1)
[0.000...s] fibonacci(1) -> 1
1
>>> fibonacci(5)
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(0) -> 0
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(2) -> 1
[0.000...s] fibonacci(3) -> 2
[0.000...s] fibonacci(0) -> 0
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(2) -> 1
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(0) -> 0
[0.000...s] fibonacci(1) -> 1
[0.000...s] fibonacci(2) -> 1
[0.000...s] fibonacci(3) -> 2
[0.000...s] fibonacci(4) -> 3
[0.000...s] fibonacci(5) -> 5
5
>>> f_args.__name__
'clocked'
"""
import time
from clockdeco import clock
@clock
def f_empty():
pass
@clock
def f_args(a, b):
return a*b
@clock
def snooze(milis):
time.sleep(milis/1000)
@clock
def average(*args):
return sum(args) / len(args)
@clock
def factorial(n):
return 1 if n < 2 else n*factorial(n-1)
@clock
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)

50
decorators/currency.py Normal file
View File

@@ -0,0 +1,50 @@
# currency.py
"""
>>> convert(1, 'BRL', 'USD')
0.4591
>>> convert(1, 'USD', 'BRL')
2.1784
>>> convert(1, 'EUR', 'USD')
1.3482
>>> convert(1, 'USD', 'EUR')
0.7417
>>> convert(1, 'EUR', 'BRL')
2.9369
>>> convert(1, 'BRL', 'EUR')
0.3405
>>> from functools import partial
>>> eur = partial(convert, cur_to='EUR')
>>> eur(1, 'USD')
0.7417
>>> eur(1, 'BRL')
0.3405
>>> eur2brl = partial(convert, cur_from='EUR', cur_to='BRL')
>>> eur2brl(100)
293.6864
>>> type(eur2brl)
<class 'functools.partial'>
"""
DEMO = """
>>> eur2brl.func
<function convert at 0x1010c5560>
>>> eur2brl.args, eur2brl.keywords
((), {'cur_from': 'EUR', 'cur_to': 'BRL'})
"""
rates = {'BRL': 2.17836,
'CAD': 1.03615,
'CNY': 6.10562,
'EUR': 0.74173,
'GBP': 0.62814,
'INR': 61.8685,
'JPY': 98.6002,
'USD': 1.0}
reference = rates['USD']
def convert(amount, cur_from, cur_to):
ref_amount = reference / rates[cur_from] * amount
return round(ref_amount * rates[cur_to], 4)

10
decorators/fibo_demo.py Normal file
View File

@@ -0,0 +1,10 @@
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))

View File

@@ -0,0 +1,13 @@
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))

60
decorators/fibonacci.py Normal file
View File

@@ -0,0 +1,60 @@
# source: http://oeis.org/A000045
fibo_seq = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610,
987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025,
121393, 196418, 317811, 514229, 832040, 1346269, 2178309,
3524578, 5702887, 9227465, 14930352, 24157817, 39088169]
from functools import lru_cache
def fibonacci(n):
if n < 2:
return n
return fibonacci(n-2) + fibonacci(n-1)
@lru_cache()
def fibonacci2(n):
if n < 2:
return n
return fibonacci2(n-2) + fibonacci2(n-1)
def memoize(func):
'''simplest memoizing decorator'''
cache = {}
def memoized(*args):
if args not in cache:
cache[args] = func(*args)
return cache[args]
return memoized
def test():
for i, expected in enumerate(fibo_seq[:31]):
print(i, expected)
assert fibonacci(i) == expected
def chronograph():
global fibonacci
from time import time
t0 = time()
n = 32
res = fibonacci(n)
#res = [fibonacci(n) for n in range(30)]
t1 = time()
print(n, res, format(t1-t0, '0.6f'))
t0 = time()
res = fibonacci2(n)
#res = [fibonacci2(n) for n in range(30)]
t1 = time()
print(n, res, format(t1-t0, '0.6f'))
t0 = time()
fibonacci = memoize(fibonacci)
res = fibonacci(n)
#res = [fibonacci2(n) for n in range(30)]
t1 = time()
print(n, res, format(t1-t0, '0.6f'))
if __name__=='__main__':
#test()
chronograph()

49
decorators/generic.py Normal file
View File

@@ -0,0 +1,49 @@
r"""
htmlize(): generic function example
# BEGIN HTMLIZE_DEMO
>>> htmlize({1, 2, 3}) # <1>
'<pre>{1, 2, 3}</pre>'
>>> htmlize(abs)
'<pre>&lt;built-in function abs&gt;</pre>'
>>> htmlize('Heimlich & Co.\n- a game') # <2>
'<p>Heimlich &amp; 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>
# END HTMLIZE_DEMO
"""
# BEGIN HTMLIZE
from functools import singledispatch
import html
@singledispatch # <1>
def htmlize(obj):
content = html.escape(repr(obj))
return '<pre>{}</pre>'.format(content)
@htmlize.register(str) # <2>
def _(text): # <3>
content = html.escape(text).replace('\n', '<br>\n')
return '<p>{0}</p>'.format(content)
@htmlize.register(int) # <4>
def _(n):
return '<pre>{0} (0x{0:x})</pre>'.format(n)
@htmlize.register(list)
def _(a_list):
inner = '</li>\n<li>'.join(htmlize(item) for item in a_list)
return '<ul>\n<li>' + inner + '</li>\n</ul>'
# END HTMLIZE

63
decorators/local_demo.py Normal file
View File

@@ -0,0 +1,63 @@
"""
>>> f1(3)
>>> b = 8
>>> f1(3)
a = 3
b = 8
>>> f2(3)
Traceback (most recent call last):
...
UnboundLocalError: local variable 'b' referenced before assignment
>>> f3(3)
a = 3
b = 7
b = 6
>>> b = -5
>>> ff = f4()
>>> ff(3)
a = 3
b = 11
b = 6
>>> print('b =', b)
b = -5
"""
def f1(a):
print('a =', a)
print('b =', b)
def f2(a):
print('a =', a)
print('b =', b)
b = a * 10
print('b =', b)
def f3(a):
global b
print('a =', a)
print('b =', b)
b = a * 10
print('b =', b)
def f3b(a):
nonlocal b
print('a =', a)
print('b =', b)
b = a * 10
print('b =', b)
def f4():
b = 11
def f5(a):
nonlocal b
print('a =', a)
print('b =', b)
b = a * 2
print('b =', b)
return f5
import doctest
doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)

View File

@@ -0,0 +1,31 @@
# BEGIN REGISTRATION
registry = [] # <1>
def register(func): # <2>
print('running register(%s)' % 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

View File

@@ -0,0 +1,16 @@
# BEGIN REGISTRATION_ABRIDGED
registry = []
def register(func):
print('running register(%s)' % func)
registry.append(func)
return func
@register
def f1():
print('running f1()')
print('running main()')
print('registry ->', registry)
f1()
# END REGISTRATION_ABRIDGED

View File

@@ -0,0 +1,29 @@
# BEGIN REGISTRATION_PARAM
registry = set() # <1>
def register(active=True): # <2>
def decorate(func): # <3>
print('running register(active=%s)->decorate(%s)'
% (active, func))
if active: # <4>
registry.add(func)
else: # <5>
if func in registry:
registry.remove(func)
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

View File

@@ -0,0 +1,27 @@
def d1(f):
def wrapped():
print('d1/wrapped')
return f()
return wrapped
def d2(f):
def wrapped():
print('d2/wrapped')
return f()
return wrapped
@d1
@d2
def f():
print('f')
f()
def g():
print('g')
g = d1(d2(g))
g()

View File

@@ -0,0 +1,113 @@
# strategy_best4.py
# Strategy pattern -- function-based implementation
# selecting best promotion from list of functions
# registered by a decorator
"""
>>> joe = Customer('John Doe', 0)
>>> ann = Customer('Ann Smith', 1100)
>>> cart = [LineItem('banana', 4, .5),
... LineItem('apple', 10, 1.5),
... LineItem('watermellon', 5, 5.0)]
>>> Order(joe, cart, fidelity)
<Order total: 42.00 due: 42.00>
>>> Order(ann, cart, fidelity)
<Order total: 42.00 due: 39.90>
>>> banana_cart = [LineItem('banana', 30, .5),
... LineItem('apple', 10, 1.5)]
>>> Order(joe, banana_cart, bulk_item)
<Order total: 30.00 due: 28.50>
>>> long_order = [LineItem(str(item_code), 1, 1.0)
... for item_code in range(10)]
>>> Order(joe, long_order, large_order)
<Order total: 10.00 due: 9.30>
>>> Order(joe, cart, large_order)
<Order total: 42.00 due: 42.00>
# BEGIN STRATEGY_BEST_TESTS
>>> Order(joe, long_order, best_promo)
<Order total: 10.00 due: 9.30>
>>> Order(joe, banana_cart, best_promo)
<Order total: 30.00 due: 28.50>
>>> Order(ann, cart, best_promo)
<Order total: 42.00 due: 39.90>
# END STRATEGY_BEST_TESTS
"""
from collections import namedtuple
Customer = namedtuple('Customer', 'name fidelity')
class LineItem:
def __init__(self, product, quantity, price):
self.product = product
self.quantity = quantity
self.price = price
def total(self):
return self.price * self.quantity
class Order: # the Context
def __init__(self, customer, cart, promotion=None):
self.customer = customer
self.cart = list(cart)
self.promotion = promotion
def total(self):
if not hasattr(self, '__total'):
self.__total = sum(item.total() for item in self.cart)
return self.__total
def due(self):
if self.promotion is None:
discount = 0
else:
discount = self.promotion(self)
return self.total() - discount
def __repr__(self):
fmt = '<Order total: {:.2f} due: {:.2f}>'
return fmt.format(self.total(), self.due())
# BEGIN STRATEGY_BEST4
promos = [] # <1>
def promotion(promo_func): # <2>
promos.append(promo_func)
return promo_func
@promotion # <3>
def fidelity(order):
"""5% discount for customers with 1000 or more fidelity points"""
return order.total() * .05 if order.customer.fidelity >= 1000 else 0
@promotion
def bulk_item(order):
"""10% discount for each LineItem with 20 or more units"""
discount = 0
for item in order.cart:
if item.quantity >= 20:
discount += item.total() * .1
return discount
@promotion
def large_order(order):
"""7% discount for orders with 10 or more distinct items"""
distinct_items = {item.product for item in order.cart}
if len(distinct_items) >= 10:
return order.total() * .07
return 0
def best_promo(order): # <4>
"""Select best discount available
"""
return max(promo(order) for promo in promos)
# END STRATEGY_BEST4

View File

@@ -0,0 +1,55 @@
"""
Container ``in`` operator performance test
"""
import sys
import timeit
SETUP = '''
import array
selected = array.array('d')
with open('selected.arr', 'rb') as fp:
selected.fromfile(fp, {size})
if {container_type} is dict:
haystack = dict.fromkeys(selected, 1)
else:
haystack = {container_type}(selected)
if {verbose}:
print(type(haystack), end=' ')
print('haystack: %10d' % len(haystack), end=' ')
needles = array.array('d')
with open('not_selected.arr', 'rb') as fp:
needles.fromfile(fp, 500)
needles.extend(selected[::{size}//500])
if {verbose}:
print(' needles: %10d' % len(needles), end=' ')
'''
TEST = '''
found = 0
for n in needles:
if n in haystack:
found += 1
if {verbose}:
print(' found: %10d' % found)
'''
def test(container_type, verbose):
MAX_EXPONENT = 7
for n in range(3, MAX_EXPONENT + 1):
size = 10**n
setup = SETUP.format(container_type=container_type,
size=size, verbose=verbose)
test = TEST.format(verbose=verbose)
tt = timeit.repeat(stmt=test, setup=setup, repeat=5, number=1)
print('|{:{}d}|{:f}'.format(size, MAX_EXPONENT + 1, min(tt)))
if __name__=='__main__':
if '-v' in sys.argv:
sys.argv.remove('-v')
verbose = True
else:
verbose = False
if len(sys.argv) != 2:
print('Usage: %s <container_type>' % sys.argv[0])
else:
test(sys.argv[1], verbose)

View File

@@ -0,0 +1,37 @@
"""
Generate data for container performance test
"""
import random
import array
MAX_EXPONENT = 7
HAYSTACK_LEN = 10 ** MAX_EXPONENT
NEEDLES_LEN = 10 ** (MAX_EXPONENT - 1)
SAMPLE_LEN = HAYSTACK_LEN + NEEDLES_LEN // 2
needles = array.array('d')
sample = {1/random.random() for i in range(SAMPLE_LEN)}
print('initial sample: %d elements' % len(sample))
# complete sample, in case duplicate random numbers were discarded
while len(sample) < SAMPLE_LEN:
sample.add(1/random.random())
print('complete sample: %d elements' % len(sample))
sample = array.array('d', sample)
random.shuffle(sample)
not_selected = sample[:NEEDLES_LEN // 2]
print('not selected: %d samples' % len(not_selected))
print(' writing not_selected.arr')
with open('not_selected.arr', 'wb') as fp:
not_selected.tofile(fp)
selected = sample[NEEDLES_LEN // 2:]
print('selected: %d samples' % len(selected))
print(' writing selected.arr')
with open('selected.arr', 'wb') as fp:
selected.tofile(fp)

30
dicts/dialcodes.py Normal file
View File

@@ -0,0 +1,30 @@
# BEGIN DIALCODES
# dial codes of the top 10 most populous countries
DIAL_CODES = [
(86, 'China'),
(91, 'India'),
(1, 'United States'),
(62, 'Indonesia'),
(55, 'Brazil'),
(92, 'Pakistan'),
(880, 'Bangladesh'),
(234, 'Nigeria'),
(7, 'Russia'),
(81, 'Japan'),
]
d1 = dict(DIAL_CODES) # <1>
print('d1:', d1.keys())
d2 = dict(sorted(DIAL_CODES)) # <2>
print('d2:', d2.keys())
d3 = dict(sorted(DIAL_CODES, key=lambda x:x[1])) # <3>
print('d3:', d3.keys())
assert d1 == d2 and d2 == d3 # <4>
# END DIALCODES
"""
# BEGIN DIALCODES_OUTPUT
d1: dict_keys([880, 1, 86, 55, 7, 234, 91, 92, 62, 81])
d2: dict_keys([880, 1, 91, 86, 81, 55, 234, 7, 92, 62])
d3: dict_keys([880, 81, 1, 86, 55, 7, 234, 91, 92, 62])
# END DIALCODES_OUTPUT
"""

34
dicts/dict_perftest.py Normal file
View File

@@ -0,0 +1,34 @@
"""
Dict performance test
"""
import timeit
SETUP = '''
import array
selected = array.array('d')
with open('selected.arr', 'rb') as fp:
selected.fromfile(fp, {size})
haystack = dict((n, n.as_integer_ratio()) for n in selected)
print('haystack: %10d' % len(haystack), end=' ')
needles = array.array('d')
with open('not_selected.arr', 'rb') as fp:
needles.fromfile(fp, 500)
needles.extend(selected[:500])
# print(' needles: %10d' % len(needles), end=' ')
'''
TEST = '''
found = 0
for n in needles:
if n in haystack:
found += 1
# print(' found: %10d' % found)
'''
MAX_EXPONENT = 7
for n in range(3, MAX_EXPONENT + 1):
size = 10**n
setup = SETUP.format(size=size)
tt = timeit.repeat(stmt=TEST, setup=setup, repeat=5, number=1)
print('|{:{}d}|{:f}'.format(size, MAX_EXPONENT + 1, min(tt)))

20
dicts/hashdiff.py Normal file
View File

@@ -0,0 +1,20 @@
import sys
MAX_BITS = len(format(sys.maxsize, 'b'))
print('%s-bit Python build' % (MAX_BITS + 1))
def hash_diff(o1, o2):
h1 = '{:>0{}b}'.format(hash(o1), MAX_BITS)
h2 = '{:>0{}b}'.format(hash(o2), MAX_BITS)
diff = ''.join('!' if b1 != b2 else ' ' for b1, b2 in zip(h1, h2))
count = '!= {}'.format(diff.count('!'))
width = max(len(repr(o1)), len(repr(o2)), 8)
sep = '-' * (width * 2 + MAX_BITS)
return '{!r:{width}} {}\n{:{width}} {} {}\n{!r:{width}} {}\n{}'.format(
o1, h1, ' ' * width, diff, count, o2, h2, sep, width=width)
if __name__ == '__main__':
print(hash_diff(1, 1.0))
print(hash_diff(1.0, 1.0001))
print(hash_diff(1.0001, 1.0002))
print(hash_diff(1.0002, 1.0003))

25
dicts/index.py Normal file
View File

@@ -0,0 +1,25 @@
# adapted from Alex Martelli's example in "Re-learning Python"
# http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
# (slide 41) Ex: lines-by-word file index
# BEGIN INDEX
"""Build an index mapping word -> list of occurrences"""
import sys
import re
WORD_RE = re.compile('\w+')
index = {}
with open(sys.argv[1], encoding='utf-8') as fp:
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start()+1
location = (line_no, column_no)
index.setdefault(word, []).append(location) # <1>
# print in alphabetical order
for word in sorted(index, key=str.upper):
print(word, index[word])
# END INDEX

28
dicts/index0.py Normal file
View File

@@ -0,0 +1,28 @@
# adapted from Alex Martelli's example in "Re-learning Python"
# http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
# (slide 41) Ex: lines-by-word file index
# BEGIN INDEX0
"""Build an index mapping word -> list of occurrences"""
import sys
import re
WORD_RE = re.compile('\w+')
index = {}
with open(sys.argv[1], encoding='utf-8') as fp:
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start()+1
location = (line_no, column_no)
# this is ugly; coded like this to make a point
occurrences = index.get(word, []) # <1>
occurrences.append(location) # <2>
index[word] = occurrences # <3>
# print in alphabetical order
for word in sorted(index, key=str.upper):
print(word, index[word])
# END INDEX0

22
dicts/index_alex.py Normal file
View File

@@ -0,0 +1,22 @@
# adapted from Alex Martelli's example in "Re-learning Python"
# http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
# (slide 41) Ex: lines-by-word file index
"""Build a map word -> list-of-line-numbers"""
import sys
import re
NONWORD_RE = re.compile('\W+')
idx = {}
with open(sys.argv[1], encoding='utf-8') as fp:
for n, line in enumerate(fp, 1):
for word in NONWORD_RE.split(line):
if word.strip():
idx.setdefault(word, []).append(n)
# print in alphabetical order
for word in sorted(idx, key=str.upper):
print(word, idx[word])

26
dicts/index_default.py Normal file
View File

@@ -0,0 +1,26 @@
# adapted from Alex Martelli's example in "Re-learning Python"
# http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
# (slide 41) Ex: lines-by-word file index
# BEGIN INDEX_DEFAULT
"""Build an index mapping word -> list of occurrences"""
import sys
import re
import collections
WORD_RE = re.compile('\w+')
index = collections.defaultdict(list) # <1>
with open(sys.argv[1], encoding='utf-8') as fp:
for line_no, line in enumerate(fp, 1):
for match in WORD_RE.finditer(line):
word = match.group()
column_no = match.start()+1
location = (line_no, column_no)
index[word].append(location) # <2>
# print in alphabetical order
for word in sorted(index, key=str.upper):
print(word, index[word])
# END INDEX_DEFAULT

48
dicts/set_perftest.py Normal file
View File

@@ -0,0 +1,48 @@
"""
Set performance test
"""
import timeit
SETUP = '''
import array
selected = array.array('d')
with open('selected.arr', 'rb') as fp:
selected.fromfile(fp, {size})
haystack = {type}(selected)
# print('haystack: %10d' % len(haystack), end=' ')
needles = array.array('d')
with open('not_selected.arr', 'rb') as fp:
needles.fromfile(fp, 500)
needles.extend(selected[:500])
needles = set(needles)
# print(' needles: %10d' % len(needles), end=' ')
'''
tests = [
('FOR_LOOP_TEST', '''
found = 0
for n in needles:
if n in haystack:
found += 1
assert found == 500
'''),
('SET_&_TEST', '''
found = len(needles & haystack)
assert found == 500
'''
)]
MAX_EXPONENT = 7
for collection_type in 'dict.fromkeys set list'.split():
if collection_type == 'set':
available_tests = tests
else:
available_tests = tests[:1]
for test_name, test in available_tests:
print('*' * 25, collection_type, test_name)
for n in range(3, MAX_EXPONENT + 1):
size = 10**n
setup = SETUP.format(type=collection_type, size=size)
tt = timeit.repeat(stmt=test, setup=setup, repeat=5, number=1)
print('|{:{}d}|{:9.6f}'.format(size, MAX_EXPONENT + 1, min(tt)))

72
dicts/strkeydict.py Normal file
View File

@@ -0,0 +1,72 @@
"""StrKeyDict always converts non-string keys to `str`
Tests for item retrieval using `d[key]` notation::
>>> d = StrKeyDict([('2', 'two'), ('4', 'four')])
>>> d['2']
'two'
>>> d[4]
'four'
>>> d[1]
Traceback (most recent call last):
...
KeyError: '1'
Tests for the `in` operator::
>>> 2 in d
True
>>> 1 in d
False
Test for item assignment using non-string key::
>>> d[0] = 'zero'
>>> d['0']
'zero'
Tests for update using a `dict` or a sequence of pairs::
>>> d.update({6:'six', '8':'eight'})
>>> sorted(d.keys())
['0', '2', '4', '6', '8']
>>> d.update([(10, 'ten'), ('12', 'twelve')])
>>> sorted(d.keys())
['0', '10', '12', '2', '4', '6', '8']
>>> d.update([1, 3, 5])
Traceback (most recent call last):
...
TypeError: 'int' object is not iterable
"""
# BEGIN STRKEYDICT
import collections
import collections.abc
class StrKeyDict(collections.UserDict): # <1>
def __missing__(self, key): # <2>
if isinstance(key, str):
raise KeyError(key)
return self[str(key)]
def __contains__(self, key):
return str(key) in self.data # <3>
def __setitem__(self, key, item):
self.data[str(key)] = item # <4>
def update(self, iterable=None, **kwds):
if iterable is not None:
if isinstance(iterable, collections.abc.Mapping): # <5>
pairs = iterable.items()
else:
pairs = ((k, v) for k, v in iterable) # <6>
for key, value in pairs:
self[key] = value # <7>
if kwds:
self.update(kwds) # <8>
# END STRKEYDICT

39
dicts/strkeydict0.py Normal file
View File

@@ -0,0 +1,39 @@
"""StrKeyDict0 converts non-string keys to `str` on lookup
# BEGIN STRKEYDICT0_TESTS
Tests for item retrieval using `d[key]` notation::
>>> d = StrKeyDict0([('2', 'two'), ('4', 'four')])
>>> d['2']
'two'
>>> d[4]
'four'
>>> d[1]
Traceback (most recent call last):
...
KeyError: '1'
Tests for the `in` operator::
>>> 2 in d
True
>>> 1 in d
False
# END STRKEYDICT0_TESTS
"""
# BEGIN STRKEYDICT0
class StrKeyDict0(dict): # <1>
def __missing__(self, key):
if isinstance(key, str): # <2>
raise KeyError(key)
return self[str(key)] # <3>
def __contains__(self, key):
return key in self.keys() or str(key) in self.keys() # <4>
# END STRKEYDICT0

View File

@@ -0,0 +1,25 @@
metro_data = [
('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
]
from collections import namedtuple
LatLong = namedtuple('LatLong', 'lat long')
Metropolis = namedtuple('Metropolis', 'name cc pop coord')
metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long_))
for name, cc, pop, (lat, long_) in metro_data]
metro_areas[0]
metro_areas[0].coord.lat
from operator import attrgetter
name_lat = attrgetter('name', 'coord.lat')
for city in sorted(metro_areas, key=attrgetter('coord.lat')):
print(name_lat(city))

View File

@@ -0,0 +1,31 @@
>>> metro_data = [
... ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
... ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
... ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
... ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
... ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
... ]
# BEGIN ATTRGETTER_DEMO
>>> from collections import namedtuple
>>> LatLong = namedtuple('LatLong', 'lat long') # <1>
>>> Metropolis = namedtuple('Metropolis', 'name cc pop coord') # <2>
>>> metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long_)) # <3>
... for name, cc, pop, (lat, long_) in metro_data]
>>> metro_areas[0]
Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=35.689722, long=139.691667))
>>> metro_areas[0].coord.lat # <4>
35.689722
>>> from operator import attrgetter
>>> name_lat = attrgetter('name', 'coord.lat') # <5>
>>>
>>> for city in sorted(metro_areas, key=attrgetter('coord.lat')): # <6>
... print(name_lat(city)) # <7>
...
('Sao Paulo', -23.547778)
('Mexico City', 19.433333)
('Delhi NCR', 28.613889)
('Tokyo', 35.689722)
('New York-Newark', 40.808611)
# END ATTRGETTER_DEMO

30
functions/bingo.py Normal file
View File

@@ -0,0 +1,30 @@
"""
# BEGIN BINGO_DEMO
>>> bingo = BingoCage(range(3))
>>> bingo()
2
>>> bingo()
0
>>> callable(bingo)
True
# END BINGO_DEMO
"""
# BEGIN BINGO
import random
class BingoCage:
def __init__(self, items):
self._items = list(items) # <1>
random.shuffle(self._items) # <2>
def __call__(self):
if not self._items: # <3>
raise IndexError('pop from empty BingoCage')
return self._items.pop()
# END BINGO

38
functions/clip.py Normal file
View File

@@ -0,0 +1,38 @@
"""
>>> clip('banana ', 6)
'banana'
>>> clip('banana ', 7)
'banana'
>>> clip('banana ', 5)
'banana'
>>> clip('banana split', 6)
'banana'
>>> clip('banana split', 7)
'banana'
>>> clip('banana split', 10)
'banana'
>>> clip('banana split', 11)
'banana'
>>> clip('banana split', 12)
'banana split'
"""
# BEGIN CLIP
def clip(text, max_len=80):
"""Return text clipped at the last space before or after max_len
"""
end = None
if len(text) > max_len:
space_before = text.rfind(' ', 0, max_len)
if space_before >= 0:
end = space_before
else:
space_after = text.rfind(' ', max_len)
if space_after >= 0:
end = space_after
if end is None: # no spaces were found
end = len(text)
return text[:end].rstrip()
# END CLIP

38
functions/clip_annot.py Normal file
View File

@@ -0,0 +1,38 @@
"""
>>> clip('banana ', 6)
'banana'
>>> clip('banana ', 7)
'banana'
>>> clip('banana ', 5)
'banana'
>>> clip('banana split', 6)
'banana'
>>> clip('banana split', 7)
'banana'
>>> clip('banana split', 10)
'banana'
>>> clip('banana split', 11)
'banana'
>>> clip('banana split', 12)
'banana split'
"""
# BEGIN CLIP_ANNOT
def clip(text:str, max_len:'int > 0'=80) -> str:
"""Return text clipped at the last space before or after max_len
"""
end = None
if len(text) > max_len:
space_before = text.rfind(' ', 0, max_len)
if space_before >= 0:
end = space_before
else:
space_after = text.rfind(' ', max_len)
if space_after >= 0:
end = space_after
if end is None: # no spaces were found
end = len(text)
return text[:end].rstrip()
# END CLIP_ANNOT

View File

@@ -0,0 +1,10 @@
>>> from clip_annot import clip
>>> from inspect import signature
>>> sig = signature(clip)
>>> sig.return_annotation
<class 'str'>
>>> for param in sig.parameters.values():
... note = repr(param.annotation).ljust(13)
... print(note, ':', param.name, '=', param.default)
<class 'str'> : text = <class 'inspect._empty'>
'int > 0' : max_len = 80

View File

@@ -0,0 +1,9 @@
>>> from clip import clip
>>> clip.__defaults__
(80,)
>>> clip.__code__ # doctest: +ELLIPSIS
<code object clip at 0x...>
>>> clip.__code__.co_varnames
('text', 'max_len', 'end', 'space_before', 'space_after')
>>> clip.__code__.co_argcount
2

View File

@@ -0,0 +1,12 @@
>>> from clip import clip
>>> from inspect import signature
>>> sig = signature(clip)
>>> sig # doctest: +ELLIPSIS
<inspect.Signature object at 0x...>
>>> str(sig)
'(text, max_len=80)'
>>> for name, param in sig.parameters.items():
... print(param.kind, ':', name, '=', param.default)
...
POSITIONAL_OR_KEYWORD : text = <class 'inspect._empty'>
POSITIONAL_OR_KEYWORD : max_len = 80

5
functions/hello.py Normal file
View File

@@ -0,0 +1,5 @@
import bobo
@bobo.query('/')
def hello(person):
return 'Hello %s!' % person

76
functions/strkeydict2.py Normal file
View File

@@ -0,0 +1,76 @@
"""StrKeyDict always converts non-string keys to `str`
Tests for item retrieval using `d[key]` notation::
>>> d = StrKeyDict([('2', 'two'), ('4', 'four')])
>>> d['2']
'two'
>>> d[4]
'four'
>>> d[1]
Traceback (most recent call last):
...
KeyError: '1'
Tests for the `in` operator::
>>> 2 in d
True
>>> 1 in d
False
Test for item assignment using non-string key::
>>> d[0] = 'zero'
>>> d['0']
'zero'
Tests for update using a `dict` or a sequence of pairs::
>>> d.update({6:'six', '8':'eight'})
>>> sorted(d.keys())
['0', '2', '4', '6', '8']
>>> d.update([(10, 'ten'), ('12', 'twelve')])
>>> sorted(d.keys())
['0', '10', '12', '2', '4', '6', '8']
>>> d.update([1, 3, 5])
Traceback (most recent call last):
...
TypeError: 'int' object is not iterable
"""
# BEGIN STRKEYDICT
import collections
import collections.abc
class StrKeyDict(collections.UserDict): # <1>
def __init__(self, args, normalize=str, **kwargs):
super().__init__(self, *args, **kwargs)
self.normalize = normalize
def __missing__(self, key): # <2>
if self.normalize(key) == key:
raise KeyError(key)
return self[self.normalize(key)]
def __contains__(self, key):
return self.normalize(key) in self.data # <3>
def __setitem__(self, key, item):
self.data[self.normalize(key)] = item # <4>
def update(self, iterable=None, **kwds):
if iterable is not None:
if isinstance(iterable, collections.abc.Mapping): # <5>
pairs = iterable.items()
else:
pairs = ((k, v) for k, v in iterable) # <6>
for key, value in pairs:
self[key] = value # <7>
if kwds:
self.update(kwds) # <8>
# END STRKEYDICT

44
functions/tagger.py Normal file
View File

@@ -0,0 +1,44 @@
"""
# BEGIN TAG_DEMO
>>> tag('br') # <1>
'<br />'
>>> tag('p', 'hello') # <2>
'<p>hello</p>'
>>> print(tag('p', 'hello', 'world'))
<p>hello</p>
<p>world</p>
>>> tag('p', 'hello', id=33) # <3>
'<p id="33">hello</p>'
>>> print(tag('p', 'hello', 'world', cls='sidebar')) # <4>
<p class="sidebar">hello</p>
<p class="sidebar">world</p>
>>> tag(content='testing', name="img") # <5>
'<img content="testing" />'
>>> my_tag = {'name': 'img', 'title': 'Sunset Boulevard',
... 'src': 'sunset.jpg', 'cls': 'framed'}
>>> tag(**my_tag) # <6>
'<img class="framed" src="sunset.jpg" title="Sunset Boulevard" />'
# END TAG_DEMO
"""
# BEGIN TAG_FUNC
def tag(name, *content, cls=None, **attrs):
"""Generate one or more HTML tags"""
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ''.join(' %s="%s"' % (attr, value)
for attr, value
in sorted(attrs.items()))
else:
attr_str = ''
if content:
return '\n'.join('<%s%s>%s</%s>' %
(name, attr_str, c, name) for c in content)
else:
return '<%s%s />' % (name, attr_str)
# END TAG_FUNC

19
interfaces/bingo.py Normal file
View File

@@ -0,0 +1,19 @@
import random
from tombola import Tombola
class BingoCage(Tombola): # <1>
def __init__(self, items):
self._balls = list(items) # <2>
def load(self, items):
self._balls.extend(items)
def pop(self):
try:
position = random.randrange(len(self._balls)) # <4>
except ValueError:
raise LookupError('pop from empty BingoCage')
return self._balls.pop(position)

17
interfaces/drum.py Normal file
View File

@@ -0,0 +1,17 @@
from random import shuffle
from tombola import Tombola
class TumblingDrum(Tombola):
def __init__(self, iterable):
self._balls = []
self.load(iterable)
def load(self, iterable):
self._balls.extend(iterable)
shuffle(self._balls)
def pop(self):
return self._balls.pop()

View File

@@ -0,0 +1,61 @@
BaseException
├── SystemExit
├── KeyboardInterrupt
├── GeneratorExit
└── Exception
├── StopIteration
├── ArithmeticError
│ ├── FloatingPointError
│ ├── OverflowError
│ └── ZeroDivisionError
├── AssertionError
├── AttributeError
├── BufferError
├── EOFError
├── ImportError
├── LookupError
│ ├── IndexError
│ └── KeyError
├── MemoryError
├── NameError
│ └── UnboundLocalError
├── OSError
│ ├── BlockingIOError
│ ├── ChildProcessError
│ ├── ConnectionError
│ │ ├── BrokenPipeError
│ │ ├── ConnectionAbortedError
│ │ ├── ConnectionRefusedError
│ │ └── ConnectionResetError
│ ├── FileExistsError
│ ├── FileNotFoundError
│ ├── InterruptedError
│ ├── IsADirectoryError
│ ├── NotADirectoryError
│ ├── PermissionError
│ ├── ProcessLookupError
│ └── TimeoutError
├── ReferenceError
├── RuntimeError
│ └── NotImplementedError
├── SyntaxError
│ └── IndentationError
│ └── TabError
├── SystemError
├── TypeError
├── ValueError
│ └── UnicodeError
│ ├── UnicodeDecodeError
│ ├── UnicodeEncodeError
│ └── UnicodeTranslateError
└── Warning
├── DeprecationWarning
├── PendingDeprecationWarning
├── RuntimeWarning
├── SyntaxWarning
├── UserWarning
├── FutureWarning
├── ImportWarning
├── UnicodeWarning
├── BytesWarning
└── ResourceWarning

24
interfaces/lotto.py Normal file
View File

@@ -0,0 +1,24 @@
import random
from tombola import Tombola
class LotteryBlower(Tombola):
def __init__(self, iterable):
self.randomizer = random.SystemRandom() # <1>
self.clear()
self.load(iterable)
def clear(self):
self._balls = []
def load(self, iterable):
self._balls.extend(iterable)
self.randomizer.shuffle(self._balls) # <2>
def pop(self):
return self._balls.pop() # <3>
def loaded(self): # <4>
return len(self._balls) > 0

25
interfaces/tombola.py Normal file
View File

@@ -0,0 +1,25 @@
from abc import ABCMeta, abstractmethod
class Tombola(metaclass=ABCMeta): # <1>
@abstractmethod
def __init__(self, iterable): # <2>
raise NotImplementedError
@abstractmethod
def load(self):
raise NotImplementedError
@abstractmethod
def pop(self):
raise NotImplementedError
def loaded(self): # <3>
try:
item = self.pop()
except LookupError:
return False
else:
self.load([item]) # put it back
return True

View File

@@ -0,0 +1,52 @@
import sys
import importlib
import doctest
from tombola import Tombola
TESTS = 'tombola_tests.rst'
MODULES = 'bingo lotto tombolist drum'.split()
def test_module(module_name, verbose=False):
module = importlib.import_module(module_name)
tombola_class = None
for name in dir(module):
obj = getattr(module, name)
if (isinstance(obj, type) and
issubclass(obj, Tombola) and
obj.__module__ == module_name):
tombola_class = obj
break # stop at first matching class
if tombola_class is None:
print('ERROR: no Tombola subclass found in', module_name)
sys.exit(1)
res = doctest.testfile(TESTS,
globs={'TombolaUnderTest': tombola_class},
verbose=verbose,
optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
print('{:10} {}'.format(module_name, res))
if __name__ == '__main__':
args = sys.argv[:]
if '-v' in args:
args.remove('-v')
verbose = True
else:
verbose = False
if len(args) == 2:
module_names = [args[1]]
else:
module_names = MODULES
for name in module_names:
print('*' * 60, name)
test_module(name, verbose)

View File

@@ -0,0 +1,70 @@
==============
Tombola tests
==============
Every concrete subclass of Tombola should pass these tests.
Create and load instance from iterable::
>>> balls = list(range(3))
>>> globe = TombolaUnderTest(balls)
>>> globe.loaded()
True
Pop and collect balls::
>>> picks = []
>>> picks.append(globe.pop())
>>> picks.append(globe.pop())
>>> picks.append(globe.pop())
Check state and results::
>>> globe.loaded()
False
>>> sorted(picks) == balls
True
Reload::
>>> globe.load(balls)
>>> globe.loaded()
True
>>> picks = [globe.pop() for i in balls]
>>> globe.loaded()
False
Load and pop 20 balls to verify that the order has changed::
>>> balls = list(range(20))
>>> globe = TombolaUnderTest(balls)
>>> picks = []
>>> while globe.loaded():
... picks.append(globe.pop())
>>> len(picks) == len(balls)
True
>>> picks != balls
True
Also check that the order is not simply reversed either::
>>> picks[::-1] != balls
True
Note: last 2 tests each have 1 chance in 20! (factorial) of failing even if the implementation is OK. 1/20!, or approximately 4.11e-19, is the probability of the 20 balls coming out, by chance, in the exact order the were loaded.
Check that `LookupError` (or a subclass) is the exception thrown when the device is empty::
>>> globe = TombolaUnderTest([])
>>> try:
... globe.pop()
... except LookupError as exc:
... print('OK')
OK

19
interfaces/tombolist.py Normal file
View File

@@ -0,0 +1,19 @@
from random import randrange
from tombola import Tombola
class TomboList(list): # <1>
def pop(self):
if self: # <2>
return super().pop(randrange(len(self))) # <3>
else:
raise LookupError('pop from empty TomboList')
def load(self, iterable): self.extend(iterable) # <4>
def loaded(self): return bool(self) # <5>
Tombola.register(TomboList) # <6>

View File

@@ -0,0 +1,5 @@
Examples in this directory were adapted from:
Erik Meijer. 2014. The curse of the excluded middle.
Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
http://doi.acm.org/10.1145/2605176

View File

@@ -0,0 +1,10 @@
"""
Erik Meijer. 2014. The curse of the excluded middle.
Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
http://doi.acm.org/10.1145/2605176
"""
with open('citation.txt', encoding='ascii') as fp:
get_contents = lambda: fp.read()
print(get_contents())

17
iterables/CACM/haha.py Normal file
View File

@@ -0,0 +1,17 @@
"""
Erik Meijer. 2014. The curse of the excluded middle.
Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
http://doi.acm.org/10.1145/2605176
"""
def ha():
ha = 'Ha'
print(ha)
return ha
# prints 'Ha' twice before showing result
print('result ->', ha() + ha())
# prints 'Ha' only once before showing reult
ha_res = ha() # common subexpression elimination
print('result ->', ha_res + ha_res)

View File

@@ -0,0 +1,22 @@
"""
Meijer, Erik - The Curse of the Excluded Middle
DOI:10.1145/2605176
CACM vol.57 no.06
"""
def less_than_30(n):
check = n < 30
print('%d < 30 : %s' % (n, check))
return check
def more_than_20(n):
check = n > 20
print('%d > 20 : %s' % (n, check))
return check
l = [1, 25, 40, 5, 23]
q0 = (n for n in l if less_than_30(n))
q1 = (n for n in q0 if more_than_20(n))
for n in q1:
print('-> %d' % n)

View File

@@ -0,0 +1,14 @@
"""
Erik Meijer. 2014. The curse of the excluded middle.
Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
http://doi.acm.org/10.1145/2605176
"""
l = range(10, -1, -1)
try:
res = (1/x for x in l)
except ZeroDivisionError:
res = []
for z in res:
print(z)

View File

@@ -0,0 +1,40 @@
>>> from sentence_gen import Sentence
>>> s = Sentence('The time has come')
>>> s
Sentence('The time has come')
>>> list(s)
['The', 'time', 'has', 'come']
>>> it = iter(s)
>>> next(it)
'The'
>>> next(it)
'time'
>>> next(it)
'has'
>>> next(it)
'come'
>>> next(it)
Traceback (most recent call last):
...
StopIteration
>>> s = Sentence('"The time has come," the Walrus said,')
>>> s
Sentence('"The time ha... Walrus said,')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']
>>> s = Sentence('''"The time has come," the Walrus said,
... "To talk of many things:"''')
>>> s
Sentence('"The time ha...many things:"')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said', 'To', 'talk', 'of', 'many', 'things']
>>> s = Sentence('Agora vou-me. Ou me vão?')
>>> s
Sentence('Agora vou-me. Ou me vão?')
>>> list(s)
['Agora', 'vou', 'me', 'Ou', 'me', 'vão']

23
iterables/sentence_gen.py Normal file
View File

@@ -0,0 +1,23 @@
"""
Sentence: iterate over words using a generator function
"""
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
for match in RE_WORD.finditer(self.text): # <1>
yield match.group() # <2>
# done! <3>

View File

@@ -0,0 +1,40 @@
>>> from sentence_genexp import Sentence
>>> s = Sentence('The time has come')
>>> s
Sentence('The time has come')
>>> list(s)
['The', 'time', 'has', 'come']
>>> it = iter(s)
>>> next(it)
'The'
>>> next(it)
'time'
>>> next(it)
'has'
>>> next(it)
'come'
>>> next(it)
Traceback (most recent call last):
...
StopIteration
>>> s = Sentence('"The time has come," the Walrus said,')
>>> s
Sentence('"The time ha... Walrus said,')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']
>>> s = Sentence('''"The time has come," the Walrus said,
... "To talk of many things:"''')
>>> s
Sentence('"The time ha...many things:"')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said', 'To', 'talk', 'of', 'many', 'things']
>>> s = Sentence('Agora vou-me. Ou me vão?')
>>> s
Sentence('Agora vou-me. Ou me vão?')
>>> list(s)
['Agora', 'vou', 'me', 'Ou', 'me', 'vão']

View File

@@ -0,0 +1,44 @@
"""
Sentence: iterate over words using a generator expression
"""
# BEGIN SENTENCE_GENEXP
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
return (match.group() for match in RE_WORD.finditer(self.text))
# END SENTENCE_GENEXP
def main():
import sys
import warnings
try:
filename = sys.argv[1]
word_number = int(sys.argv[2])
except (IndexError, ValueError):
print('Usage: %s <file-name> <word-number>' % sys.argv[0])
sys.exit(1)
with open(filename, 'rt', encoding='utf-8') as text_file:
s = Sentence(text_file.read())
for n, word in enumerate(s, 1):
if n == word_number:
print(word)
break
else:
warnings.warn('last word is #%d, "%s"' % (n, word))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,40 @@
>>> from sentence_iter import Sentence
>>> s = Sentence('The time has come')
>>> s
Sentence('The time has come')
>>> list(s)
['The', 'time', 'has', 'come']
>>> it = iter(s)
>>> next(it)
'The'
>>> next(it)
'time'
>>> next(it)
'has'
>>> next(it)
'come'
>>> next(it)
Traceback (most recent call last):
...
StopIteration
>>> s = Sentence('"The time has come," the Walrus said,')
>>> s
Sentence('"The time ha... Walrus said,')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']
>>> s = Sentence('''"The time has come," the Walrus said,
... "To talk of many things:"''')
>>> s
Sentence('"The time ha...many things:"')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said', 'To', 'talk', 'of', 'many', 'things']
>>> s = Sentence('Agora vou-me. Ou me vão?')
>>> s
Sentence('Agora vou-me. Ou me vão?')
>>> list(s)
['Agora', 'vou', 'me', 'Ou', 'me', 'vão']

View File

@@ -0,0 +1,65 @@
"""
Sentence: iterate over words using the Iterator Pattern, take #1
WARNING: the Iterator Pattern is much simpler in idiomatic Python;
see: sentence_gen*.py.
"""
# BEGIN SENTENCE_ITER
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
self.words = RE_WORD.findall(text)
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self): # <1>
return SentenceIter(self.words) # <2>
class SentenceIter:
def __init__(self, words):
self.words = words # <3>
self.index = 0 # <4>
def __next__(self):
try:
word = self.words[self.index] # <5>
except IndexError:
raise StopIteration() # <6>
self.index += 1 # <7>
return word # <8>
def __iter__(self): # <9>
return self
# END SENTENCE_ITER
def main():
import sys
import warnings
try:
filename = sys.argv[1]
word_number = int(sys.argv[2])
except (IndexError, ValueError):
print('Usage: %s <file-name> <word-number>' % sys.argv[0])
sys.exit(1)
with open(filename, 'rt', encoding='utf-8') as text_file:
s = Sentence(text_file.read())
for n, word in enumerate(s, 1):
if n == word_number:
print(word)
break
else:
warnings.warn('last word is #%d, "%s"' % (n, word))
if __name__ == '__main__':
main()

View File

@@ -0,0 +1,40 @@
>>> from sentence_iter2 import Sentence
>>> s = Sentence('The time has come')
>>> s
Sentence('The time has come')
>>> list(s)
['The', 'time', 'has', 'come']
>>> it = iter(s)
>>> next(it)
'The'
>>> next(it)
'time'
>>> next(it)
'has'
>>> next(it)
'come'
>>> next(it)
Traceback (most recent call last):
...
StopIteration
>>> s = Sentence('"The time has come," the Walrus said,')
>>> s
Sentence('"The time ha... Walrus said,')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said']
>>> s = Sentence('''"The time has come," the Walrus said,
... "To talk of many things:"''')
>>> s
Sentence('"The time ha...many things:"')
>>> list(s)
['The', 'time', 'has', 'come', 'the', 'Walrus', 'said', 'To', 'talk', 'of', 'many', 'things']
>>> s = Sentence('Agora vou-me. Ou me vão?')
>>> s
Sentence('Agora vou-me. Ou me vão?')
>>> list(s)
['Agora', 'vou', 'me', 'Ou', 'me', 'vão']

View File

@@ -0,0 +1,37 @@
"""
Sentence: iterate over words using the Iterator Pattern, take #2
WARNING: the Iterator Pattern is much simpler in idiomatic Python;
see: sentence_gen*.py.
"""
import re
import reprlib
RE_WORD = re.compile('\w+')
class Sentence:
def __init__(self, text):
self.text = text
def __repr__(self):
return 'Sentence(%s)' % reprlib.repr(self.text)
def __iter__(self):
word_iter = RE_WORD.finditer(self.text) # <1>
return SentenceIter(word_iter) # <2>
class SentenceIter():
def __init__(self, word_iter):
self.word_iter = word_iter # <3>
def __next__(self):
match = next(self.word_iter) # <4>
return match.group() # <5>
def __iter__(self):
return self

View File

@@ -0,0 +1,42 @@
>>> def gen_123():
... yield 1
... yield 2
... yield 3
...
>>> gen_123 # doctest: +ELLIPSIS
<function gen_123 at 0x...>
>>> gen_123() # doctest: +ELLIPSIS
<generator object gen_123 at 0x...>
>>> for i in gen_123():
... print(i)
1
2
3
>>> g = gen_123()
>>> next(g)
1
>>> next(g)
2
>>> next(g)
3
>>> next(g)
Traceback (most recent call last):
...
StopIteration
>>> def gen_AB():
... print('start')
... yield 'A'
... print('continue')
... yield 'B'
... print('end.')
...
>>> for c in gen_AB():
... print('--->', c)
...
start
---> A
continue
---> B
end.

103
iterables/vector.py Normal file
View File

@@ -0,0 +1,103 @@
"""
The `+` operator produces a `Vector` result.
>>> v1 = Vector(2, 4)
>>> v2 = Vector(2, 1)
>>> v1 + v2
Vector(4, 5)
We can also implemement the `*` operator to perform scalar multiplication
or elementwise multiplication.
>>> v = Vector(3, 4)
>>> abs(v)
5.0
>>> v * 3
Vector(9, 12)
>>> abs(v * 3)
15.0
>>> v25 = Vector(2, 5)
>>> v71 = Vector(7, 1)
>>> v71 * v25
Vector(14, 5)
A vector can be used in a boolean context, where it will be considered
_falsy_ if it has magnitude zero, otherwise it is _truthy_::
>>> bool(v)
True
>>> bool(Vector(0, 0))
False
Vectors can have n-dimensions::
>>> v3 = Vector(1, 2, 3)
>>> len(v3)
3
>>> v3
Vector(1, 2, 3)
>>> abs(v3) # doctest:+ELLIPSIS
3.74165738...
>>> v3 + Vector(4, 5, 6)
Vector(5, 7, 9)
>>> v3 * 5
Vector(5, 10, 15)
>>> v2 + v3
Traceback (most recent call last):
...
ValueError: Addition applies only to vectors of equal dimensions.
The `repr` of a Vector is produced with the help of the `reprlib.repr`
function, limiting the size of the output string:
>>> Vector(*range(100))
Vector(0, 1, 2, 3, 4, 5, ...)
"""
# BEGIN VECTOR_ITER
import math
import numbers
import reprlib
EQ_DIMENSIONS_MSG = '%s applies only to vectors of equal dimensions.'
class Vector:
"""An n-dimensional vector"""
def __init__(self, *components): # <1>
self._components = tuple(components) # <2>
def __repr__(self):
return 'Vector' + (reprlib.repr(self._components)) # <3>
def __iter__(self):
return iter(self._components) # <4>
def __abs__(self):
return math.sqrt(sum(comp*comp for comp in self)) # <5>
def __len__(self):
return len(self._components) # <6>
def __add__(self, other):
if len(self) != len(other):
raise ValueError(EQ_DIMENSIONS_MSG % 'Addition')
return Vector(*(a+b for a, b in zip(self, other))) # <7>
def __mul__(self, other):
if isinstance(other, numbers.Number):
return Vector(*(comp*other for comp in self)) # <8>
else:
return self.elementwise_mul(other) # <9>
def elementwise_mul(self, other):
if len(self) != len(other):
raise ValueError(EQ_DIMENSIONS_MSG %
'Elementwise multiplication')
return Vector(*(a*b for a, b in zip(self, other))) # <10>
def __bool__(self):
return any(self) # <11>
# END VECTOR_ITER

View File

@@ -0,0 +1,43 @@
"""
A more flexible `__init__` for the Vector class
A Vector can be built from an iterable:
>>> Vector([10, 20, 30])
Vector(10, 20, 30)
>>> Vector(range(1, 5))
Vector(1, 2, 3, 4)
Or from two more arguments:
>>> Vector(100, 200)
Vector(100, 200)
>>> Vector(1, 2, 3, 4, 5)
Vector(1, 2, 3, 4, 5)
One-dimensional vectors are not supported:
>>> Vector(99)
Traceback (most recent call last):
...
TypeError: Vector() takes one iterable argument or at least 2 scalar arguments
"""
import reprlib
class Vector:
"""An n-dimensional vector"""
def __init__(self, first, *rest): # <1>
if rest:
self._components = (first,) + tuple(rest) # <3>
else:
try:
self._components = tuple(first) # <2>
except TypeError:
raise TypeError('Vector() takes one iterable argument or at least 2 scalar arguments')
def __repr__(self):
return 'Vector' + reprlib.repr(self._components)

View File

@@ -7,4 +7,4 @@ __pycache__
.DS_Store
*.arr
README.rst
LICENCE
LICENSE

61
objects/attr_list.py Normal file
View File

@@ -0,0 +1,61 @@
import sys
from collections import Counter
def fn():
pass
class Class():
pass
# int, str,
sample_types = [object, list, Class, type(Class), type(fn)]
if '-' in sys.argv:
del sample_types[0] # exlude `object`
sample_objs = [type_() for type_ in sample_types[:-2]] + [Class, fn]
sample_oids = [id(obj) for obj in sample_objs]
fmt = '{attr:17}' + '|{:8}' * len(sample_types)
headings = [t.__name__ for t in sample_types]
headings[headings.index('Class')] = 'instance'
headings[headings.index('type')] = 'class'
common_attrs = set()
for obj in sample_objs:
for attr_name in dir(obj):
common_attrs.add(attr_name)
print(fmt.format(*headings, attr=''))
counter = Counter()
for attr_name in sorted(common_attrs):
if not attr_name.startswith('__'):
continue
flags = []
found = 0
for obj in sample_objs:
try:
attr = getattr(obj, attr_name)
if type(attr) == type:
flag = 'type'
elif callable(attr):
flag = 'method'
else:
flag = 'data'
counter[id(obj)] += 1
found += 1
except AttributeError:
flag = ''
flags.append(flag)
if '-' in sys.argv:
include = found < len(sample_objs)
else:
include = found == len(sample_objs)
if include:
print(fmt.format(*flags, attr=attr_name))
counts = [counter[oid] for oid in sample_oids]
print(fmt.format(*counts, attr='TOTALS'))
print(sys.argv)

View File

@@ -0,0 +1,28 @@
|instance|list |function|class
__add__ | |method | |
__annotations__ | | |data |
__call__ | | |method |method
__closure__ | | |data |
__code__ | | |data |
__contains__ | |method | |
__defaults__ | | |data |
__delitem__ | |method | |
__dict__ |data | |data |data
__get__ | | |method |
__getitem__ | |method | |
__globals__ | | |data |
__iadd__ | |method | |
__imul__ | |method | |
__iter__ | |method | |
__kwdefaults__ | | |data |
__len__ | |method | |
__module__ |data | |data |data
__mul__ | |method | |
__name__ | | |data |data
__qualname__ | | |data |data
__reversed__ | |method | |
__rmul__ | |method | |
__setitem__ | |method | |
__weakref__ |data | | |data
TOTALS | 25| 34| 34| 28
['attr_list.py', '-']

29
objects/bus.py Normal file
View File

@@ -0,0 +1,29 @@
"""
>>> import copy
>>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
>>> bus2 = copy.copy(bus1)
>>> bus3 = copy.deepcopy(bus1)
>>> bus1.drop('Bill')
>>> bus2.passengers
['Alice', 'Claire', 'David']
>>> bus3.passengers
['Alice', 'Bill', 'Claire', 'David']
"""
# BEGIN BUS_CLASS
class Bus:
def __init__(self, passengers=None):
if passengers is None:
self.passengers = []
else:
self.passengers = list(passengers)
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name)
# END BUS_CLASS

54
objects/cards.py Normal file
View File

@@ -0,0 +1,54 @@
"""
Spadille is the nickname for the Ace of Spades in some games
(see `Webster 1913`_)
>>> beer_card = Card('7', Suite.diamonds)
>>> beer_card
Card('7', Suite.diamonds)
>>> spadille = Card('A', Suite.spades, long_rank='Ace')
>>> spadille
Card('A', Suite.spades)
>>> print(spadille)
Ace of spades
>>> bytes(spadille)
b'A\\x01'
>>> charles = Card('K', Suite.hearts)
>>> bytes(charles)
b'K\\x04'
>>> big_cassino = Card('10', Suite.diamonds)
>>> bytes(big_cassino)
b'T\\x02'
__ http://machaut.uchicago.edu/cgi-bin/WEBSTER.sh?WORD=spadille
"""
from enum import Enum
Suite = Enum('Suite', 'spades diamonds clubs hearts')
class Card:
def __init__(self, rank, suite, *, long_rank=None):
self.rank = rank
if long_rank is None:
self.long_rank = self.rank
else:
self.long_rank = long_rank
self.suite = suite
def __str__(self):
return '{long_rank} of {suite.name}'.format(**self.__dict__)
def __repr__(self):
constructor = '{cls.__name__}({args})'
args = '{0.rank!r}, Suite.{0.suite.name}'.format(self)
return constructor.format(cls=self.__class__, args=args)
def __bytes__(self):
if self.rank == '10':
rank_byte = b'T'
else:
rank_byte = bytes([ord(self.rank)])
return rank_byte + bytes([self.suite.value])

122
objects/cards_format.py Normal file
View File

@@ -0,0 +1,122 @@
"""
Test Suite formatting:
>>> Suite.spades
<Suite.spades: 0>
>>> print(Suite.spades)
Suite.spades
>>> format(Suite.spades)
'spades'
>>> format(Suite.spades, 's')
'spades'
>>> format(Suite.spades, 'S')
'Spades'
>>> format(Suite.spades, 'p')
''
>>> format(Suite.spades, 'z')
Traceback (most recent call last):
...
ValueError: Invalid format spec 'z' for object of type 'Suite'
>>> bytes(Suite.spades), bytes(Suite.clubs)
(b'\\x00', b'\\x03')
Spadille is the nickname for the Ace of Spades in some games
(see `Webster 1913`_)
>>> spadille = Card('A', Suite.spades, long_rank='Ace')
>>> spadille
Card('A', 'spades')
>>> print(spadille)
Ace of spades
>>> format(spadille)
'A-spades'
>>> format(spadille, 'r/p')
'A/♠'
>>> format(spadille, 'R of S')
'Ace of Spades'
>>> bytes(spadille)
b'A\\x00'
>>> beer_card = Card('7', Suite.diamonds)
>>> bytes(beer_card)
b'7\\x02'
>>> big_cassino = Card('10', Suite.diamonds)
>>> bytes(big_cassino)
b'10\\x02'
__ http://machaut.uchicago.edu/cgi-bin/WEBSTER.sh?WORD=spadille
"""
from enum import Enum
from operator import attrgetter
import re
spades diamonds clubs hearts
class Suite(Enum):
spades = '\u2660' # U+2660 ♠ BLACK SPADE SUIT
diamonds = '\u2662' # U+2662 ♢ WHITE DIAMOND SUIT
clubs = '\u2663' # U+2663 ♣ BLACK CLUB SUIT
hearts = '\u2661' # U+2661 ♡ WHITE HEART SUIT
def format_p(self):
return chr(0x2660 + self.value)
def format_s(self):
return self.name
def format_S(self):
return self.name.capitalize()
def __bytes__(self):
return bytes([self.value])
def __format__(self, format_spec):
use_spec = 's' if format_spec == '' else format_spec
format_method = getattr(self, 'format_' + use_spec, None)
if format_method:
return format_method()
msg = "Invalid format spec {!r} for object of type 'Suite'"
raise ValueError(msg.format(format_spec))
class Card:
def __init__(self, rank, suite, *, long_rank=None):
self.rank = rank
if long_rank is None:
self.long_rank = self.rank
else:
self.long_rank = long_rank
self.suite = suite
def __str__(self):
return '{long_rank} of {suite.name}'.format(**self.__dict__)
def __repr__(self):
template = '{cls.__name__}({rank!r}, {suite.name!r})'
return template.format(cls=self.__class__, **self.__dict__)
def __bytes__(self):
rank_bytes = bytes(ord(char) for char in self.rank)
return rank_bytes + bytes(self.suite)
rank_codes = {
'r': attrgetter('rank'),
'R': attrgetter('long_rank'),
}
def __format__(self, format_spec):
if not format_spec:
format_spec = 'r-s'
result = []
for code in format_spec:
if code in Card.rank_codes:
result.append(Card.rank_codes[code](self))
else:
try:
result.append(format(self.suite, code))
except ValueError:
result.append(code)
return ''.join(result)

28
objects/cheese.py Normal file
View File

@@ -0,0 +1,28 @@
"""
>>> import weakref
>>> stock = weakref.WeakValueDictionary()
>>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'),
... Cheese('Brie'), Cheese('Parmesan')]
...
>>> for cheese in catalog:
... stock[cheese.kind] = cheese
...
>>> sorted(stock.keys())
['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']
>>> del catalog
>>> sorted(stock.keys())
['Parmesan']
>>> del cheese
>>> sorted(stock.keys())
[]
"""
# BEGIN CHEESE_CLASS
class Cheese:
def __init__(self, kind):
self.kind = kind
def __repr__(self):
return 'Cheese(%r)' % self.kind
# END CHEESE_CLASS

25
objects/common_attrs.txt Normal file
View File

@@ -0,0 +1,25 @@
|object |instance|list |function|class
__class__ |type |type |type |type |type
__delattr__ |method |method |method |method |method
__dir__ |method |method |method |method |method
__doc__ |data |data |data |data |data
__eq__ |method |method |method |method |method
__format__ |method |method |method |method |method
__ge__ |method |method |method |method |method
__getattribute__ |method |method |method |method |method
__gt__ |method |method |method |method |method
__hash__ |method |method |data |method |method
__init__ |method |method |method |method |method
__le__ |method |method |method |method |method
__lt__ |method |method |method |method |method
__ne__ |method |method |method |method |method
__new__ |method |method |method |method |method
__reduce__ |method |method |method |method |method
__reduce_ex__ |method |method |method |method |method
__repr__ |method |method |method |method |method
__setattr__ |method |method |method |method |method
__sizeof__ |method |method |method |method |method
__str__ |method |method |method |method |method
__subclasshook__ |method |method |method |method |method
TOTALS | 22| 25| 34| 34| 28
['attr_list.py', '+']

47
objects/haunted_bus.py Normal file
View File

@@ -0,0 +1,47 @@
"""
>>> bus1 = HountedBus(['Alice', 'Bill'])
>>> bus1.passengers
['Alice', 'Bill']
>>> bus1.pick('Charlie')
>>> bus1.drop('Alice')
>>> bus1.passengers
['Bill', 'Charlie']
>>> bus2 = HountedBus()
>>> bus2.pick('Carrie')
>>> bus2.passengers
['Carrie']
>>> bus3 = HountedBus()
>>> bus3.passengers
['Carrie']
>>> bus3.pick('Dave')
>>> bus2.passengers
['Carrie', 'Dave']
>>> bus2.passengers is bus3.passengers
True
>>> bus1.passengers
['Bill', 'Charlie']
>>> dir(HountedBus.__init__) # doctest: +ELLIPSIS
['__annotations__', '__call__', ..., '__defaults__', ...]
>>> HountedBus.__init__.__defaults__
(['Carrie', 'Dave'],)
>>> HountedBus.__init__.__defaults__[0] is bus2.passengers
True
"""
# BEGIN HAUNTED_BUS_CLASS
class HountedBus:
"""A bus model hounted by ghost passengers"""
def __init__(self, passengers=[]): # <1>
self.passengers = passengers # <2>
def pick(self, name):
self.passengers.append(name) # <3>
def drop(self, name):
self.passengers.remove(name)
# END HAUNTED_BUS_CLASS

View File

@@ -0,0 +1,28 @@
|object |instance|list |function|class
__add__ | | |method | |
__annotations__ | | | |data |
__call__ | | | |method |method
__closure__ | | | |data |
__code__ | | | |data |
__contains__ | | |method | |
__defaults__ | | | |data |
__delitem__ | | |method | |
__dict__ | |data | |data |data
__get__ | | | |method |
__getitem__ | | |method | |
__globals__ | | | |data |
__iadd__ | | |method | |
__imul__ | | |method | |
__iter__ | | |method | |
__kwdefaults__ | | | |data |
__len__ | | |method | |
__module__ | |data | |data |data
__mul__ | | |method | |
__name__ | | | |data |data
__qualname__ | | | |data |data
__reversed__ | | |method | |
__rmul__ | | |method | |
__setitem__ | | |method | |
__weakref__ | |data | | |data
TOTALS | 22| 25| 34| 34| 28
['attr_list.py']

26
objects/twilight_bus.py Normal file
View File

@@ -0,0 +1,26 @@
"""
>>> basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
>>> bus = TwilightBus(basketball_team)
>>> bus.drop('Tina')
>>> bus.drop('Pat')
>>> basketball_team
['Sue', 'Maya', 'Diana']
"""
# BEGIN TWILIGHT_BUS_CLASS
class TwilightBus:
"""A bus model that makes passengers vanish"""
def __init__(self, passengers=None):
if passengers is None:
self.passengers = [] # <1>
else:
self.passengers = passengers #<2>
def pick(self, name):
self.passengers.append(name)
def drop(self, name):
self.passengers.remove(name) # <3>
# END TWILIGHT_BUS_CLASS

140
operator/vector.py Normal file
View File

@@ -0,0 +1,140 @@
"""
The `+` operator produces a `Vector` result.
>>> v1 = Vector(2, 4)
>>> v2 = Vector(2, 1)
>>> v1 + v2
Vector(4, 5)
We can also implemement the `*` operator to perform scalar multiplication
or elementwise multiplication.
>>> v = Vector(3, 4)
>>> abs(v)
5.0
>>> v * 3
Vector(9, 12)
>>> abs(v * 3)
15.0
>>> v25 = Vector(2, 5)
>>> v71 = Vector(7, 1)
>>> v71 * v25
Vector(14, 5)
A vector can be used in a boolean context, where it will be considered
_falsy_ if it has magnitude zero, otherwise it is _truthy_::
>>> bool(v)
True
>>> bool(Vector(0, 0))
False
Vectors can have n-dimensions::
>>> v3 = Vector(1, 2, 3)
>>> len(v3)
3
>>> v3
Vector(1, 2, 3)
>>> abs(v3) # doctest:+ELLIPSIS
3.74165738...
>>> v3 + Vector(4, 5, 6)
Vector(5, 7, 9)
>>> v3 * 5
Vector(5, 10, 15)
>>> v2 + v3
Traceback (most recent call last):
...
ValueError: Addition applies only to vectors of equal dimensions.
The `repr` of a Vector is produced with the help of the `reprlib.repr`
function, limiting the size of the output string:
>>> Vector(*range(100))
Vector(0, 1, 2, 3, 4, 5, ...)
Dot product is a scalar: the sum of the products of the corresponding
components of two vectors.
>>> v25 = Vector(2, 5)
>>> v71 = Vector(7, 1)
>>> v25.dot(v71)
19
>>> Vector(1, 2, 3).dot(Vector(4, 5, 6))
32
>>> Vector(1, 2, 3).dot(Vector(-2, 0, 5))
13
As described in PEP 465, starting with Python 3.5, `__matmul__` is
the special method for the new ``@`` operator, to be used the dot
product of vectors or matrix multiplication (as opposed to ``*``
which is intended for scalar or elementwise multiplication):
>>> # skip these tests on Python < 3.5
>>> v25 @ v71 # doctest:+SKIP
19
>>> v71 * v25
Vector(14, 5)
>>> Vector(1, 2, 3) @ Vector(-2, 0, 5) # doctest:+SKIP
13
"""
# BEGIN VECTOR_OPS
import math
import numbers
import reprlib
EQ_DIMENSIONS_MSG = '%s applies only to vectors of equal dimensions.'
class Vector:
"""An n-dimensional vector"""
def __init__(self, *components): # <1>
self._components = tuple(components) # <2>
def __repr__(self):
return 'Vector' + (reprlib.repr(self._components)) # <3>
def __iter__(self):
return iter(self._components) # <4>
def __abs__(self):
return math.sqrt(sum(comp*comp for comp in self)) # <5>
def __len__(self):
return len(self._components) # <6>
def __add__(self, other):
if len(self) != len(other):
raise ValueError(EQ_DIMENSIONS_MSG % 'Addition')
return Vector(*(a+b for a, b in zip(self, other))) # <7>
def __mul__(self, other):
if isinstance(other, numbers.Number):
return Vector(*(comp*other for comp in self)) # <8>
else:
return self.elementwise_mul(other) # <9>
def elementwise_mul(self, other):
if len(self) != len(other):
raise ValueError(EQ_DIMENSIONS_MSG %
'Elementwise multiplication')
return Vector(*(a*b for a, b in zip(self, other))) # <10>
def __bool__(self):
return any(self) # <11>
def dot(self, other):
if len(self) != len(other):
raise ValueError(EQ_DIMENSIONS_MSG %
'Dot product')
return sum(a*b for a, b in zip(self, other)) # <12>
__matmul__ = dot # support @ operator in Python 3.5
# END VECTOR_OPS

Some files were not shown because too many files have changed in this diff Show More