updated contents from Atlas repo
This commit is contained in:
24
classes/mem_test.py
Normal file
24
classes/mem_test.py
Normal 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
60
classes/vector_v0.py
Normal 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
86
classes/vector_v1.py
Normal 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
113
classes/vector_v2.py
Normal 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)
|
||||||
115
classes/vector_v2_fmt_snippet.py
Normal file
115
classes/vector_v2_fmt_snippet.py
Normal 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
123
classes/vector_v3.py
Normal 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
125
classes/vector_v3_slots.py
Normal 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
124
closures/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
|
||||||
|
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
|
||||||
54
datamodel/frenchdeck.doctest
Normal file
54
datamodel/frenchdeck.doctest
Normal 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
17
datamodel/frenchdeck.py
Normal 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
4
datamodel/tox.ini
Normal 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
24
datamodel/vector2d.py
Normal 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
33
decorators/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
|
||||||
20
decorators/average_broken.py
Normal file
20
decorators/average_broken.py
Normal 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
|
||||||
45
decorators/average_fixed.py
Normal file
45
decorators/average_fixed.py
Normal 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
|
||||||
39
decorators/average_fixed_py2.py
Normal file
39
decorators/average_fixed_py2.py
Normal 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
21
decorators/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)
|
||||||
34
decorators/average_partial.py
Normal file
34
decorators/average_partial.py
Normal 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
28
decorators/average_py2.py
Normal 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
15
decorators/clockdeco.py
Normal 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
23
decorators/clockdeco2.py
Normal 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
|
||||||
31
decorators/clockdeco2_demo.py
Normal file
31
decorators/clockdeco2_demo.py
Normal 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)
|
||||||
95
decorators/clockdeco2_tests.py
Normal file
95
decorators/clockdeco2_tests.py
Normal 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)
|
||||||
18
decorators/clockdeco_demo.py
Normal file
18
decorators/clockdeco_demo.py
Normal 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))
|
||||||
22
decorators/clockdeco_demo2.py
Normal file
22
decorators/clockdeco_demo2.py
Normal 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))
|
||||||
20
decorators/clockdeco_demo3.py
Normal file
20
decorators/clockdeco_demo3.py
Normal 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))
|
||||||
40
decorators/clockdeco_param.py
Normal file
40
decorators/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
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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
|
||||||
9
decorators/clockdeco_param_demo1.py
Normal file
9
decorators/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
decorators/clockdeco_param_demo2.py
Normal file
9
decorators/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)
|
||||||
82
decorators/clockdeco_tests.py
Normal file
82
decorators/clockdeco_tests.py
Normal 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
50
decorators/currency.py
Normal 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
10
decorators/fibo_demo.py
Normal 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))
|
||||||
13
decorators/fibo_demo_lru.py
Normal file
13
decorators/fibo_demo_lru.py
Normal 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
60
decorators/fibonacci.py
Normal 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
49
decorators/generic.py
Normal 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><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>
|
||||||
|
|
||||||
|
# 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
63
decorators/local_demo.py
Normal 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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
31
decorators/registration.py
Normal file
31
decorators/registration.py
Normal 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
|
||||||
16
decorators/registration_abridged.py
Normal file
16
decorators/registration_abridged.py
Normal 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
|
||||||
29
decorators/registration_param.py
Normal file
29
decorators/registration_param.py
Normal 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
|
||||||
27
decorators/stacked_demo.py
Normal file
27
decorators/stacked_demo.py
Normal 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()
|
||||||
113
decorators/strategy_best4.py
Normal file
113
decorators/strategy_best4.py
Normal 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
|
||||||
55
dicts/container_perftest.py
Normal file
55
dicts/container_perftest.py
Normal 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)
|
||||||
37
dicts/container_perftest_datagen.py
Normal file
37
dicts/container_perftest_datagen.py
Normal 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
30
dicts/dialcodes.py
Normal 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
34
dicts/dict_perftest.py
Normal 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
20
dicts/hashdiff.py
Normal 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
25
dicts/index.py
Normal 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
28
dicts/index0.py
Normal 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
22
dicts/index_alex.py
Normal 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
26
dicts/index_default.py
Normal 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
48
dicts/set_perftest.py
Normal 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
72
dicts/strkeydict.py
Normal 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
39
dicts/strkeydict0.py
Normal 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
|
||||||
25
functions/attrgetter_demo.py
Normal file
25
functions/attrgetter_demo.py
Normal 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))
|
||||||
31
functions/attrgetter_demo.rst
Normal file
31
functions/attrgetter_demo.rst
Normal 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
30
functions/bingo.py
Normal 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
38
functions/clip.py
Normal 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
38
functions/clip_annot.py
Normal 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
|
||||||
10
functions/clip_annot_signature.rst
Normal file
10
functions/clip_annot_signature.rst
Normal 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
|
||||||
9
functions/clip_introspection.rst
Normal file
9
functions/clip_introspection.rst
Normal 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
|
||||||
12
functions/clip_signature.rst
Normal file
12
functions/clip_signature.rst
Normal 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
5
functions/hello.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import bobo
|
||||||
|
|
||||||
|
@bobo.query('/')
|
||||||
|
def hello(person):
|
||||||
|
return 'Hello %s!' % person
|
||||||
76
functions/strkeydict2.py
Normal file
76
functions/strkeydict2.py
Normal 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
44
functions/tagger.py
Normal 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
19
interfaces/bingo.py
Normal 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
17
interfaces/drum.py
Normal 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()
|
||||||
61
interfaces/exceptions-tree.txt
Normal file
61
interfaces/exceptions-tree.txt
Normal 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
24
interfaces/lotto.py
Normal 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
25
interfaces/tombola.py
Normal 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
|
||||||
52
interfaces/tombola_runner.py
Normal file
52
interfaces/tombola_runner.py
Normal 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)
|
||||||
70
interfaces/tombola_tests.rst
Normal file
70
interfaces/tombola_tests.rst
Normal 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
19
interfaces/tombolist.py
Normal 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>
|
||||||
5
iterables/CACM/citation.txt
Normal file
5
iterables/CACM/citation.txt
Normal 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
|
||||||
10
iterables/CACM/closed_file.py
Normal file
10
iterables/CACM/closed_file.py
Normal 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
17
iterables/CACM/haha.py
Normal 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)
|
||||||
22
iterables/CACM/less_more.py
Normal file
22
iterables/CACM/less_more.py
Normal 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)
|
||||||
14
iterables/CACM/zero_div.py
Normal file
14
iterables/CACM/zero_div.py
Normal 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)
|
||||||
40
iterables/sentence_gen.doctest
Normal file
40
iterables/sentence_gen.doctest
Normal 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
23
iterables/sentence_gen.py
Normal 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>
|
||||||
40
iterables/sentence_genexp.doctest
Normal file
40
iterables/sentence_genexp.doctest
Normal 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']
|
||||||
44
iterables/sentence_genexp.py
Normal file
44
iterables/sentence_genexp.py
Normal 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()
|
||||||
40
iterables/sentence_iter.doctest
Normal file
40
iterables/sentence_iter.doctest
Normal 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']
|
||||||
65
iterables/sentence_iter.py
Normal file
65
iterables/sentence_iter.py
Normal 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()
|
||||||
40
iterables/sentence_iter2.doctest
Normal file
40
iterables/sentence_iter2.doctest
Normal 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']
|
||||||
37
iterables/sentence_iter2.py
Normal file
37
iterables/sentence_iter2.py
Normal 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
|
||||||
42
iterables/simplest_generators.doctest
Normal file
42
iterables/simplest_generators.doctest
Normal 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
103
iterables/vector.py
Normal 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
|
||||||
43
iterables/vector_flex_init.py
Normal file
43
iterables/vector_flex_init.py
Normal 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)
|
||||||
@@ -7,4 +7,4 @@ __pycache__
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
*.arr
|
*.arr
|
||||||
README.rst
|
README.rst
|
||||||
LICENCE
|
LICENSE
|
||||||
|
|||||||
61
objects/attr_list.py
Normal file
61
objects/attr_list.py
Normal 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)
|
||||||
28
objects/attrs_not_in_object.py
Normal file
28
objects/attrs_not_in_object.py
Normal 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
29
objects/bus.py
Normal 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
54
objects/cards.py
Normal 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
122
objects/cards_format.py
Normal 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
28
objects/cheese.py
Normal 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
25
objects/common_attrs.txt
Normal 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
47
objects/haunted_bus.py
Normal 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
|
||||||
|
|
||||||
28
objects/not_so_common_attrs.txt
Normal file
28
objects/not_so_common_attrs.txt
Normal 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
26
objects/twilight_bus.py
Normal 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
140
operator/vector.py
Normal 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
Reference in New Issue
Block a user