diff --git a/classes/vector_v1.py b/10-seq-hacking/vector_v1.py similarity index 100% rename from classes/vector_v1.py rename to 10-seq-hacking/vector_v1.py diff --git a/classes/vector_v2.py b/10-seq-hacking/vector_v2.py similarity index 92% rename from classes/vector_v2.py rename to 10-seq-hacking/vector_v2.py index fd94ae7..cfa7891 100644 --- a/classes/vector_v2.py +++ b/10-seq-hacking/vector_v2.py @@ -112,6 +112,7 @@ Test of slicing:: from array import array import reprlib import math +import numbers class Vector: @@ -150,13 +151,13 @@ class Vector: def __getitem__(self, index): cls = type(self) # <1> - if isinstance(index, slice): - return cls(self._components[index]) # <2> - elif isinstance(index, int): - return self._components[index] # <3> + if isinstance(index, slice): # <2> + return cls(self._components[index]) # <3> + elif isinstance(index, numbers.Integral): # <4> + return self._components[index] # <5> else: msg = '{cls.__name__} indices must be integers' - raise TypeError(msg.format(cls=cls)) # <4> + raise TypeError(msg.format(cls=cls)) # <6> # END VECTOR_V2 @classmethod diff --git a/classes/vector_v3.py b/10-seq-hacking/vector_v3.py similarity index 98% rename from classes/vector_v3.py rename to 10-seq-hacking/vector_v3.py index d248ee8..98e3cff 100644 --- a/classes/vector_v3.py +++ b/10-seq-hacking/vector_v3.py @@ -155,6 +155,7 @@ Other attributes can be set:: from array import array import reprlib import math +import numbers class Vector: @@ -194,7 +195,7 @@ class Vector: cls = type(self) if isinstance(index, slice): return cls(self._components[index]) - elif isinstance(index, int): + elif isinstance(index, numbers.Integral): return self._components[index] else: msg = '{.__name__} indices must be integers' diff --git a/classes/vector_v4.py b/10-seq-hacking/vector_v4.py similarity index 91% rename from classes/vector_v4.py rename to 10-seq-hacking/vector_v4.py index 93aec46..cb6d1e6 100644 --- a/classes/vector_v4.py +++ b/10-seq-hacking/vector_v4.py @@ -135,17 +135,22 @@ Tests of hashing:: >>> v2 = Vector([3.1, 4.2]) >>> v3 = Vector([3, 4, 5]) >>> v6 = Vector(range(6)) - >>> hash(v1), hash(v2), hash(v3), hash(v6) - (7, 384307168202284039, 2, 1) - >>> len(set([v1, v2, v3, v6])) - 4 + >>> hash(v1), hash(v3), hash(v6) + (7, 2, 1) +Most hash values of non-integers vary from a 32-bit to 64-bit CPython build:: + + >>> import sys + >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986) + True + """ from array import array import reprlib import math +import numbers import functools import operator @@ -192,11 +197,11 @@ class Vector: cls = type(self) if isinstance(index, slice): return cls(self._components[index]) - elif isinstance(index, int): + elif isinstance(index, numbers.Integral): return self._components[index] else: - msg = '{.__name__} indices must be integers' - raise TypeError(msg.format(cls)) + msg = '{cls.__name__} indices must be integers' + raise TypeError(msg.format(cls=cls)) shortcut_names = 'xyzt' diff --git a/classes/vector_v5.py b/10-seq-hacking/vector_v5.py similarity index 95% rename from classes/vector_v5.py rename to 10-seq-hacking/vector_v5.py index 13a0c2f..cf327bc 100644 --- a/classes/vector_v5.py +++ b/10-seq-hacking/vector_v5.py @@ -136,10 +136,15 @@ Tests of hashing:: >>> v2 = Vector([3.1, 4.2]) >>> v3 = Vector([3, 4, 5]) >>> v6 = Vector(range(6)) - >>> hash(v1), hash(v2), hash(v3), hash(v6) - (7, 384307168202284039, 2, 1) - >>> len(set([v1, v2, v3, v6])) - 4 + >>> hash(v1), hash(v3), hash(v6) + (7, 2, 1) + + +Most hash values of non-integers vary from a 32-bit to 64-bit CPython build:: + + >>> import sys + >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986) + True Tests of ``format()`` with Cartesian coordinates in 2D:: @@ -187,6 +192,7 @@ Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D:: from array import array import reprlib import math +import numbers import functools import operator import itertools # <1> @@ -234,7 +240,7 @@ class Vector: cls = type(self) if isinstance(index, slice): return cls(self._components[index]) - elif isinstance(index, int): + elif isinstance(index, numbers.Integral): return self._components[index] else: msg = '{.__name__} indices must be integers' diff --git a/interfaces/bingo.py b/11-iface-abc/bingo.py similarity index 100% rename from interfaces/bingo.py rename to 11-iface-abc/bingo.py diff --git a/attic/sequences/frenchdeck2.py b/11-iface-abc/frenchdeck2.py similarity index 100% rename from attic/sequences/frenchdeck2.py rename to 11-iface-abc/frenchdeck2.py diff --git a/interfaces/lotto.py b/11-iface-abc/lotto.py similarity index 100% rename from interfaces/lotto.py rename to 11-iface-abc/lotto.py diff --git a/interfaces/tombola.py b/11-iface-abc/tombola.py similarity index 100% rename from interfaces/tombola.py rename to 11-iface-abc/tombola.py diff --git a/interfaces/tombola_runner.py b/11-iface-abc/tombola_runner.py similarity index 100% rename from interfaces/tombola_runner.py rename to 11-iface-abc/tombola_runner.py diff --git a/interfaces/tombola_subhook.py b/11-iface-abc/tombola_subhook.py similarity index 100% rename from interfaces/tombola_subhook.py rename to 11-iface-abc/tombola_subhook.py diff --git a/interfaces/tombola_tests.rst b/11-iface-abc/tombola_tests.rst similarity index 84% rename from interfaces/tombola_tests.rst rename to 11-iface-abc/tombola_tests.rst index 30f0164..bc1ff06 100644 --- a/interfaces/tombola_tests.rst +++ b/11-iface-abc/tombola_tests.rst @@ -50,7 +50,7 @@ thrown when the device is empty:: OK -Load and pick 100 balls to verify that they are all come out:: +Load and pick 100 balls to verify that they all come out:: >>> balls = list(range(100)) >>> globe = ConcreteTombola(balls) @@ -63,7 +63,7 @@ Load and pick 100 balls to verify that they are all come out:: True -Check that the order has changed is not simply reversed either:: +Check that the order has changed and is not simply reversed:: >>> picks != balls True @@ -71,9 +71,9 @@ Check that the order has changed is not simply reversed either:: True Note: the previous 2 tests have a *very* small chance of failing -even if the implementation is OK. The probability of the 100 +even if the implementation is OK. The probability of the 100 balls coming out, by chance, in the order they were loaded is -1/100!, or approximately 1.07e-158. It's much easier to win the +1/100!, or approximately 1.07e-158. It's much easier to win the Lotto or to become a billionaire working as a programmer. THE END diff --git a/interfaces/tombolist.py b/11-iface-abc/tombolist.py similarity index 90% rename from interfaces/tombolist.py rename to 11-iface-abc/tombolist.py index ddf162c..7aaab1f 100644 --- a/interfaces/tombolist.py +++ b/11-iface-abc/tombolist.py @@ -8,7 +8,7 @@ class TomboList(list): # <2> def pick(self): if self: # <3> position = randrange(len(self)) - return super().pop(position) # <4> + return self.pop(position) # <4> else: raise LookupError('pop from empty TomboList') diff --git a/interfaces/diamond.py b/12-inheritance/diamond.py similarity index 65% rename from interfaces/diamond.py rename to 12-inheritance/diamond.py index 2d63877..5aaac18 100644 --- a/interfaces/diamond.py +++ b/12-inheritance/diamond.py @@ -14,6 +14,14 @@ class C(A): class D(B, C): - def pingpong(self): + + def ping(self): super().ping() + print('post-ping:', self) + + def pingpong(self): + self.ping() + super().ping() + self.pong() super().pong() + C.pong(self) diff --git a/classes/bingoaddable.py b/13-op-overloading/bingoaddable.py similarity index 100% rename from classes/bingoaddable.py rename to 13-op-overloading/bingoaddable.py diff --git a/classes/bingobase.py b/13-op-overloading/bingobase.py similarity index 100% rename from classes/bingobase.py rename to 13-op-overloading/bingobase.py diff --git a/13-op-overloading/unary_plus_decimal.py b/13-op-overloading/unary_plus_decimal.py new file mode 100644 index 0000000..46e481f --- /dev/null +++ b/13-op-overloading/unary_plus_decimal.py @@ -0,0 +1,35 @@ +""" +# BEGIN UNARY_PLUS_DECIMAL + +>>> import decimal +>>> ctx = decimal.getcontext() # <1> +>>> ctx.prec = 40 # <2> +>>> one_third = decimal.Decimal('1') / decimal.Decimal('3') # <3> +>>> one_third # <4> +Decimal('0.3333333333333333333333333333333333333333') +>>> one_third == +one_third # <5> +True +>>> ctx.prec = 28 # <6> +>>> one_third == +one_third # <7> +False +>>> +one_third # <8> +Decimal('0.3333333333333333333333333333') + +# END UNARY_PLUS_DECIMAL + +""" + +import decimal + +if __name__ == '__main__': + + with decimal.localcontext() as ctx: + ctx.prec = 40 + print('precision:', ctx.prec) + one_third = decimal.Decimal('1') / decimal.Decimal('3') + print(' one_third:', one_third) + print(' +one_third:', +one_third) + + print('precision:', decimal.getcontext().prec) + print(' one_third:', one_third) + print(' +one_third:', +one_third) diff --git a/13-op-overloading/vector2d_v3.py b/13-op-overloading/vector2d_v3.py new file mode 100644 index 0000000..5812dcf --- /dev/null +++ b/13-op-overloading/vector2d_v3.py @@ -0,0 +1,151 @@ +""" +A 2-dimensional vector class + + >>> v1 = Vector2d(3, 4) + >>> print(v1.x, v1.y) + 3.0 4.0 + >>> x, y = v1 + >>> x, y + (3.0, 4.0) + >>> v1 + Vector2d(3.0, 4.0) + >>> v1_clone = eval(repr(v1)) + >>> v1 == v1_clone + True + >>> print(v1) + (3.0, 4.0) + >>> octets = bytes(v1) + >>> octets + b'd\\x00\\x00\\x00\\x00\\x00\\x00\\x08@\\x00\\x00\\x00\\x00\\x00\\x00\\x10@' + >>> abs(v1) + 5.0 + >>> bool(v1), bool(Vector2d(0, 0)) + (True, False) + + +Test of ``.frombytes()`` class method: + + >>> v1_clone = Vector2d.frombytes(bytes(v1)) + >>> v1_clone + Vector2d(3.0, 4.0) + >>> v1 == v1_clone + True + + +Tests of ``format()`` with Cartesian 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:: + + >>> Vector2d(0, 0).angle() + 0.0 + >>> Vector2d(1, 0).angle() + 0.0 + >>> epsilon = 10**-8 + >>> abs(Vector2d(0, 1).angle() - math.pi/2) < epsilon + True + >>> abs(Vector2d(1, 1).angle() - math.pi/4) < epsilon + True + + +Tests of ``format()`` with polar coordinates: + + >>> format(Vector2d(1, 1), 'p') # doctest:+ELLIPSIS + '<1.414213..., 0.785398...>' + >>> format(Vector2d(1, 1), '.3ep') + '<1.414e+00, 7.854e-01>' + >>> format(Vector2d(1, 1), '0.5fp') + '<1.41421, 0.78540>' + + +Tests 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 + + +Tests of hashing: + + >>> v1 = Vector2d(3, 4) + >>> v2 = Vector2d(3.1, 4.2) + >>> hash(v1), hash(v2) + (7, 384307168202284039) + >>> len(set([v1, v2])) + 2 + +""" + +from array import array +import math + +class Vector2d: + typecode = 'd' + + 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)) + + def __repr__(self): + class_name = type(self).__name__ + return '{}({!r}, {!r})'.format(class_name, *self) + + def __str__(self): + return str(tuple(self)) + + def __bytes__(self): + return (bytes([ord(self.typecode)]) + + bytes(array(self.typecode, self))) + + def __eq__(self, other): + return tuple(self) == tuple(other) + + def __hash__(self): + return hash(self.x) ^ hash(self.y) + + 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): + typecode = chr(octets[0]) + memv = memoryview(octets[1:]).cast(typecode) + return cls(*memv) diff --git a/classes/vector_py3_5.py b/13-op-overloading/vector_py3_5.py similarity index 97% rename from classes/vector_py3_5.py rename to 13-op-overloading/vector_py3_5.py index 675be99..32a59fb 100644 --- a/classes/vector_py3_5.py +++ b/13-op-overloading/vector_py3_5.py @@ -137,10 +137,15 @@ Tests of hashing:: >>> v2 = Vector([3.1, 4.2]) >>> v3 = Vector([3, 4, 5]) >>> v6 = Vector(range(6)) - >>> hash(v1), hash(v2), hash(v3), hash(v6) - (7, 384307168202284039, 2, 1) - >>> len(set([v1, v2, v3, v6])) - 4 + >>> hash(v1), hash(v3), hash(v6) + (7, 2, 1) + + +Most hash values of non-integers vary from a 32-bit to 64-bit Python build:: + + >>> import sys + >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986) + True Tests of ``format()`` with Cartesian coordinates in 2D:: diff --git a/classes/vector_v6.py b/13-op-overloading/vector_v6.py similarity index 92% rename from classes/vector_v6.py rename to 13-op-overloading/vector_v6.py index 2942f92..ff7599f 100644 --- a/classes/vector_v6.py +++ b/13-op-overloading/vector_v6.py @@ -135,10 +135,15 @@ Tests of hashing:: >>> v2 = Vector([3.1, 4.2]) >>> v3 = Vector([3, 4, 5]) >>> v6 = Vector(range(6)) - >>> hash(v1), hash(v2), hash(v3), hash(v6) - (7, 384307168202284039, 2, 1) - >>> len(set([v1, v2, v3, v6])) - 4 + >>> hash(v1), hash(v3), hash(v6) + (7, 2, 1) + + +Most hash values of non-integers vary from a 32-bit to 64-bit Python build:: + + >>> import sys + >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986) + True Tests of ``format()`` with Cartesian coordinates in 2D:: @@ -183,6 +188,17 @@ Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D:: '<1.00000, 1.57080, 0.00000, 0.00000>' +Unary operator tests:: + + >>> v1 = Vector([3, 4]) + >>> abs(v1) + 5.0 + >>> -v1 + Vector([-3.0, -4.0]) + >>> +v1 + Vector([3.0, 4.0]) + + Basic tests of operator ``+``:: >>> v1 = Vector([3, 4, 5]) @@ -231,6 +247,7 @@ Tests of ``+`` with an unsuitable operand: from array import array import reprlib import math +import numbers import functools import operator import itertools @@ -265,9 +282,17 @@ class Vector: hashes = (hash(x) for x in self) return functools.reduce(operator.xor, hashes, 0) +# BEGIN VECTOR_V6_UNARY def __abs__(self): return math.sqrt(sum(x * x for x in self)) + def __neg__(self): + return Vector(-x for x in self) # <1> + + def __pos__(self): + return Vector(self) # <2> +# END VECTOR_V6_UNARY + def __bool__(self): return bool(abs(self)) @@ -278,7 +303,7 @@ class Vector: cls = type(self) if isinstance(index, slice): return cls(self._components[index]) - elif isinstance(index, int): + elif isinstance(index, numbers.Integral): return self._components[index] else: msg = '{.__name__} indices must be integers' @@ -324,7 +349,7 @@ class Vector: memv = memoryview(octets[1:]).cast(typecode) return cls(memv) -# BEGIN VECTOR_V6 +# BEGIN VECTOR_V6_ADD def __add__(self, other): try: pairs = itertools.zip_longest(self, other, fillvalue=0.0) @@ -334,4 +359,4 @@ class Vector: def __radd__(self, other): return self + other -# END VECTOR_V6 +# END VECTOR_V6_ADD diff --git a/classes/vector_v7.py b/13-op-overloading/vector_v7.py similarity index 94% rename from classes/vector_v7.py rename to 13-op-overloading/vector_v7.py index 73432ba..a550318 100644 --- a/classes/vector_v7.py +++ b/13-op-overloading/vector_v7.py @@ -135,10 +135,15 @@ Tests of hashing:: >>> v2 = Vector([3.1, 4.2]) >>> v3 = Vector([3, 4, 5]) >>> v6 = Vector(range(6)) - >>> hash(v1), hash(v2), hash(v3), hash(v6) - (7, 384307168202284039, 2, 1) - >>> len(set([v1, v2, v3, v6])) - 4 + >>> hash(v1), hash(v3), hash(v6) + (7, 2, 1) + + +Most hash values of non-integers vary from a 32-bit to 64-bit Python build:: + + >>> import sys + >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986) + True Tests of ``format()`` with Cartesian coordinates in 2D:: @@ -183,6 +188,17 @@ Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D:: '<1.00000, 1.57080, 0.00000, 0.00000>' +Unary operator tests:: + + >>> v1 = Vector([3, 4]) + >>> abs(v1) + 5.0 + >>> -v1 + Vector([-3.0, -4.0]) + >>> +v1 + Vector([3.0, 4.0]) + + Basic tests of operator ``+``:: >>> v1 = Vector([3, 4, 5]) @@ -258,10 +274,10 @@ Tests of ``*`` with unsuitable operands:: from array import array import reprlib import math +import numbers import functools import operator import itertools -import numbers class Vector: @@ -296,6 +312,12 @@ class Vector: def __abs__(self): return math.sqrt(sum(x * x for x in self)) + def __neg__(self): + return Vector(-x for x in self) + + def __pos__(self): + return Vector(self) + def __bool__(self): return bool(abs(self)) @@ -306,7 +328,7 @@ class Vector: cls = type(self) if isinstance(index, slice): return cls(self._components[index]) - elif isinstance(index, int): + elif isinstance(index, numbers.Integral): return self._components[index] else: msg = '{.__name__} indices must be integers' diff --git a/classes/vector_v8.py b/13-op-overloading/vector_v8.py similarity index 94% rename from classes/vector_v8.py rename to 13-op-overloading/vector_v8.py index e743b33..bcf4fba 100644 --- a/classes/vector_v8.py +++ b/13-op-overloading/vector_v8.py @@ -135,10 +135,15 @@ Tests of hashing:: >>> v2 = Vector([3.1, 4.2]) >>> v3 = Vector([3, 4, 5]) >>> v6 = Vector(range(6)) - >>> hash(v1), hash(v2), hash(v3), hash(v6) - (7, 384307168202284039, 2, 1) - >>> len(set([v1, v2, v3, v6])) - 4 + >>> hash(v1), hash(v3), hash(v6) + (7, 2, 1) + + +Most hash values of non-integers vary from a 32-bit to 64-bit Python build:: + + >>> import sys + >>> hash(v2) == (384307168202284039 if sys.maxsize > 2**32 else 357915986) + True Tests of ``format()`` with Cartesian coordinates in 2D:: @@ -183,6 +188,17 @@ Tests of ``format()`` with spherical coordinates in 2D, 3D and 4D:: '<1.00000, 1.57080, 0.00000, 0.00000>' +Unary operator tests:: + + >>> v1 = Vector([3, 4]) + >>> abs(v1) + 5.0 + >>> -v1 + Vector([-3.0, -4.0]) + >>> +v1 + Vector([3.0, 4.0]) + + Basic tests of operator ``+``:: >>> v1 = Vector([3, 4, 5]) @@ -283,10 +299,10 @@ Tests of operator `!=`:: from array import array import reprlib import math +import numbers import functools import operator import itertools -import numbers class Vector: @@ -326,6 +342,12 @@ class Vector: def __abs__(self): return math.sqrt(sum(x * x for x in self)) + def __neg__(self): + return Vector(-x for x in self) + + def __pos__(self): + return Vector(self) + def __bool__(self): return bool(abs(self)) @@ -336,7 +358,7 @@ class Vector: cls = type(self) if isinstance(index, slice): return cls(self._components[index]) - elif isinstance(index, int): + elif isinstance(index, numbers.Integral): return self._components[index] else: msg = '{.__name__} indices must be integers' diff --git a/concurrency/parallel/lelo_ex.py b/concurrency/parallel/lelo_ex.py new file mode 100644 index 0000000..f7802a0 --- /dev/null +++ b/concurrency/parallel/lelo_ex.py @@ -0,0 +1,24 @@ +import os +from time import sleep, time + +from lelo import parallel + +DELAY = .2 + +@parallel +def loiter(serial, delay): + pid = os.getpid() + print('%2d pid = %d' % (serial, pid)) + sleep(delay) + return pid + +t0 = time() + +results = [] +for i in range(15): + res = loiter(i, DELAY) + results.append(res) + +print('Processes used: ', list(set(results))) + +print('### Elapsed time: %0.2f' % (time() - t0)) diff --git a/concurrency/parallel/llize.py b/concurrency/parallel/llize.py new file mode 100644 index 0000000..7e4f873 --- /dev/null +++ b/concurrency/parallel/llize.py @@ -0,0 +1,19 @@ +import os +from parallelize import parallelize +from time import sleep, time + +print('one process:') +t0 = time() +for i in range(12): + print('%2d pid = %d' % (i, os.getpid())) + sleep(.2) +print('elapsed time: %0.2f' % (time() - t0)) + +print() + +print('several processes:') +t0 = time() +for i in parallelize(range(12)): + print('%2d pid = %d' % (i, os.getpid())) + sleep(.2) +print('elapsed time: %0.2f' % (time() - t0)) diff --git a/concurrency/parallel/llize_ex.py b/concurrency/parallel/llize_ex.py new file mode 100644 index 0000000..2bbc59c --- /dev/null +++ b/concurrency/parallel/llize_ex.py @@ -0,0 +1,23 @@ +import os +from time import sleep, time + +from parallelize import parallelize, per_item + +DELAY = .2 + +def loiter(serial, delay): + pid = os.getpid() + print('%2d pid = %d' % (serial, pid)) + sleep(delay) + return pid + +t0 = time() + +results = [] +for i in parallelize(range(15), fork=per_item): + res = loiter(i, DELAY) + results.append(res) + +print('Processes used: ', list(set(results))) + +print('### Elapsed time: %0.2f' % (time() - t0))