├── .gitignore
├── 01-data-model
├── README.rst
├── frenchdeck.doctest
├── frenchdeck.ipynb
├── frenchdeck.py
├── frenchdeck_soln.ipynb
├── vector2d.ipynb
├── vector2d.py
└── vector2d_soln.ipynb
├── 02-array-seq
├── README.rst
├── bisect_demo.py
├── bisect_insort.py
├── listcomp_speed.py
└── metro_lat_long.py
├── 03-dict-set
├── README.rst
├── dialcodes.ipynb
├── dialcodes.py
├── index.ipynb
├── index.py
├── index0.py
├── index_default.py
├── strkeydict.py
├── strkeydict0.py
├── support
│ ├── container_perftest.py
│ ├── container_perftest_datagen.py
│ └── hashdiff.py
└── transformdict.py
├── 04-text-byte
├── README.rst
├── default_encodings.py
├── normeq.py
├── numerics_demo.py
├── ola.py
├── ramanujan.py
└── sanitize.py
├── 05-1class-func
├── README.rst
├── bingocall.py
├── chapter05.ipynb
├── clip.py
├── clip_annot.py
├── clip_annot_signature.rst
├── clip_introspection.rst
├── clip_signature.rst
└── tagger.py
├── 06-dp-1class-func
├── README.rst
├── classic_strategy.py
├── promotions.py
├── strategy.py
├── strategy_best.py
├── strategy_best2.py
└── strategy_best3.py
├── 07-closure-deco
├── README.rst
├── average.py
├── average_oo.py
├── clockdeco.py
├── clockdeco_cls.py
├── clockdeco_demo.py
├── clockdeco_param.py
├── clockdeco_param_demo1.py
├── clockdeco_param_demo2.py
├── fibo_demo.py
├── fibo_demo_lru.py
├── generic.py
├── global_x_local.rst
├── registration.py
├── registration_abridged.py
├── registration_param.py
└── strategy_best4.py
├── 08-obj-ref
├── README.rst
├── bus.py
├── bus_solution.ipynb
├── cheese.py
├── haunted_bus.py
└── twilight_bus.py
├── 09-pythonic-obj
├── Chapter 9.ipynb
├── README.rst
├── mem_test.py
├── private
│ ├── Confidential.java
│ ├── Expose.java
│ ├── expose.py
│ ├── leakprivate.py
│ └── no_respect.py
├── vector2d_v0.py
├── vector2d_v1.py
├── vector2d_v2.py
├── vector2d_v2_fmt_snippet.py
├── vector2d_v3.py
├── vector2d_v3_prophash.py
└── vector2d_v3_slots.py
├── 10-seq-hacking
├── README.rst
├── vector_v1.py
├── vector_v2.py
├── vector_v3.py
├── vector_v4.py
└── vector_v5.py
├── 11-iface-abc
├── 11-iface-abc.ipynb
├── README.rst
├── bingo.py
├── drum.py
├── frenchdeck2.py
├── lotto.py
├── tombola.py
├── tombola_runner.py
├── tombola_subhook.py
├── tombola_tests.rst
└── tombolist.py
├── 12-inheritance
├── README.rst
└── diamond.py
├── 13-op-overloading
├── README.rst
├── bingo.py
├── bingoaddable.py
├── tombola.py
├── unary_plus_decimal.py
├── vector2d_v3.py
├── vector_py3_5.py
├── vector_v6.py
├── vector_v7.py
└── vector_v8.py
├── 14-it-generator
├── README.rst
├── aritprog.rst
├── aritprog_float_error.py
├── aritprog_runner.py
├── aritprog_v0.py
├── aritprog_v1.py
├── aritprog_v2.py
├── aritprog_v3.py
├── fibo_by_hand.py
├── isis2json
│ ├── README.rst
│ ├── isis2json.py
│ ├── iso2709.py
│ └── subfield.py
├── sentence.py
├── sentence.rst
├── sentence_gen.py
├── sentence_gen2.py
├── sentence_genexp.py
├── sentence_iter.py
├── sentence_iter2.py
├── sentence_runner.py
├── yield_delegate_fail.py
└── yield_delegate_fix.py
├── 15-context-mngr
├── README.rst
├── mirror.py
├── mirror_gen.py
└── mirror_gen_exc.py
├── 16-coroutine
├── README.rst
├── coro_exc_demo.py
├── coro_finally_demo.py
├── coroaverager0.py
├── coroaverager1.py
├── coroaverager2.py
├── coroaverager3.py
├── coroutil.py
├── taxi_sim.py
├── taxi_sim0.py
├── taxi_sim_delay.py
├── yield_from_expansion.py
└── yield_from_expansion_simplified.py
├── 17-futures
├── README.rst
├── countries
│ ├── README.rst
│ ├── country_codes.txt
│ ├── flags.py
│ ├── flags.zip
│ ├── flags2_asyncio.py
│ ├── flags2_asyncio_executor.py
│ ├── flags2_await.py
│ ├── flags2_common.py
│ ├── flags2_sequential.py
│ ├── flags2_threadpool.py
│ ├── flags3_asyncio.py
│ ├── flags3_threadpool.py
│ ├── flags_asyncio.py
│ ├── flags_await.py
│ ├── flags_threadpool.py
│ ├── flags_threadpool_ac.py
│ ├── requirements.txt
│ ├── vaurien_delay.sh
│ └── vaurien_error_delay.sh
├── crypto
│ ├── arcfour-timings.txt
│ ├── arcfour.py
│ ├── arcfour_futures.py
│ ├── arcfour_test.py
│ ├── sha-timings.txt
│ └── sha_futures.py
└── demo_executor_map.py
├── 18-asyncio
├── README.rst
├── charfinder
│ ├── charfinder.py
│ ├── http_charfinder.html
│ ├── http_charfinder.py
│ ├── tcp_charfinder.py
│ └── test_charfinder.py
├── countries
│ └── README.rst
├── spinner_asyncio.ipynb
├── spinner_asyncio.py
├── spinner_thread.ipynb
└── spinner_thread.py
├── 18b-async-await
├── README.rst
├── charfinder
│ ├── .gitignore
│ ├── charfinder.py
│ ├── http_charfinder.html
│ ├── http_charfinder.py
│ ├── tcp_charfinder.py
│ └── test_charfinder.py
├── countries
│ └── README.rst
├── spinner_asyncio.py
├── spinner_await.py
└── spinner_thread.py
├── 19-dyn-attr-prop
├── README.rst
├── blackknight.py
├── bulkfood
│ ├── bulkfood_v1.py
│ ├── bulkfood_v2.py
│ ├── bulkfood_v2b.py
│ └── bulkfood_v2prop.py
├── doc_property.py
├── oscon
│ ├── data
│ │ └── osconfeed.json
│ ├── demo_schedule2.py
│ ├── explore0.py
│ ├── explore1.py
│ ├── explore2.py
│ ├── osconfeed-sample.json
│ ├── osconfeed.py
│ ├── schedule1.py
│ ├── schedule2.py
│ ├── test_schedule1.py
│ └── test_schedule2.py
└── pseudo_construction.py
├── 20-descriptor
├── README.rst
├── bulkfood
│ ├── auto_chapter20.ipynb
│ ├── bulkfood_v3.py
│ ├── bulkfood_v4.py
│ ├── bulkfood_v4b.py
│ ├── bulkfood_v4c.py
│ ├── bulkfood_v4prop.py
│ ├── bulkfood_v5.py
│ ├── bulkfood_v5_check.py
│ ├── chapter20code.ipynb
│ ├── helper_use_chapter20.ipynb
│ ├── helpers.py
│ ├── model_v4c.py
│ ├── model_v5.py
│ └── model_v5_check.py
├── descriptorkinds.py
├── descriptorkinds_dump.py
└── method_is_descriptor.py
├── 21-class-metaprog
├── README.rst
├── bulkfood
│ ├── bulkfood_v6.py
│ ├── bulkfood_v7.py
│ ├── bulkfood_v8.py
│ ├── model_v6.py
│ ├── model_v7.py
│ └── model_v8.py
├── evalsupport.py
├── evaltime.py
├── evaltime_meta.py
└── factories.py
├── LICENSE
├── README.md
├── attic
├── attributes
│ ├── exists_truthy.py
│ └── hasattr.py
├── classes
│ ├── spherical-coordinates.txt
│ ├── sum-nth-element.rst
│ └── test_vector_spherical.py
├── concurrency
│ ├── charfinder
│ │ ├── charfinder.html
│ │ ├── charfinder.py
│ │ ├── http_charfinder.py
│ │ ├── http_charfinder2.py
│ │ ├── tcp_charfinder.py
│ │ └── test_charfinder.py
│ ├── flags
│ │ ├── README.rst
│ │ ├── add_continent.py
│ │ ├── build_fixture.py
│ │ ├── build_fixture_with_continents.py
│ │ ├── cc_count.py
│ │ ├── cc_tlds.py
│ │ ├── continents.zip
│ │ ├── count_colors.py
│ │ ├── countries-continents.tab
│ │ ├── country-codes.tab
│ │ ├── countryflags.py
│ │ ├── fixture.tar.gz
│ │ ├── getsequential.py
│ │ ├── getthreadpool.py
│ │ ├── graphs.ods
│ │ ├── img.zip
│ │ ├── img
│ │ │ └── README.txt
│ │ ├── tlds.tab
│ │ ├── vaurien_delay.sh
│ │ └── vaurien_error_delay.sh
│ ├── parallel
│ │ ├── lelo_ex.py
│ │ ├── llize.py
│ │ └── llize_ex.py
│ ├── spinner_asyncio.py
│ ├── spinner_asyncio2.py
│ ├── spinner_proc.py
│ ├── spinner_thread.py
│ ├── timer.py
│ ├── timer2.py
│ ├── timer_cb.py
│ ├── timer_clo.py
│ ├── timer_seq.py
│ └── wikipedia
│ │ ├── README.rst
│ │ ├── build_fixture.py
│ │ ├── daypicts.py
│ │ ├── daypicts_asyncio.py
│ │ ├── daypicts_threads.py
│ │ ├── delay.sh
│ │ ├── fast_tests.sh
│ │ ├── fixture
│ │ ├── README.rst
│ │ └── docroot.zip
│ │ ├── orig
│ │ ├── README.rst
│ │ ├── futureprocs.py
│ │ ├── futurethreads.py
│ │ ├── potd.py
│ │ ├── potd_tests.py
│ │ ├── sync.py
│ │ └── sync_py3.py
│ │ └── test_daypicts.py
├── control
│ ├── adder
│ │ ├── coroadder.py
│ │ ├── coroadder0.py
│ │ ├── coroadder_deco.py
│ │ ├── soma.py
│ │ ├── soma_deco.py
│ │ ├── yetanother.py
│ │ └── yield_from_input.py
│ ├── coro_demo.rst
│ ├── coro_simple_demo.rst
│ ├── coroaverager.py
│ ├── countdown_yf.py
│ ├── demo_coro.py
│ ├── exemplo0.py
│ ├── exemplo1.py
│ ├── flatten.py
│ ├── guido
│ │ ├── guido0.py
│ │ ├── guido1.py
│ │ ├── guido1b.py
│ │ ├── guido2.py
│ │ └── guido3.py
│ ├── http_cli0.py
│ └── kwcombos.py
├── decorators
│ ├── average_broken.py
│ ├── average_fixed.py
│ ├── average_fixed_py2.py
│ ├── average_partial.py
│ ├── average_py2.py
│ ├── clockdeco2.py
│ ├── clockdeco2_demo.py
│ ├── clockdeco2_tests.py
│ ├── clockdeco_demo2.py
│ ├── clockdeco_demo3.py
│ ├── clockdeco_tests.py
│ ├── currency.py
│ ├── fibonacci.py
│ ├── local_demo.py
│ └── stacked_demo.py
├── descriptors
│ └── doc_descriptor.py
├── dicts
│ ├── dict_perftest.py
│ ├── index_alex.py
│ ├── set_perftest.py
│ ├── strkeydict0_userdictsub.py
│ ├── strkeydict_dictsub.py
│ └── test_transformdict.py
├── functions
│ ├── accgen.py
│ ├── attrgetter_demo.py
│ ├── attrgetter_demo.rst
│ ├── hello.py
│ └── strkeydict2.py
├── futures
│ ├── callbackhell.js
│ ├── callbackhell.py
│ ├── charfinder
│ │ └── charfinder_index.pickle
│ ├── coroutine_purgatory.py
│ ├── countries
│ │ ├── README.rst
│ │ ├── flags_asyncio2.py
│ │ ├── flags_processpool.py
│ │ └── notes.txt
│ ├── demo_executor_submit.py
│ ├── future_yield.py
│ └── future_yield2.py
├── interfaces
│ ├── dict_subclass.py
│ ├── exceptions-tree.txt
│ ├── pypy_difference.rst
│ └── subclassing_builtins.rst
├── iterables
│ ├── CACM
│ │ ├── citation.txt
│ │ ├── closed_file.py
│ │ ├── haha.py
│ │ ├── less_more.py
│ │ └── zero_div.py
│ ├── almost_aritprog_v0.py
│ ├── almost_aritprog_v6.py
│ ├── aritprog_v4.py
│ ├── aritprog_v5.py
│ ├── paragraph.py
│ ├── simplest_generators.doctest
│ ├── vector.py
│ └── vector_flex_init.py
├── metaprog
│ ├── plainpoint.py
│ ├── prop_inheritance.py
│ ├── special_attrs.py
│ ├── spreadsheet.py
│ └── spreadsheet2.py
├── objects
│ ├── attr_list.py
│ ├── attrs_not_in_object.py
│ ├── cards.py
│ ├── cards_format.py
│ ├── common_attrs.txt
│ └── not_so_common_attrs.txt
├── operator
│ ├── Interest.java
│ ├── dispatch.py
│ ├── factorial
│ │ ├── CorrectFactorial.java
│ │ ├── SimpleFactorial.java
│ │ └── factorial.py
│ ├── interest.py
│ └── vector.py
├── sequences
│ ├── bisect_demo_pos.py
│ ├── bisect_find.py
│ ├── bisect_in.py
│ ├── bisect_time.py
│ ├── dis_iadd_to_item.ods
│ ├── dis_iadd_to_item.txt
│ ├── frenchdeck2.doctest
│ ├── metro_areas.py
│ ├── metro_areas.txt
│ ├── named_slices.py
│ ├── sentence.doctest
│ ├── sentence_slice.doctest
│ ├── sentence_slice.py
│ ├── slice_dump.py
│ ├── slice_test.py
│ ├── slice_viewer.py
│ ├── str_concat.py
│ ├── table.py
│ ├── tuples.doctest
│ └── war-and-peace.txt
└── strings-bytes
│ ├── cafe-gr.txt
│ ├── cafe.txt
│ ├── casefold_demo.py
│ ├── category_demo.py
│ ├── charfinder.py
│ ├── currency_demo.py
│ ├── encodings_demo.py
│ ├── identifier_norm.py
│ ├── identifier_norm_writer.py
│ ├── nfc_demo.py
│ ├── nfk_demo.py
│ ├── numerics.py
│ ├── numerics_demo.txt
│ ├── plane_count.py
│ ├── sorting.py
│ ├── sorting_uca.py
│ ├── sorting_uca.txt
│ ├── str_repr.py
│ ├── str_repr2.py
│ └── strings-bytes-test.txt
├── localfiles.txt
└── update.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | *.sublime-project
2 | *.sublime-workspace
3 | concurrency/flags/img/*.gif
4 | concurrency/charfinder/charfinder_index.pickle
5 | 18-asyncio/charfinder/charfinder_index.pickle
6 | metaprog/oscon-schedule/data/schedule?_db
7 | concurrency/wikipedia/fixture/docroot/
8 | 17-futures/countries/flags/
9 | attic/futures/countries/flags/
10 |
11 | # Byte-compiled / optimized / DLL files
12 | __pycache__/
13 | *.py[cod]
14 |
15 | # C extensions
16 | *.so
17 |
18 | # Java
19 | *.class
20 |
21 | # Distribution / packaging
22 | .Python
23 | env/
24 | build/
25 | develop-eggs/
26 | dist/
27 | downloads/
28 | eggs/
29 | lib/
30 | lib64/
31 | parts/
32 | sdist/
33 | var/
34 | *.egg-info/
35 | .installed.cfg
36 | *.egg
37 |
38 | # PyInstaller
39 | # Usually these files are written by a python script from a template
40 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
41 | *.manifest
42 | *.spec
43 |
44 | # Installer logs
45 | pip-log.txt
46 | pip-delete-this-directory.txt
47 |
48 | # Unit test / coverage reports
49 | htmlcov/
50 | .tox/
51 | .coverage
52 | .cache
53 | nosetests.xml
54 | coverage.xml
55 |
56 | # Translations
57 | *.mo
58 | *.pot
59 |
60 | # Django stuff:
61 | *.log
62 |
63 | # Sphinx documentation
64 | docs/_build/
65 |
66 | # PyBuilder
67 | target/
68 |
69 | # PyCharm
70 | .idea/
71 |
--------------------------------------------------------------------------------
/01-data-model/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 1 - "The Python Data Model"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/01-data-model/frenchdeck.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 | Card = collections.namedtuple('Card', ['rank', 'suit'])
4 |
5 | class FrenchDeck:
6 | ranks = [str(n) for n in range(2, 11)] + list('JQKA')
7 | suits = 'spades diamonds clubs hearts'.split()
8 |
9 | def __init__(self):
10 | self._cards = [Card(rank, suit) for suit in self.suits
11 | for rank in self.ranks]
12 |
13 | def __len__(self):
14 | return len(self._cards)
15 |
16 | def __getitem__(self, position):
17 | return self._cards[position]
18 |
--------------------------------------------------------------------------------
/01-data-model/vector2d.py:
--------------------------------------------------------------------------------
1 | from math import hypot
2 |
3 | class Vector:
4 |
5 | def __init__(self, x=0, y=0):
6 | self.x = x
7 | self.y = y
8 |
9 | def __repr__(self):
10 | return 'Vector(%r, %r)' % (self.x, self.y)
11 |
12 | def __abs__(self):
13 | return hypot(self.x, self.y)
14 |
15 | def __bool__(self):
16 | return bool(abs(self))
17 |
18 | def __add__(self, other):
19 | x = self.x + other.x
20 | y = self.y + other.y
21 | return Vector(x, y)
22 |
23 | def __mul__(self, scalar):
24 | return Vector(self.x * scalar, self.y * scalar)
25 |
--------------------------------------------------------------------------------
/02-array-seq/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 2 - "An array of sequences"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/02-array-seq/bisect_demo.py:
--------------------------------------------------------------------------------
1 | # BEGIN BISECT_DEMO
2 | import bisect
3 | import sys
4 |
5 | HAYSTACK = [1, 4, 5, 6, 8, 12, 15, 20, 21, 23, 23, 26, 29, 30]
6 | NEEDLES = [0, 1, 2, 5, 8, 10, 22, 23, 29, 30, 31]
7 |
8 | ROW_FMT = '{0:2d} @ {1:2d} {2}{0:<2d}'
9 |
10 | def demo(bisect_fn):
11 | for needle in reversed(NEEDLES):
12 | position = bisect_fn(HAYSTACK, needle) # <1>
13 | offset = position * ' |' # <2>
14 | print(ROW_FMT.format(needle, position, offset)) # <3>
15 |
16 | if __name__ == '__main__':
17 |
18 | if sys.argv[-1] == 'left': # <4>
19 | bisect_fn = bisect.bisect_left
20 | else:
21 | bisect_fn = bisect.bisect
22 |
23 | print('DEMO:', bisect_fn.__name__) # <5>
24 | print('haystack ->', ' '.join('%2d' % n for n in HAYSTACK))
25 | demo(bisect_fn)
26 |
27 | # END BISECT_DEMO
28 |
--------------------------------------------------------------------------------
/02-array-seq/bisect_insort.py:
--------------------------------------------------------------------------------
1 | import bisect
2 | import random
3 |
4 | SIZE = 7
5 |
6 | random.seed(1729)
7 |
8 | my_list = []
9 | for i in range(SIZE):
10 | new_item = random.randrange(SIZE*2)
11 | bisect.insort(my_list, new_item)
12 | print('%2d ->' % new_item, my_list)
13 |
--------------------------------------------------------------------------------
/02-array-seq/listcomp_speed.py:
--------------------------------------------------------------------------------
1 | import timeit
2 |
3 | TIMES = 10000
4 |
5 | SETUP = """
6 | symbols = '$¢£¥€¤'
7 | def non_ascii(c):
8 | return c > 127
9 | """
10 |
11 | def clock(label, cmd):
12 | res = timeit.repeat(cmd, setup=SETUP, number=TIMES)
13 | print(label, *('{:.3f}'.format(x) for x in res))
14 |
15 | clock('listcomp :', '[ord(s) for s in symbols if ord(s) > 127]')
16 | clock('listcomp + func :', '[ord(s) for s in symbols if non_ascii(ord(s))]')
17 | clock('filter + lambda :', 'list(filter(lambda c: c > 127, map(ord, symbols)))')
18 | clock('filter + func :', 'list(filter(non_ascii, map(ord, symbols)))')
19 |
--------------------------------------------------------------------------------
/02-array-seq/metro_lat_long.py:
--------------------------------------------------------------------------------
1 | metro_areas = [
2 | ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), # <1>
3 | ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
4 | ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
5 | ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
6 | ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
7 | ]
8 |
9 | print('{:15} | {:^9} | {:^9}'.format('', 'lat.', 'long.'))
10 | fmt = '{:15} | {:9.4f} | {:9.4f}'
11 | for name, cc, pop, (latitude, longitude) in metro_areas: # <2>
12 | if longitude <= 0: # <3>
13 | print(fmt.format(name, latitude, longitude))
14 |
--------------------------------------------------------------------------------
/03-dict-set/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 3 - "Dictionaries and sets"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/03-dict-set/dialcodes.py:
--------------------------------------------------------------------------------
1 | # BEGIN DIALCODES
2 | # dial codes of the top 10 most populous countries
3 | DIAL_CODES = [
4 | (86, 'China'),
5 | (91, 'India'),
6 | (1, 'United States'),
7 | (62, 'Indonesia'),
8 | (55, 'Brazil'),
9 | (92, 'Pakistan'),
10 | (880, 'Bangladesh'),
11 | (234, 'Nigeria'),
12 | (7, 'Russia'),
13 | (81, 'Japan'),
14 | ]
15 |
16 | d1 = dict(DIAL_CODES) # <1>
17 | print('d1:', d1.keys())
18 | d2 = dict(sorted(DIAL_CODES)) # <2>
19 | print('d2:', d2.keys())
20 | d3 = dict(sorted(DIAL_CODES, key=lambda x:x[1])) # <3>
21 | print('d3:', d3.keys())
22 | assert d1 == d2 and d2 == d3 # <4>
23 | # END DIALCODES
24 | """
25 | # BEGIN DIALCODES_OUTPUT
26 | d1: dict_keys([880, 1, 86, 55, 7, 234, 91, 92, 62, 81])
27 | d2: dict_keys([880, 1, 91, 86, 81, 55, 234, 7, 92, 62])
28 | d3: dict_keys([880, 81, 1, 86, 55, 7, 234, 91, 92, 62])
29 | # END DIALCODES_OUTPUT
30 | """
31 |
--------------------------------------------------------------------------------
/03-dict-set/index.py:
--------------------------------------------------------------------------------
1 | # adapted from Alex Martelli's example in "Re-learning Python"
2 | # http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
3 | # (slide 41) Ex: lines-by-word file index
4 |
5 | # BEGIN INDEX
6 | """Build an index mapping word -> list of occurrences"""
7 |
8 | import sys
9 | import re
10 |
11 | WORD_RE = re.compile('\w+')
12 |
13 | index = {}
14 | with open(sys.argv[1], encoding='utf-8') as fp:
15 | for line_no, line in enumerate(fp, 1):
16 | for match in WORD_RE.finditer(line):
17 | word = match.group()
18 | column_no = match.start()+1
19 | location = (line_no, column_no)
20 | index.setdefault(word, []).append(location) # <1>
21 |
22 | # print in alphabetical order
23 | for word in sorted(index, key=str.upper):
24 | print(word, index[word])
25 | # END INDEX
26 |
--------------------------------------------------------------------------------
/03-dict-set/index0.py:
--------------------------------------------------------------------------------
1 | # adapted from Alex Martelli's example in "Re-learning Python"
2 | # http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
3 | # (slide 41) Ex: lines-by-word file index
4 |
5 | # BEGIN INDEX0
6 | """Build an index mapping word -> list of occurrences"""
7 |
8 | import sys
9 | import re
10 |
11 | WORD_RE = re.compile('\w+')
12 |
13 | index = {}
14 | with open(sys.argv[1], encoding='utf-8') as fp:
15 | for line_no, line in enumerate(fp, 1):
16 | for match in WORD_RE.finditer(line):
17 | word = match.group()
18 | column_no = match.start()+1
19 | location = (line_no, column_no)
20 | # this is ugly; coded like this to make a point
21 | occurrences = index.get(word, []) # <1>
22 | occurrences.append(location) # <2>
23 | index[word] = occurrences # <3>
24 |
25 | # print in alphabetical order
26 | for word in sorted(index, key=str.upper): # <4>
27 | print(word, index[word])
28 | # END INDEX0
29 |
--------------------------------------------------------------------------------
/03-dict-set/index_default.py:
--------------------------------------------------------------------------------
1 | # adapted from Alex Martelli's example in "Re-learning Python"
2 | # http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
3 | # (slide 41) Ex: lines-by-word file index
4 |
5 | # BEGIN INDEX_DEFAULT
6 | """Build an index mapping word -> list of occurrences"""
7 |
8 | import sys
9 | import re
10 | import collections
11 |
12 | WORD_RE = re.compile('\w+')
13 |
14 | index = collections.defaultdict(list) # <1>
15 | with open(sys.argv[1], encoding='utf-8') as fp:
16 | for line_no, line in enumerate(fp, 1):
17 | for match in WORD_RE.finditer(line):
18 | word = match.group()
19 | column_no = match.start()+1
20 | location = (line_no, column_no)
21 | index[word].append(location) # <2>
22 |
23 | # print in alphabetical order
24 | for word in sorted(index, key=str.upper):
25 | print(word, index[word])
26 | # END INDEX_DEFAULT
27 |
--------------------------------------------------------------------------------
/03-dict-set/strkeydict0.py:
--------------------------------------------------------------------------------
1 | """StrKeyDict0 converts non-string keys to `str` on lookup
2 |
3 | # BEGIN STRKEYDICT0_TESTS
4 |
5 | Tests for item retrieval using `d[key]` notation::
6 |
7 | >>> d = StrKeyDict0([('2', 'two'), ('4', 'four')])
8 | >>> d['2']
9 | 'two'
10 | >>> d[4]
11 | 'four'
12 | >>> d[1]
13 | Traceback (most recent call last):
14 | ...
15 | KeyError: '1'
16 |
17 | Tests for item retrieval using `d.get(key)` notation::
18 |
19 | >>> d.get('2')
20 | 'two'
21 | >>> d.get(4)
22 | 'four'
23 | >>> d.get(1, 'N/A')
24 | 'N/A'
25 |
26 |
27 | Tests for the `in` operator::
28 |
29 | >>> 2 in d
30 | True
31 | >>> 1 in d
32 | False
33 |
34 | # END STRKEYDICT0_TESTS
35 | """
36 |
37 |
38 | # BEGIN STRKEYDICT0
39 | class StrKeyDict0(dict): # <1>
40 |
41 | def __missing__(self, key):
42 | if isinstance(key, str): # <2>
43 | raise KeyError(key)
44 | return self[str(key)] # <3>
45 |
46 | def get(self, key, default=None):
47 | try:
48 | return self[key] # <4>
49 | except KeyError:
50 | return default # <5>
51 |
52 | def __contains__(self, key):
53 | return key in self.keys() or str(key) in self.keys() # <6>
54 |
55 | # END STRKEYDICT0
56 |
--------------------------------------------------------------------------------
/03-dict-set/support/container_perftest_datagen.py:
--------------------------------------------------------------------------------
1 | """
2 | Generate data for container performance test
3 | """
4 |
5 | import random
6 | import array
7 |
8 | MAX_EXPONENT = 7
9 | HAYSTACK_LEN = 10 ** MAX_EXPONENT
10 | NEEDLES_LEN = 10 ** (MAX_EXPONENT - 1)
11 | SAMPLE_LEN = HAYSTACK_LEN + NEEDLES_LEN // 2
12 |
13 | needles = array.array('d')
14 |
15 | sample = {1/random.random() for i in range(SAMPLE_LEN)}
16 | print('initial sample: %d elements' % len(sample))
17 |
18 | # complete sample, in case duplicate random numbers were discarded
19 | while len(sample) < SAMPLE_LEN:
20 | sample.add(1/random.random())
21 |
22 | print('complete sample: %d elements' % len(sample))
23 |
24 | sample = array.array('d', sample)
25 | random.shuffle(sample)
26 |
27 | not_selected = sample[:NEEDLES_LEN // 2]
28 | print('not selected: %d samples' % len(not_selected))
29 | print(' writing not_selected.arr')
30 | with open('not_selected.arr', 'wb') as fp:
31 | not_selected.tofile(fp)
32 |
33 | selected = sample[NEEDLES_LEN // 2:]
34 | print('selected: %d samples' % len(selected))
35 | print(' writing selected.arr')
36 | with open('selected.arr', 'wb') as fp:
37 | selected.tofile(fp)
38 |
--------------------------------------------------------------------------------
/03-dict-set/support/hashdiff.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | MAX_BITS = len(format(sys.maxsize, 'b'))
4 | print('%s-bit Python build' % (MAX_BITS + 1))
5 |
6 | def hash_diff(o1, o2):
7 | h1 = '{:>0{}b}'.format(hash(o1), MAX_BITS)
8 | h2 = '{:>0{}b}'.format(hash(o2), MAX_BITS)
9 | diff = ''.join('!' if b1 != b2 else ' ' for b1, b2 in zip(h1, h2))
10 | count = '!= {}'.format(diff.count('!'))
11 | width = max(len(repr(o1)), len(repr(o2)), 8)
12 | sep = '-' * (width * 2 + MAX_BITS)
13 | return '{!r:{width}} {}\n{:{width}} {} {}\n{!r:{width}} {}\n{}'.format(
14 | o1, h1, ' ' * width, diff, count, o2, h2, sep, width=width)
15 |
16 | if __name__ == '__main__':
17 | print(hash_diff(1, 1.0))
18 | print(hash_diff(1.0, 1.0001))
19 | print(hash_diff(1.0001, 1.0002))
20 | print(hash_diff(1.0002, 1.0003))
21 |
--------------------------------------------------------------------------------
/04-text-byte/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 4 - "Text and bytes"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/04-text-byte/default_encodings.py:
--------------------------------------------------------------------------------
1 | import sys, locale
2 |
3 | expressions = """
4 | locale.getpreferredencoding()
5 | type(my_file)
6 | my_file.encoding
7 | sys.stdout.isatty()
8 | sys.stdout.encoding
9 | sys.stdin.isatty()
10 | sys.stdin.encoding
11 | sys.stderr.isatty()
12 | sys.stderr.encoding
13 | sys.getdefaultencoding()
14 | sys.getfilesystemencoding()
15 | """
16 |
17 | my_file = open('dummy', 'w')
18 |
19 | for expression in expressions.split():
20 | value = eval(expression)
21 | print(expression.rjust(30), '->', repr(value))
22 |
--------------------------------------------------------------------------------
/04-text-byte/normeq.py:
--------------------------------------------------------------------------------
1 | """
2 | Utility functions for normalized Unicode string comparison.
3 |
4 | Using Normal Form C, case sensitive:
5 |
6 | >>> s1 = 'café'
7 | >>> s2 = 'cafe\u0301'
8 | >>> s1 == s2
9 | False
10 | >>> nfc_equal(s1, s2)
11 | True
12 | >>> nfc_equal('A', 'a')
13 | False
14 |
15 | Using Normal Form C with case folding:
16 |
17 | >>> s3 = 'Straße'
18 | >>> s4 = 'strasse'
19 | >>> s3 == s4
20 | False
21 | >>> nfc_equal(s3, s4)
22 | False
23 | >>> fold_equal(s3, s4)
24 | True
25 | >>> fold_equal(s1, s2)
26 | True
27 | >>> fold_equal('A', 'a')
28 | True
29 |
30 | """
31 |
32 | from unicodedata import normalize
33 |
34 | def nfc_equal(str1, str2):
35 | return normalize('NFC', str1) == normalize('NFC', str2)
36 |
37 | def fold_equal(str1, str2):
38 | return (normalize('NFC', str1).casefold() ==
39 | normalize('NFC', str2).casefold())
40 |
--------------------------------------------------------------------------------
/04-text-byte/numerics_demo.py:
--------------------------------------------------------------------------------
1 | # BEGIN NUMERICS_DEMO
2 | import unicodedata
3 | import re
4 |
5 | re_digit = re.compile(r'\d')
6 |
7 | sample = '1\xbc\xb2\u0969\u136b\u216b\u2466\u2480\u3285'
8 |
9 | for char in sample:
10 | print('U+%04x' % ord(char), # <1>
11 | char.center(6), # <2>
12 | 're_dig' if re_digit.match(char) else '-', # <3>
13 | 'isdig' if char.isdigit() else '-', # <4>
14 | 'isnum' if char.isnumeric() else '-', # <5>
15 | format(unicodedata.numeric(char), '5.2f'), # <6>
16 | unicodedata.name(char), # <7>
17 | sep='\t')
18 | # END NUMERICS_DEMO
19 |
--------------------------------------------------------------------------------
/04-text-byte/ola.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/04-text-byte/ola.py
--------------------------------------------------------------------------------
/04-text-byte/ramanujan.py:
--------------------------------------------------------------------------------
1 | # BEGIN RE_DEMO
2 | import re
3 |
4 | re_numbers_str = re.compile(r'\d+') # <1>
5 | re_words_str = re.compile(r'\w+')
6 | re_numbers_bytes = re.compile(rb'\d+') # <2>
7 | re_words_bytes = re.compile(rb'\w+')
8 |
9 | text_str = ("Ramanujan saw \u0be7\u0bed\u0be8\u0bef" # <3>
10 | " as 1729 = 1³ + 12³ = 9³ + 10³.") # <4>
11 |
12 | text_bytes = text_str.encode('utf_8') # <5>
13 |
14 | print('Text', repr(text_str), sep='\n ')
15 | print('Numbers')
16 | print(' str :', re_numbers_str.findall(text_str)) # <6>
17 | print(' bytes:', re_numbers_bytes.findall(text_bytes)) # <7>
18 | print('Words')
19 | print(' str :', re_words_str.findall(text_str)) # <8>
20 | print(' bytes:', re_words_bytes.findall(text_bytes)) # <9>
21 | # END RE_DEMO
22 |
--------------------------------------------------------------------------------
/05-1class-func/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 5 - "First-class functions"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/05-1class-func/bingocall.py:
--------------------------------------------------------------------------------
1 | """
2 | # BEGIN BINGO_DEMO
3 |
4 | >>> bingo = BingoCage(range(3))
5 | >>> bingo.pick()
6 | 1
7 | >>> bingo()
8 | 0
9 | >>> callable(bingo)
10 | True
11 |
12 | # END BINGO_DEMO
13 |
14 | """
15 |
16 | # BEGIN BINGO
17 |
18 | import random
19 |
20 | class BingoCage:
21 |
22 | def __init__(self, items):
23 | self._items = list(items) # <1>
24 | random.shuffle(self._items) # <2>
25 |
26 | def pick(self): # <3>
27 | try:
28 | return self._items.pop()
29 | except IndexError:
30 | raise LookupError('pick from empty BingoCage') # <4>
31 |
32 | def __call__(self): # <5>
33 | return self.pick()
34 |
35 | # END BINGO
36 |
--------------------------------------------------------------------------------
/05-1class-func/clip.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> clip('banana ', 6)
3 | 'banana'
4 | >>> clip('banana ', 7)
5 | 'banana'
6 | >>> clip('banana ', 5)
7 | 'banana'
8 | >>> clip('banana split', 6)
9 | 'banana'
10 | >>> clip('banana split', 7)
11 | 'banana'
12 | >>> clip('banana split', 10)
13 | 'banana'
14 | >>> clip('banana split', 11)
15 | 'banana'
16 | >>> clip('banana split', 12)
17 | 'banana split'
18 | """
19 |
20 | # BEGIN CLIP
21 |
22 | def clip(text, max_len=80):
23 | """Return text clipped at the last space before or after max_len
24 | """
25 | end = None
26 | if len(text) > max_len:
27 | space_before = text.rfind(' ', 0, max_len)
28 | if space_before >= 0:
29 | end = space_before
30 | else:
31 | space_after = text.rfind(' ', max_len)
32 | if space_after >= 0:
33 | end = space_after
34 | if end is None: # no spaces were found
35 | end = len(text)
36 | return text[:end].rstrip()
37 |
38 | # END CLIP
39 |
--------------------------------------------------------------------------------
/05-1class-func/clip_annot.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> clip('banana ', 6)
3 | 'banana'
4 | >>> clip('banana ', 7)
5 | 'banana'
6 | >>> clip('banana ', 5)
7 | 'banana'
8 | >>> clip('banana split', 6)
9 | 'banana'
10 | >>> clip('banana split', 7)
11 | 'banana'
12 | >>> clip('banana split', 10)
13 | 'banana'
14 | >>> clip('banana split', 11)
15 | 'banana'
16 | >>> clip('banana split', 12)
17 | 'banana split'
18 | """
19 |
20 | # BEGIN CLIP_ANNOT
21 |
22 | def clip(text:str, max_len:'int > 0'=80) -> str: # <1>
23 | """Return text clipped at the last space before or after max_len
24 | """
25 | end = None
26 | if len(text) > max_len:
27 | space_before = text.rfind(' ', 0, max_len)
28 | if space_before >= 0:
29 | end = space_before
30 | else:
31 | space_after = text.rfind(' ', max_len)
32 | if space_after >= 0:
33 | end = space_after
34 | if end is None: # no spaces were found
35 | end = len(text)
36 | return text[:end].rstrip()
37 |
38 | # END CLIP_ANNOT
39 |
--------------------------------------------------------------------------------
/05-1class-func/clip_annot_signature.rst:
--------------------------------------------------------------------------------
1 | >>> from clip_annot import clip
2 | >>> from inspect import signature
3 | >>> sig = signature(clip)
4 | >>> sig.return_annotation
5 |
6 | >>> for param in sig.parameters.values():
7 | ... note = repr(param.annotation).ljust(13)
8 | ... print(note, ':', param.name, '=', param.default)
9 | : text =
10 | 'int > 0' : max_len = 80
11 |
--------------------------------------------------------------------------------
/05-1class-func/clip_introspection.rst:
--------------------------------------------------------------------------------
1 | >>> from clip import clip
2 | >>> clip.__defaults__
3 | (80,)
4 | >>> clip.__code__ # doctest: +ELLIPSIS
5 |
6 | >>> clip.__code__.co_varnames
7 | ('text', 'max_len', 'end', 'space_before', 'space_after')
8 | >>> clip.__code__.co_argcount
9 | 2
10 |
--------------------------------------------------------------------------------
/05-1class-func/clip_signature.rst:
--------------------------------------------------------------------------------
1 | >>> from clip import clip
2 | >>> from inspect import signature
3 | >>> sig = signature(clip)
4 | >>> sig # doctest: +ELLIPSIS
5 |
6 | >>> str(sig)
7 | '(text, max_len=80)'
8 | >>> for name, param in sig.parameters.items():
9 | ... print(param.kind, ':', name, '=', param.default)
10 | ...
11 | POSITIONAL_OR_KEYWORD : text =
12 | POSITIONAL_OR_KEYWORD : max_len = 80
13 |
--------------------------------------------------------------------------------
/05-1class-func/tagger.py:
--------------------------------------------------------------------------------
1 |
2 |
3 | """
4 | # BEGIN TAG_DEMO
5 | >>> tag('br') # <1>
6 | '
'
7 | >>> tag('p', 'hello') # <2>
8 | 'hello
'
9 | >>> print(tag('p', 'hello', 'world'))
10 | hello
11 | world
12 | >>> tag('p', 'hello', id=33) # <3>
13 | 'hello
'
14 | >>> print(tag('p', 'hello', 'world', cls='sidebar')) # <4>
15 |
16 |
17 | >>> tag(content='testing', name="img") # <5>
18 | '
'
19 | >>> my_tag = {'name': 'img', 'title': 'Sunset Boulevard',
20 | ... 'src': 'sunset.jpg', 'cls': 'framed'}
21 | >>> tag(**my_tag) # <6>
22 | '
'
23 |
24 | # END TAG_DEMO
25 | """
26 |
27 |
28 | # BEGIN TAG_FUNC
29 | def tag(name, *content, cls=None, **attrs):
30 | """Generate one or more HTML tags"""
31 | if cls is not None:
32 | attrs['class'] = cls
33 | if attrs:
34 | attr_str = ''.join(' %s="%s"' % (attr, value)
35 | for attr, value
36 | in sorted(attrs.items()))
37 | else:
38 | attr_str = ''
39 | if content:
40 | return '\n'.join('<%s%s>%s%s>' %
41 | (name, attr_str, c, name) for c in content)
42 | else:
43 | return '<%s%s />' % (name, attr_str)
44 | # END TAG_FUNC
45 |
--------------------------------------------------------------------------------
/06-dp-1class-func/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 6 - "Design patterns with first class functions"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/06-dp-1class-func/promotions.py:
--------------------------------------------------------------------------------
1 |
2 | def fidelity_promo(order):
3 | """5% discount for customers with 1000 or more fidelity points"""
4 | return order.total() * .05 if order.customer.fidelity >= 1000 else 0
5 |
6 |
7 | def bulk_item_promo(order):
8 | """10% discount for each LineItem with 20 or more units"""
9 | discount = 0
10 | for item in order.cart:
11 | if item.quantity >= 20:
12 | discount += item.total() * .1
13 | return discount
14 |
15 | def large_order_promo(order):
16 | """7% discount for orders with 10 or more distinct items"""
17 | distinct_items = {item.product for item in order.cart}
18 | if len(distinct_items) >= 10:
19 | return order.total() * .07
20 | return 0
21 |
--------------------------------------------------------------------------------
/07-closure-deco/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 7 - "Closures and decorators"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/07-closure-deco/average.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> avg = make_averager()
3 | >>> avg(10)
4 | 10.0
5 | >>> avg(11)
6 | 10.5
7 | >>> avg(12)
8 | 11.0
9 | >>> avg.__code__.co_varnames
10 | ('new_value', 'total')
11 | >>> avg.__code__.co_freevars
12 | ('series',)
13 | >>> avg.__closure__ # doctest: +ELLIPSIS
14 | (,)
15 | >>> avg.__closure__[0].cell_contents
16 | [10, 11, 12]
17 | """
18 |
19 | DEMO = """
20 | >>> avg.__closure__
21 | (,)
22 | """
23 |
24 |
25 | def make_averager():
26 | series = []
27 |
28 | def averager(new_value):
29 | series.append(new_value)
30 | total = sum(series)
31 | return total/len(series)
32 |
33 | return averager
34 |
--------------------------------------------------------------------------------
/07-closure-deco/average_oo.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> avg = Averager()
3 | >>> avg(10)
4 | 10.0
5 | >>> avg(11)
6 | 10.5
7 | >>> avg(12)
8 | 11.0
9 |
10 | """
11 |
12 |
13 | class Averager():
14 |
15 | def __init__(self):
16 | self.series = []
17 |
18 | def __call__(self, new_value):
19 | self.series.append(new_value)
20 | total = sum(self.series)
21 | return total/len(self.series)
22 |
--------------------------------------------------------------------------------
/07-closure-deco/clockdeco.py:
--------------------------------------------------------------------------------
1 | # clockdeco.py
2 |
3 | import time
4 |
5 |
6 | def clock(func):
7 | def clocked(*args):
8 | t0 = time.time()
9 | result = func(*args)
10 | elapsed = time.time() - t0
11 | name = func.__name__
12 | arg_str = ', '.join(repr(arg) for arg in args)
13 | print('[%0.8fs] %s(%s) -> %r' % (elapsed, name, arg_str, result))
14 | return result
15 | return clocked
16 |
--------------------------------------------------------------------------------
/07-closure-deco/clockdeco_cls.py:
--------------------------------------------------------------------------------
1 | # clockdeco_param.py
2 |
3 | """
4 | >>> snooze(.1) # doctest: +ELLIPSIS
5 | [0.101...s] snooze(0.1) -> None
6 | >>> clock('{name}: {elapsed}')(time.sleep)(.2) # doctest: +ELLIPSIS
7 | sleep: 0.20...
8 | >>> clock('{name}({args}) dt={elapsed:0.3f}s')(time.sleep)(.2)
9 | sleep(0.2) dt=0.201s
10 | """
11 |
12 | # BEGIN CLOCKDECO_CLS
13 | import time
14 |
15 | DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
16 |
17 | class clock:
18 |
19 | def __init__(self, fmt=DEFAULT_FMT):
20 | self.fmt = fmt
21 |
22 | def __call__(self, func):
23 | def clocked(*_args):
24 | t0 = time.time()
25 | _result = func(*_args)
26 | elapsed = time.time() - t0
27 | name = func.__name__
28 | args = ', '.join(repr(arg) for arg in _args)
29 | result = repr(_result)
30 | print(self.fmt.format(**locals()))
31 | return _result
32 | return clocked
33 |
34 | if __name__ == '__main__':
35 |
36 | @clock()
37 | def snooze(seconds):
38 | time.sleep(seconds)
39 |
40 | for i in range(3):
41 | snooze(.123)
42 |
43 | # END CLOCKDECO_CLS
44 |
--------------------------------------------------------------------------------
/07-closure-deco/clockdeco_demo.py:
--------------------------------------------------------------------------------
1 | # clockdeco_demo.py
2 |
3 | import time
4 | from clockdeco import clock
5 |
6 | @clock
7 | def snooze(seconds):
8 | time.sleep(seconds)
9 |
10 | @clock
11 | def factorial(n):
12 | return 1 if n < 2 else n*factorial(n-1)
13 |
14 | if __name__=='__main__':
15 | print('*' * 40, 'Calling snooze(.123)')
16 | snooze(.123)
17 | print('*' * 40, 'Calling factorial(6)')
18 | print('6! =', factorial(6))
19 |
--------------------------------------------------------------------------------
/07-closure-deco/clockdeco_param.py:
--------------------------------------------------------------------------------
1 | # clockdeco_param.py
2 |
3 | """
4 | >>> snooze(.1) # doctest: +ELLIPSIS
5 | [0.101...s] snooze(0.1) -> None
6 | >>> clock('{name}: {elapsed}')(time.sleep)(.2) # doctest: +ELLIPSIS
7 | sleep: 0.20...
8 | >>> clock('{name}({args}) dt={elapsed:0.3f}s')(time.sleep)(.2)
9 | sleep(0.2) dt=0.201s
10 | """
11 |
12 | # BEGIN CLOCKDECO_PARAM
13 | import time
14 |
15 | DEFAULT_FMT = '[{elapsed:0.8f}s] {name}({args}) -> {result}'
16 |
17 | def clock(fmt=DEFAULT_FMT): # <1>
18 | def decorate(func): # <2>
19 | def clocked(*_args): # <3>
20 | t0 = time.time()
21 | _result = func(*_args) # <4>
22 | elapsed = time.time() - t0
23 | name = func.__name__
24 | args = ', '.join(repr(arg) for arg in _args) # <5>
25 | result = repr(_result) # <6>
26 | print(fmt.format(**locals())) # <7>
27 | return _result # <8>
28 | return clocked # <9>
29 | return decorate # <10>
30 |
31 | if __name__ == '__main__':
32 |
33 | @clock() # <11>
34 | def snooze(seconds):
35 | time.sleep(seconds)
36 |
37 | for i in range(3):
38 | snooze(.123)
39 |
40 | # END CLOCKDECO_PARAM
41 |
--------------------------------------------------------------------------------
/07-closure-deco/clockdeco_param_demo1.py:
--------------------------------------------------------------------------------
1 | import time
2 | from clockdeco_param import clock
3 |
4 | @clock('{name}: {elapsed}s')
5 | def snooze(seconds):
6 | time.sleep(seconds)
7 |
8 | for i in range(3):
9 | snooze(.123)
10 |
--------------------------------------------------------------------------------
/07-closure-deco/clockdeco_param_demo2.py:
--------------------------------------------------------------------------------
1 | import time
2 | from clockdeco_param import clock
3 |
4 | @clock('{name}({args}) dt={elapsed:0.3f}s')
5 | def snooze(seconds):
6 | time.sleep(seconds)
7 |
8 | for i in range(3):
9 | snooze(.123)
10 |
--------------------------------------------------------------------------------
/07-closure-deco/fibo_demo.py:
--------------------------------------------------------------------------------
1 | from clockdeco import clock
2 |
3 | @clock
4 | def fibonacci(n):
5 | if n < 2:
6 | return n
7 | return fibonacci(n-2) + fibonacci(n-1)
8 |
9 | if __name__=='__main__':
10 | print(fibonacci(6))
11 |
--------------------------------------------------------------------------------
/07-closure-deco/fibo_demo_lru.py:
--------------------------------------------------------------------------------
1 | import functools
2 |
3 | from clockdeco import clock
4 |
5 | @functools.lru_cache() # <1>
6 | @clock # <2>
7 | def fibonacci(n):
8 | if n < 2:
9 | return n
10 | return fibonacci(n-2) + fibonacci(n-1)
11 |
12 | if __name__=='__main__':
13 | print(fibonacci(6))
14 |
--------------------------------------------------------------------------------
/07-closure-deco/generic.py:
--------------------------------------------------------------------------------
1 | r"""
2 | htmlize(): generic function example
3 |
4 | # BEGIN HTMLIZE_DEMO
5 |
6 | >>> htmlize({1, 2, 3}) # <1>
7 | '{1, 2, 3} '
8 | >>> htmlize(abs)
9 | '<built-in function abs> '
10 | >>> htmlize('Heimlich & Co.\n- a game') # <2>
11 | 'Heimlich & Co. \n- a game '
12 | >>> htmlize(42) # <3>
13 | '42 (0x2a) '
14 | >>> print(htmlize(['alpha', 66, {3, 2, 1}])) # <4>
15 |
16 | alpha
17 | 66 (0x42)
18 | {1, 2, 3}
19 |
20 |
21 | # END HTMLIZE_DEMO
22 | """
23 |
24 | # BEGIN HTMLIZE
25 |
26 | from functools import singledispatch
27 | from collections import abc
28 | import numbers
29 | import html
30 |
31 | @singledispatch # <1>
32 | def htmlize(obj):
33 | content = html.escape(repr(obj))
34 | return '{} '.format(content)
35 |
36 | @htmlize.register(str) # <2>
37 | def _(text): # <3>
38 | content = html.escape(text).replace('\n', ' \n')
39 | return '{0} '.format(content)
40 |
41 | @htmlize.register(numbers.Integral) # <4>
42 | def _(n):
43 | return '{0} (0x{0:x}) '.format(n)
44 |
45 | @htmlize.register(tuple) # <5>
46 | @htmlize.register(abc.MutableSequence)
47 | def _(seq):
48 | inner = '\n'.join(htmlize(item) for item in seq)
49 | return ''
50 |
51 | # END HTMLIZE
52 |
53 |
--------------------------------------------------------------------------------
/07-closure-deco/registration.py:
--------------------------------------------------------------------------------
1 | # BEGIN REGISTRATION
2 |
3 | registry = [] # <1>
4 |
5 | def register(func): # <2>
6 | print('running register(%s)' % func) # <3>
7 | registry.append(func) # <4>
8 | return func # <5>
9 |
10 | @register # <6>
11 | def f1():
12 | print('running f1()')
13 |
14 | @register
15 | def f2():
16 | print('running f2()')
17 |
18 | def f3(): # <7>
19 | print('running f3()')
20 |
21 | def main(): # <8>
22 | print('running main()')
23 | print('registry ->', registry)
24 | f1()
25 | f2()
26 | f3()
27 |
28 | if __name__=='__main__':
29 | main() # <9>
30 |
31 | # END REGISTRATION
--------------------------------------------------------------------------------
/07-closure-deco/registration_abridged.py:
--------------------------------------------------------------------------------
1 | # BEGIN REGISTRATION_ABRIDGED
2 | registry = []
3 |
4 | def register(func):
5 | print('running register(%s)' % func)
6 | registry.append(func)
7 | return func
8 |
9 | @register
10 | def f1():
11 | print('running f1()')
12 |
13 | print('running main()')
14 | print('registry ->', registry)
15 | f1()
16 | # END REGISTRATION_ABRIDGED
17 |
--------------------------------------------------------------------------------
/07-closure-deco/registration_param.py:
--------------------------------------------------------------------------------
1 | # BEGIN REGISTRATION_PARAM
2 |
3 | registry = set() # <1>
4 |
5 | def register(active=True): # <2>
6 | def decorate(func): # <3>
7 | print('running register(active=%s)->decorate(%s)'
8 | % (active, func))
9 | if active: # <4>
10 | registry.add(func)
11 | else:
12 | registry.discard(func) # <5>
13 |
14 | return func # <6>
15 | return decorate # <7>
16 |
17 | @register(active=False) # <8>
18 | def f1():
19 | print('running f1()')
20 |
21 | @register() # <9>
22 | def f2():
23 | print('running f2()')
24 |
25 | def f3():
26 | print('running f3()')
27 |
28 | # END REGISTRATION_PARAM
29 |
--------------------------------------------------------------------------------
/08-obj-ref/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 8 - "Object references, mutability and recycling"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/08-obj-ref/bus.py:
--------------------------------------------------------------------------------
1 |
2 | """
3 | >>> import copy
4 | >>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David'])
5 | >>> bus2 = copy.copy(bus1)
6 | >>> bus3 = copy.deepcopy(bus1)
7 | >>> bus1.drop('Bill')
8 | >>> bus2.passengers
9 | ['Alice', 'Claire', 'David']
10 | >>> bus3.passengers
11 | ['Alice', 'Bill', 'Claire', 'David']
12 |
13 | """
14 |
15 | # BEGIN BUS_CLASS
16 | class Bus:
17 |
18 | def __init__(self, passengers=None):
19 | if passengers is None:
20 | self.passengers = []
21 | else:
22 | self.passengers = list(passengers)
23 |
24 | def pick(self, name):
25 | self.passengers.append(name)
26 |
27 | def drop(self, name):
28 | self.passengers.remove(name)
29 | # END BUS_CLASS
30 |
--------------------------------------------------------------------------------
/08-obj-ref/cheese.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> import weakref
3 | >>> stock = weakref.WeakValueDictionary()
4 | >>> catalog = [Cheese('Red Leicester'), Cheese('Tilsit'),
5 | ... Cheese('Brie'), Cheese('Parmesan')]
6 | ...
7 | >>> for cheese in catalog:
8 | ... stock[cheese.kind] = cheese
9 | ...
10 | >>> sorted(stock.keys())
11 | ['Brie', 'Parmesan', 'Red Leicester', 'Tilsit']
12 | >>> del catalog
13 | >>> sorted(stock.keys())
14 | ['Parmesan']
15 | >>> del cheese
16 | >>> sorted(stock.keys())
17 | []
18 | """
19 |
20 | # BEGIN CHEESE_CLASS
21 | class Cheese:
22 |
23 | def __init__(self, kind):
24 | self.kind = kind
25 |
26 | def __repr__(self):
27 | return 'Cheese(%r)' % self.kind
28 | # END CHEESE_CLASS
29 |
--------------------------------------------------------------------------------
/08-obj-ref/haunted_bus.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> bus1 = HauntedBus(['Alice', 'Bill'])
3 | >>> bus1.passengers
4 | ['Alice', 'Bill']
5 | >>> bus1.pick('Charlie')
6 | >>> bus1.drop('Alice')
7 | >>> bus1.passengers
8 | ['Bill', 'Charlie']
9 | >>> bus2 = HauntedBus()
10 | >>> bus2.pick('Carrie')
11 | >>> bus2.passengers
12 | ['Carrie']
13 | >>> bus3 = HauntedBus()
14 | >>> bus3.passengers
15 | ['Carrie']
16 | >>> bus3.pick('Dave')
17 | >>> bus2.passengers
18 | ['Carrie', 'Dave']
19 | >>> bus2.passengers is bus3.passengers
20 | True
21 | >>> bus1.passengers
22 | ['Bill', 'Charlie']
23 |
24 |
25 | >>> dir(HauntedBus.__init__) # doctest: +ELLIPSIS
26 | ['__annotations__', '__call__', ..., '__defaults__', ...]
27 | >>> HauntedBus.__init__.__defaults__
28 | (['Carrie', 'Dave'],)
29 | >>> HauntedBus.__init__.__defaults__[0] is bus2.passengers
30 | True
31 |
32 | """
33 |
34 | # BEGIN HAUNTED_BUS_CLASS
35 | class HauntedBus:
36 | """A bus model haunted by ghost passengers"""
37 |
38 | def __init__(self, passengers=[]): # <1>
39 | self.passengers = passengers # <2>
40 |
41 | def pick(self, name):
42 | self.passengers.append(name) # <3>
43 |
44 | def drop(self, name):
45 | self.passengers.remove(name)
46 | # END HAUNTED_BUS_CLASS
47 |
48 |
--------------------------------------------------------------------------------
/08-obj-ref/twilight_bus.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> basketball_team = ['Sue', 'Tina', 'Maya', 'Diana', 'Pat']
3 | >>> bus = TwilightBus(basketball_team)
4 | >>> bus.drop('Tina')
5 | >>> bus.drop('Pat')
6 | >>> basketball_team
7 | ['Sue', 'Maya', 'Diana']
8 | """
9 |
10 | # BEGIN TWILIGHT_BUS_CLASS
11 | class TwilightBus:
12 | """A bus model that makes passengers vanish"""
13 |
14 | def __init__(self, passengers=None):
15 | if passengers is None:
16 | self.passengers = [] # <1>
17 | else:
18 | self.passengers = passengers #<2>
19 |
20 | def pick(self, name):
21 | self.passengers.append(name)
22 |
23 | def drop(self, name):
24 | self.passengers.remove(name) # <3>
25 | # END TWILIGHT_BUS_CLASS
26 |
27 |
--------------------------------------------------------------------------------
/09-pythonic-obj/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 9 - "Pythonic objects"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/09-pythonic-obj/mem_test.py:
--------------------------------------------------------------------------------
1 | import importlib
2 | import sys
3 | import resource
4 |
5 | NUM_VECTORS = 10**7
6 |
7 | if len(sys.argv) == 2:
8 | module_name = sys.argv[1].replace('.py', '')
9 | module = importlib.import_module(module_name)
10 | else:
11 | print('Usage: {} '.format())
12 | sys.exit(1)
13 |
14 | fmt = 'Selected Vector2d type: {.__name__}.{.__name__}'
15 | print(fmt.format(module, module.Vector2d))
16 |
17 | mem_init = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
18 | print('Creating {:,} Vector2d instances'.format(NUM_VECTORS))
19 |
20 | vectors = [module.Vector2d(3.0, 4.0) for i in range(NUM_VECTORS)]
21 |
22 | mem_final = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss
23 | print('Initial RAM usage: {:14,}'.format(mem_init))
24 | print(' Final RAM usage: {:14,}'.format(mem_final))
25 |
--------------------------------------------------------------------------------
/09-pythonic-obj/private/Confidential.java:
--------------------------------------------------------------------------------
1 | public class Confidential {
2 |
3 | private String secret = "";
4 |
5 | public Confidential(String text) {
6 | secret = text.toUpperCase();
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/09-pythonic-obj/private/Expose.java:
--------------------------------------------------------------------------------
1 | import java.lang.reflect.Field;
2 |
3 | public class Expose {
4 |
5 | public static void main(String[] args) {
6 | Confidential message = new Confidential("top secret text");
7 | Field secretField = null;
8 | try {
9 | secretField = Confidential.class.getDeclaredField("secret");
10 | }
11 | catch (NoSuchFieldException e) {
12 | System.err.println(e);
13 | System.exit(1);
14 | }
15 | secretField.setAccessible(true); // break the lock!
16 | try {
17 | String wasHidden = (String) secretField.get(message);
18 | System.out.println("message.secret = " + wasHidden);
19 | }
20 | catch (IllegalAccessException e) {
21 | // this will not happen after setAcessible(true)
22 | System.err.println(e);
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/09-pythonic-obj/private/expose.py:
--------------------------------------------------------------------------------
1 | import Confidential
2 |
3 | message = Confidential('top secret text')
4 | secret_field = Confidential.getDeclaredField('secret')
5 | secret_field.setAccessible(True) # break the lock!
6 | print 'message.secret =', secret_field.get(message)
7 |
--------------------------------------------------------------------------------
/09-pythonic-obj/private/leakprivate.py:
--------------------------------------------------------------------------------
1 | from java.lang.reflect import Modifier
2 | import Confidential
3 |
4 | message = Confidential('top secret text')
5 | fields = Confidential.getDeclaredFields()
6 | for field in fields:
7 | # list private fields only
8 | if Modifier.isPrivate(field.getModifiers()):
9 | field.setAccessible(True) # break the lock
10 | print 'field:', field
11 | print '\t', field.getName(), '=', field.get(message)
12 |
--------------------------------------------------------------------------------
/09-pythonic-obj/private/no_respect.py:
--------------------------------------------------------------------------------
1 |
2 | """
3 | In the Jython registry file there is this line:
4 |
5 | python.security.respectJavaAccessibility = true
6 |
7 | Set this to false and Jython provides access to non-public
8 | fields, methods, and constructors of Java objects.
9 | """
10 |
11 | import Confidential
12 |
13 | message = Confidential('top secret text')
14 | for name in dir(message):
15 | attr = getattr(message, name)
16 | if not callable(attr): # non-methods only
17 | print name + '\t=', attr
18 |
--------------------------------------------------------------------------------
/10-seq-hacking/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 10 - "Sequence hacking, hashing and slicing"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/11-iface-abc/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 11 - "Interfaces, protocols and ABCs"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/11-iface-abc/bingo.py:
--------------------------------------------------------------------------------
1 | # BEGIN TOMBOLA_BINGO
2 |
3 | import random
4 |
5 | from tombola import Tombola
6 |
7 |
8 | class BingoCage(Tombola): # <1>
9 |
10 | def __init__(self, items):
11 | self._randomizer = random.SystemRandom() # <2>
12 | self._items = []
13 | self.load(items) # <3>
14 |
15 | def load(self, items):
16 | self._items.extend(items)
17 | self._randomizer.shuffle(self._items) # <4>
18 |
19 | def pick(self): # <5>
20 | try:
21 | return self._items.pop()
22 | except IndexError:
23 | raise LookupError('pick from empty BingoCage')
24 |
25 | def __call__(self): # <7>
26 | self.pick()
27 |
28 | # END TOMBOLA_BINGO
29 |
--------------------------------------------------------------------------------
/11-iface-abc/drum.py:
--------------------------------------------------------------------------------
1 | from random import shuffle
2 |
3 | from tombola import Tombola
4 |
5 |
6 | class TumblingDrum(Tombola):
7 |
8 | def __init__(self, iterable):
9 | self._balls = []
10 | self.load(iterable)
11 |
12 | def load(self, iterable):
13 | self._balls.extend(iterable)
14 | shuffle(self._balls)
15 |
16 | def pick(self):
17 | return self._balls.pop()
18 |
--------------------------------------------------------------------------------
/11-iface-abc/frenchdeck2.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 | Card = collections.namedtuple('Card', ['rank', 'suit'])
4 |
5 | class FrenchDeck2(collections.MutableSequence):
6 | ranks = [str(n) for n in range(2, 11)] + list('JQKA')
7 | suits = 'spades diamonds clubs hearts'.split()
8 |
9 | def __init__(self):
10 | self._cards = [Card(rank, suit) for suit in self.suits
11 | for rank in self.ranks]
12 |
13 | def __len__(self):
14 | return len(self._cards)
15 |
16 | def __getitem__(self, position):
17 | return self._cards[position]
18 |
19 | def __setitem__(self, position, value): # <1>
20 | self._cards[position] = value
21 |
22 | def __delitem__(self, position): # <2>
23 | del self._cards[position]
24 |
25 | def insert(self, position, value): # <3>
26 | self._cards.insert(position, value)
27 |
--------------------------------------------------------------------------------
/11-iface-abc/lotto.py:
--------------------------------------------------------------------------------
1 | # BEGIN LOTTERY_BLOWER
2 |
3 | import random
4 |
5 | from tombola import Tombola
6 |
7 |
8 | class LotteryBlower(Tombola):
9 |
10 | def __init__(self, iterable):
11 | self._balls = list(iterable) # <1>
12 |
13 | def load(self, iterable):
14 | self._balls.extend(iterable)
15 |
16 | def pick(self):
17 | try:
18 | position = random.randrange(len(self._balls)) # <2>
19 | except ValueError:
20 | raise LookupError('pick from empty BingoCage')
21 | return self._balls.pop(position) # <3>
22 |
23 | def loaded(self): # <4>
24 | return bool(self._balls)
25 |
26 | def inspect(self): # <5>
27 | return tuple(sorted(self._balls))
28 |
29 |
30 | # END LOTTERY_BLOWER
31 |
--------------------------------------------------------------------------------
/11-iface-abc/tombola.py:
--------------------------------------------------------------------------------
1 | # BEGIN TOMBOLA_ABC
2 |
3 | import abc
4 |
5 | class Tombola(abc.ABC): # <1>
6 |
7 | @abc.abstractmethod
8 | def load(self, iterable): # <2>
9 | """Add items from an iterable."""
10 |
11 | @abc.abstractmethod
12 | def pick(self): # <3>
13 | """Remove item at random, returning it.
14 |
15 | This method should raise `LookupError` when the instance is empty.
16 | """
17 |
18 | def loaded(self): # <4>
19 | """Return `True` if there's at least 1 item, `False` otherwise."""
20 | return bool(self.inspect()) # <5>
21 |
22 |
23 | def inspect(self):
24 | """Return a sorted tuple with the items currently inside."""
25 | items = []
26 | while True: # <6>
27 | try:
28 | items.append(self.pick())
29 | except LookupError:
30 | break
31 | self.load(items) # <7>
32 | return tuple(sorted(items))
33 |
34 |
35 | # END TOMBOLA_ABC
36 |
--------------------------------------------------------------------------------
/11-iface-abc/tombola_runner.py:
--------------------------------------------------------------------------------
1 | # BEGIN TOMBOLA_RUNNER
2 | import doctest
3 |
4 | from tombola import Tombola
5 |
6 | # modules to test
7 | import bingo, lotto, tombolist, drum # <1>
8 |
9 | TEST_FILE = 'tombola_tests.rst'
10 | TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
11 |
12 |
13 | def main(argv):
14 | verbose = '-v' in argv
15 | real_subclasses = Tombola.__subclasses__() # <2>
16 | virtual_subclasses = list(Tombola._abc_registry) # <3>
17 |
18 | for cls in real_subclasses + virtual_subclasses: # <4>
19 | test(cls, verbose)
20 |
21 |
22 | def test(cls, verbose=False):
23 |
24 | res = doctest.testfile(
25 | TEST_FILE,
26 | globs={'ConcreteTombola': cls}, # <5>
27 | verbose=verbose,
28 | optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
29 | tag = 'FAIL' if res.failed else 'OK'
30 | print(TEST_MSG.format(cls.__name__, res, tag)) # <6>
31 |
32 |
33 | if __name__ == '__main__':
34 | import sys
35 | main(sys.argv)
36 | # END TOMBOLA_RUNNER
37 |
--------------------------------------------------------------------------------
/11-iface-abc/tombolist.py:
--------------------------------------------------------------------------------
1 | from random import randrange
2 |
3 | from tombola import Tombola
4 |
5 | @Tombola.register # <1>
6 | class TomboList(list): # <2>
7 |
8 | def pick(self):
9 | if self: # <3>
10 | position = randrange(len(self))
11 | return self.pop(position) # <4>
12 | else:
13 | raise LookupError('pop from empty TomboList')
14 |
15 | load = list.extend # <5>
16 |
17 | def loaded(self):
18 | return bool(self) # <6>
19 |
20 | def inspect(self):
21 | return tuple(sorted(self))
22 |
23 | # Tombola.register(TomboList) # <7>
24 |
--------------------------------------------------------------------------------
/12-inheritance/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 12 - "Inheritance: for good or for worse"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/12-inheritance/diamond.py:
--------------------------------------------------------------------------------
1 | class A:
2 | def ping(self):
3 | print('ping:', self)
4 |
5 |
6 | class B(A):
7 | def pong(self):
8 | print('pong:', self)
9 |
10 |
11 | class C(A):
12 | def pong(self):
13 | print('PONG:', self)
14 |
15 |
16 | class D(B, C):
17 |
18 | def ping(self):
19 | super().ping()
20 | print('post-ping:', self)
21 |
22 | def pingpong(self):
23 | self.ping()
24 | super().ping()
25 | self.pong()
26 | super().pong()
27 | C.pong(self)
28 |
--------------------------------------------------------------------------------
/13-op-overloading/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 13 - "Operator overloading: doing it right"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/13-op-overloading/bingo.py:
--------------------------------------------------------------------------------
1 | # BEGIN TOMBOLA_BINGO
2 |
3 | import random
4 |
5 | from tombola import Tombola
6 |
7 |
8 | class BingoCage(Tombola): # <1>
9 |
10 | def __init__(self, items):
11 | self._randomizer = random.SystemRandom() # <2>
12 | self._items = []
13 | self.load(items) # <3>
14 |
15 | def load(self, items):
16 | self._items.extend(items)
17 | self._randomizer.shuffle(self._items) # <4>
18 |
19 | def pick(self): # <5>
20 | try:
21 | return self._items.pop()
22 | except IndexError:
23 | raise LookupError('pick from empty BingoCage')
24 |
25 | def __call__(self): # <7>
26 | self.pick()
27 |
28 | # END TOMBOLA_BINGO
29 |
--------------------------------------------------------------------------------
/13-op-overloading/tombola.py:
--------------------------------------------------------------------------------
1 | # BEGIN TOMBOLA_ABC
2 |
3 | import abc
4 |
5 | class Tombola(abc.ABC): # <1>
6 |
7 | @abc.abstractmethod
8 | def load(self, iterable): # <2>
9 | """Add items from an iterable."""
10 |
11 | @abc.abstractmethod
12 | def pick(self): # <3>
13 | """Remove item at random, returning it.
14 |
15 | This method should raise `LookupError` when the instance is empty.
16 | """
17 |
18 | def loaded(self): # <4>
19 | """Return `True` if there's at least 1 item, `False` otherwise."""
20 | return bool(self.inspect()) # <5>
21 |
22 |
23 | def inspect(self):
24 | """Return a sorted tuple with the items currently inside."""
25 | items = []
26 | while True: # <6>
27 | try:
28 | items.append(self.pick())
29 | except LookupError:
30 | break
31 | self.load(items) # <7>
32 | return tuple(sorted(items))
33 |
34 |
35 | # END TOMBOLA_ABC
36 |
--------------------------------------------------------------------------------
/13-op-overloading/unary_plus_decimal.py:
--------------------------------------------------------------------------------
1 | """
2 | # BEGIN UNARY_PLUS_DECIMAL
3 |
4 | >>> import decimal
5 | >>> ctx = decimal.getcontext() # <1>
6 | >>> ctx.prec = 40 # <2>
7 | >>> one_third = decimal.Decimal('1') / decimal.Decimal('3') # <3>
8 | >>> one_third # <4>
9 | Decimal('0.3333333333333333333333333333333333333333')
10 | >>> one_third == +one_third # <5>
11 | True
12 | >>> ctx.prec = 28 # <6>
13 | >>> one_third == +one_third # <7>
14 | False
15 | >>> +one_third # <8>
16 | Decimal('0.3333333333333333333333333333')
17 |
18 | # END UNARY_PLUS_DECIMAL
19 |
20 | """
21 |
22 | import decimal
23 |
24 | if __name__ == '__main__':
25 |
26 | with decimal.localcontext() as ctx:
27 | ctx.prec = 40
28 | print('precision:', ctx.prec)
29 | one_third = decimal.Decimal('1') / decimal.Decimal('3')
30 | print(' one_third:', one_third)
31 | print(' +one_third:', +one_third)
32 |
33 | print('precision:', decimal.getcontext().prec)
34 | print(' one_third:', one_third)
35 | print(' +one_third:', +one_third)
36 |
--------------------------------------------------------------------------------
/14-it-generator/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 14 - "Iterables, iterators and generators"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/14-it-generator/aritprog.rst:
--------------------------------------------------------------------------------
1 | ===========================================
2 | Tests for arithmetic progression generators
3 | ===========================================
4 |
5 | Tests with built-in numeric types::
6 |
7 | >>> ap = aritprog_gen(1, .5, 3)
8 | >>> list(ap)
9 | [1.0, 1.5, 2.0, 2.5]
10 | >>> ap = aritprog_gen(0, 1/3, 1)
11 | >>> list(ap)
12 | [0.0, 0.3333333333333333, 0.6666666666666666]
13 |
14 |
15 | Tests with standard library numeric types::
16 |
17 | >>> from fractions import Fraction
18 | >>> ap = aritprog_gen(0, Fraction(1, 3), 1)
19 | >>> list(ap)
20 | [Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
21 | >>> from decimal import Decimal
22 | >>> ap = aritprog_gen(0, Decimal('.1'), .3)
23 | >>> list(ap)
24 | [Decimal('0'), Decimal('0.1'), Decimal('0.2')]
25 |
26 |
27 | Test producing an empty series::
28 |
29 | >>> ap = aritprog_gen(0, 1, 0)
30 | >>> list(ap)
31 | []
32 |
--------------------------------------------------------------------------------
/14-it-generator/aritprog_float_error.py:
--------------------------------------------------------------------------------
1 | """
2 | Demonstrate difference between Arithmetic Progression calculated
3 | as a series of increments accumulating errors versus one addition
4 | and one multiplication.
5 | """
6 |
7 | from fractions import Fraction
8 | from aritprog_v0 import ArithmeticProgression as APv0
9 | from aritprog_v1 import ArithmeticProgression as APv1
10 |
11 | if __name__ == '__main__':
12 |
13 | ap0 = iter(APv0(1, .1))
14 | ap1 = iter(APv1(1, .1))
15 | ap_frac = iter(APv1(Fraction(1, 1), Fraction(1, 10)))
16 | epsilon = 10**-10
17 | iteration = 0
18 | delta = next(ap0) - next(ap1)
19 | frac = next(ap_frac)
20 | while abs(delta) <= epsilon:
21 | delta = next(ap0) - next(ap1)
22 | frac = next(ap_frac)
23 | iteration +=1
24 |
25 | print('iteration: {}\tfraction: {}\tepsilon: {}\tdelta: {}'.
26 | format(iteration, frac, epsilon, delta))
27 |
--------------------------------------------------------------------------------
/14-it-generator/aritprog_runner.py:
--------------------------------------------------------------------------------
1 | import doctest
2 | import importlib
3 | import glob
4 |
5 |
6 | TARGET_GLOB = 'aritprog*.py'
7 | TEST_FILE = 'aritprog.rst'
8 | TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
9 |
10 |
11 | def main(argv):
12 | verbose = '-v' in argv
13 | for module_file_name in sorted(glob.glob(TARGET_GLOB)):
14 | module_name = module_file_name.replace('.py', '')
15 | module = importlib.import_module(module_name)
16 | gen_factory = getattr(module, 'ArithmeticProgression', None)
17 | if gen_factory is None:
18 | gen_factory = getattr(module, 'aritprog_gen', None)
19 | if gen_factory is None:
20 | continue
21 |
22 | test(gen_factory, verbose)
23 |
24 |
25 | def test(gen_factory, verbose=False):
26 | res = doctest.testfile(
27 | TEST_FILE,
28 | globs={'aritprog_gen': gen_factory},
29 | verbose=verbose,
30 | optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
31 | tag = 'FAIL' if res.failed else 'OK'
32 | print(TEST_MSG.format(gen_factory.__module__, res, tag))
33 |
34 |
35 | if __name__ == '__main__':
36 | import sys
37 | main(sys.argv)
38 |
--------------------------------------------------------------------------------
/14-it-generator/aritprog_v0.py:
--------------------------------------------------------------------------------
1 | """
2 | Arithmetic progression class
3 |
4 | >>> ap = ArithmeticProgression(1, .5, 3)
5 | >>> list(ap)
6 | [1.0, 1.5, 2.0, 2.5]
7 |
8 |
9 | """
10 |
11 |
12 | class ArithmeticProgression:
13 |
14 | def __init__(self, begin, step, end=None):
15 | self.begin = begin
16 | self.step = step
17 | self.end = end # None -> "infinite" series
18 |
19 | def __iter__(self):
20 | result = type(self.begin + self.step)(self.begin)
21 | forever = self.end is None
22 | while forever or result < self.end:
23 | yield result
24 | result += self.step
25 |
--------------------------------------------------------------------------------
/14-it-generator/aritprog_v1.py:
--------------------------------------------------------------------------------
1 | """
2 | Arithmetic progression class
3 |
4 | # BEGIN ARITPROG_CLASS_DEMO
5 |
6 | >>> ap = ArithmeticProgression(0, 1, 3)
7 | >>> list(ap)
8 | [0, 1, 2]
9 | >>> ap = ArithmeticProgression(1, .5, 3)
10 | >>> list(ap)
11 | [1.0, 1.5, 2.0, 2.5]
12 | >>> ap = ArithmeticProgression(0, 1/3, 1)
13 | >>> list(ap)
14 | [0.0, 0.3333333333333333, 0.6666666666666666]
15 | >>> from fractions import Fraction
16 | >>> ap = ArithmeticProgression(0, Fraction(1, 3), 1)
17 | >>> list(ap)
18 | [Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
19 | >>> from decimal import Decimal
20 | >>> ap = ArithmeticProgression(0, Decimal('.1'), .3)
21 | >>> list(ap)
22 | [Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
23 |
24 | # END ARITPROG_CLASS_DEMO
25 | """
26 |
27 |
28 | # BEGIN ARITPROG_CLASS
29 | class ArithmeticProgression:
30 |
31 | def __init__(self, begin, step, end=None): # <1>
32 | self.begin = begin
33 | self.step = step
34 | self.end = end # None -> "infinite" series
35 |
36 | def __iter__(self):
37 | result = type(self.begin + self.step)(self.begin) # <2>
38 | forever = self.end is None # <3>
39 | index = 0
40 | while forever or result < self.end: # <4>
41 | yield result # <5>
42 | index += 1
43 | result = self.begin + self.step * index # <6>
44 | # END ARITPROG_CLASS
45 |
--------------------------------------------------------------------------------
/14-it-generator/aritprog_v2.py:
--------------------------------------------------------------------------------
1 | """
2 | Arithmetic progression generator function::
3 |
4 | >>> ap = aritprog_gen(1, .5, 3)
5 | >>> list(ap)
6 | [1.0, 1.5, 2.0, 2.5]
7 | >>> ap = aritprog_gen(0, 1/3, 1)
8 | >>> list(ap)
9 | [0.0, 0.3333333333333333, 0.6666666666666666]
10 | >>> from fractions import Fraction
11 | >>> ap = aritprog_gen(0, Fraction(1, 3), 1)
12 | >>> list(ap)
13 | [Fraction(0, 1), Fraction(1, 3), Fraction(2, 3)]
14 | >>> from decimal import Decimal
15 | >>> ap = aritprog_gen(0, Decimal('.1'), .3)
16 | >>> list(ap)
17 | [Decimal('0.0'), Decimal('0.1'), Decimal('0.2')]
18 |
19 | """
20 |
21 |
22 | # BEGIN ARITPROG_GENFUNC
23 | def aritprog_gen(begin, step, end=None):
24 | result = type(begin + step)(begin)
25 | forever = end is None
26 | index = 0
27 | while forever or result < end:
28 | yield result
29 | index += 1
30 | result = begin + step * index
31 | # END ARITPROG_GENFUNC
32 |
--------------------------------------------------------------------------------
/14-it-generator/aritprog_v3.py:
--------------------------------------------------------------------------------
1 | # BEGIN ARITPROG_ITERTOOLS
2 | import itertools
3 |
4 |
5 | def aritprog_gen(begin, step, end=None):
6 | first = type(begin + step)(begin)
7 | ap_gen = itertools.count(first, step)
8 | if end is not None:
9 | ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
10 | return ap_gen
11 | # END ARITPROG_ITERTOOLS
12 |
--------------------------------------------------------------------------------
/14-it-generator/fibo_by_hand.py:
--------------------------------------------------------------------------------
1 | """
2 | Fibonacci generator implemented "by hand" without generator objects
3 |
4 | >>> from itertools import islice
5 | >>> list(islice(Fibonacci(), 15))
6 | [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377]
7 |
8 | """
9 |
10 |
11 | # BEGIN FIBO_BY_HAND
12 | class Fibonacci:
13 |
14 | def __iter__(self):
15 | return FibonacciGenerator()
16 |
17 |
18 | class FibonacciGenerator:
19 |
20 | def __init__(self):
21 | self.a = 0
22 | self.b = 1
23 |
24 | def __next__(self):
25 | result = self.a
26 | self.a, self.b = self.b, self.a + self.b
27 | return result
28 |
29 | def __iter__(self):
30 | return self
31 | # END FIBO_BY_HAND
32 |
33 | # for comparison, this is the usual implementation of a Fibonacci
34 | # generator in Python:
35 |
36 |
37 | def fibonacci():
38 | a, b = 0, 1
39 | while True:
40 | yield a
41 | a, b = b, a + b
42 |
43 |
44 | if __name__ == '__main__':
45 |
46 | for x, y in zip(Fibonacci(), fibonacci()):
47 | assert x == y, '%s != %s' % (x, y)
48 | print(x)
49 | if x > 10**10:
50 | break
51 | print('etc...')
52 |
--------------------------------------------------------------------------------
/14-it-generator/isis2json/README.rst:
--------------------------------------------------------------------------------
1 | isis2json.py
2 | ============
3 |
4 | This directory contains a copy of the ``isis2json.py`` script, with
5 | minimal dependencies, just to allow the O'Reilly Atlas toolchain to
6 | render the listing of the script in appendix A of the book.
7 |
8 | If you want to use or contribute to this script, please get the full
9 | source code with all dependencies from the main ``isis2json``
10 | repository:
11 |
12 | https://github.com/fluentpython/isis2json
13 |
--------------------------------------------------------------------------------
/14-it-generator/sentence.py:
--------------------------------------------------------------------------------
1 | """
2 | Sentence: access words by index
3 | """
4 |
5 | import re
6 | import reprlib
7 |
8 | RE_WORD = re.compile('\w+')
9 |
10 |
11 | class Sentence:
12 |
13 | def __init__(self, text):
14 | self.text = text
15 | self.words = RE_WORD.findall(text) # <1>
16 |
17 | def __getitem__(self, index):
18 | return self.words[index] # <2>
19 |
20 | def __len__(self, index): # <3>
21 | return len(self.words)
22 |
23 | def __repr__(self):
24 | return 'Sentence(%s)' % reprlib.repr(self.text) # <4>
25 |
--------------------------------------------------------------------------------
/14-it-generator/sentence_gen.py:
--------------------------------------------------------------------------------
1 | """
2 | Sentence: iterate over words using a generator function
3 | """
4 |
5 | import re
6 | import reprlib
7 |
8 | RE_WORD = re.compile('\w+')
9 |
10 |
11 | class Sentence:
12 |
13 | def __init__(self, text):
14 | self.text = text
15 | self.words = RE_WORD.findall(text)
16 |
17 | def __repr__(self):
18 | return 'Sentence(%s)' % reprlib.repr(self.text)
19 |
20 | def __iter__(self):
21 | for word in self.words: # <1>
22 | yield word # <2>
23 | return # <3>
24 |
25 | # done! <4>
26 |
--------------------------------------------------------------------------------
/14-it-generator/sentence_gen2.py:
--------------------------------------------------------------------------------
1 | """
2 | Sentence: iterate over words using a generator function
3 | """
4 |
5 | import re
6 | import reprlib
7 |
8 | RE_WORD = re.compile('\w+')
9 |
10 |
11 | class Sentence:
12 |
13 | def __init__(self, text):
14 | self.text = text # <1>
15 |
16 | def __repr__(self):
17 | return 'Sentence(%s)' % reprlib.repr(self.text)
18 |
19 | def __iter__(self):
20 | for match in RE_WORD.finditer(self.text): # <2>
21 | yield match.group() # <3>
22 |
--------------------------------------------------------------------------------
/14-it-generator/sentence_genexp.py:
--------------------------------------------------------------------------------
1 | """
2 | Sentence: iterate over words using a generator expression
3 | """
4 |
5 | # BEGIN SENTENCE_GENEXP
6 | import re
7 | import reprlib
8 |
9 | RE_WORD = re.compile('\w+')
10 |
11 |
12 | class Sentence:
13 |
14 | def __init__(self, text):
15 | self.text = text
16 |
17 | def __repr__(self):
18 | return 'Sentence(%s)' % reprlib.repr(self.text)
19 |
20 | def __iter__(self):
21 | return (match.group() for match in RE_WORD.finditer(self.text))
22 | # END SENTENCE_GENEXP
23 |
24 |
25 | def main():
26 | import sys
27 | import warnings
28 | try:
29 | filename = sys.argv[1]
30 | word_number = int(sys.argv[2])
31 | except (IndexError, ValueError):
32 | print('Usage: %s ' % sys.argv[0])
33 | sys.exit(1)
34 | with open(filename, 'rt', encoding='utf-8') as text_file:
35 | s = Sentence(text_file.read())
36 | for n, word in enumerate(s, 1):
37 | if n == word_number:
38 | print(word)
39 | break
40 | else:
41 | warnings.warn('last word is #%d, "%s"' % (n, word))
42 |
43 | if __name__ == '__main__':
44 | main()
45 |
--------------------------------------------------------------------------------
/14-it-generator/sentence_iter2.py:
--------------------------------------------------------------------------------
1 | """
2 | Sentence: iterate over words using the Iterator Pattern, take #2
3 |
4 | WARNING: the Iterator Pattern is much simpler in idiomatic Python;
5 | see: sentence_gen*.py.
6 | """
7 |
8 | import re
9 | import reprlib
10 |
11 | RE_WORD = re.compile('\w+')
12 |
13 |
14 | class Sentence:
15 |
16 | def __init__(self, text):
17 | self.text = text
18 |
19 | def __repr__(self):
20 | return 'Sentence(%s)' % reprlib.repr(self.text)
21 |
22 | def __iter__(self):
23 | word_iter = RE_WORD.finditer(self.text) # <1>
24 | return SentenceIter(word_iter) # <2>
25 |
26 |
27 | class SentenceIter():
28 |
29 | def __init__(self, word_iter):
30 | self.word_iter = word_iter # <3>
31 |
32 | def __next__(self):
33 | match = next(self.word_iter) # <4>
34 | return match.group() # <5>
35 |
36 | def __iter__(self):
37 | return self
38 |
--------------------------------------------------------------------------------
/14-it-generator/sentence_runner.py:
--------------------------------------------------------------------------------
1 | import doctest
2 | import importlib
3 | import glob
4 |
5 |
6 | TARGET_GLOB = 'sentence*.py'
7 | TEST_FILE = 'sentence.rst'
8 | TEST_MSG = '{0:16} {1.attempted:2} tests, {1.failed:2} failed - {2}'
9 |
10 |
11 | def main(argv):
12 | verbose = '-v' in argv
13 | for module_file_name in sorted(glob.glob(TARGET_GLOB)):
14 | module_name = module_file_name.replace('.py', '')
15 | module = importlib.import_module(module_name)
16 | try:
17 | cls = getattr(module, 'Sentence')
18 | except AttributeError:
19 | continue
20 | test(cls, verbose)
21 |
22 |
23 | def test(cls, verbose=False):
24 |
25 | res = doctest.testfile(
26 | TEST_FILE,
27 | globs={'Sentence': cls},
28 | verbose=verbose,
29 | optionflags=doctest.REPORT_ONLY_FIRST_FAILURE)
30 | tag = 'FAIL' if res.failed else 'OK'
31 | print(TEST_MSG.format(cls.__module__, res, tag))
32 |
33 |
34 | if __name__ == '__main__':
35 | import sys
36 | main(sys.argv)
37 |
--------------------------------------------------------------------------------
/14-it-generator/yield_delegate_fail.py:
--------------------------------------------------------------------------------
1 | """ Example from `Python: The Full Monty`__ -- A Tested Semantics for the
2 | Python Programming Language
3 |
4 | __ http://cs.brown.edu/~sk/Publications/Papers/Published/pmmwplck-python-full-monty/
5 |
6 | "The following program, [...] seems to perform a simple abstraction over the
7 | process of yielding:"
8 |
9 | Citation:
10 |
11 | Joe Gibbs Politz, Alejandro Martinez, Matthew Milano, Sumner Warren,
12 | Daniel Patterson, Junsong Li, Anand Chitipothu, and Shriram Krishnamurthi.
13 | 2013. Python: the full monty. SIGPLAN Not. 48, 10 (October 2013), 217-232.
14 | DOI=10.1145/2544173.2509536 http://doi.acm.org/10.1145/2544173.2509536
15 | """
16 |
17 | # BEGIN YIELD_DELEGATE_FAIL
18 | def f():
19 | def do_yield(n):
20 | yield n
21 | x = 0
22 | while True:
23 | x += 1
24 | do_yield(x)
25 | # END YIELD_DELEGATE_FAIL
26 |
27 | if __name__ == '__main__':
28 | print('Invoking f() results in an infinite loop')
29 | f()
30 |
--------------------------------------------------------------------------------
/14-it-generator/yield_delegate_fix.py:
--------------------------------------------------------------------------------
1 | """ Example adapted from ``yield_delegate_fail.py``
2 |
3 | The following program performs a simple abstraction over the process of
4 | yielding.
5 |
6 | """
7 |
8 | # BEGIN YIELD_DELEGATE_FIX
9 | def f():
10 | def do_yield(n):
11 | yield n
12 | x = 0
13 | while True:
14 | x += 1
15 | yield from do_yield(x)
16 | # END YIELD_DELEGATE_FIX
17 |
18 | if __name__ == '__main__':
19 | print('Invoking f() now produces a generator')
20 | g = f()
21 | print(next(g))
22 | print(next(g))
23 | print(next(g))
24 |
25 |
--------------------------------------------------------------------------------
/15-context-mngr/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 15 - "Context managers and something else"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/16-coroutine/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 16 - "Coroutines"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/16-coroutine/coroaverager0.py:
--------------------------------------------------------------------------------
1 | """
2 | A coroutine to compute a running average
3 |
4 | # BEGIN CORO_AVERAGER_TEST
5 | >>> coro_avg = averager() # <1>
6 | >>> next(coro_avg) # <2>
7 | >>> coro_avg.send(10) # <3>
8 | 10.0
9 | >>> coro_avg.send(30)
10 | 20.0
11 | >>> coro_avg.send(5)
12 | 15.0
13 |
14 | # END CORO_AVERAGER_TEST
15 |
16 | """
17 |
18 | # BEGIN CORO_AVERAGER
19 | def averager():
20 | total = 0.0
21 | count = 0
22 | average = None
23 | while True: # <1>
24 | term = yield average # <2>
25 | total += term
26 | count += 1
27 | average = total/count
28 | # END CORO_AVERAGER
29 |
--------------------------------------------------------------------------------
/16-coroutine/coroaverager1.py:
--------------------------------------------------------------------------------
1 | # BEGIN DECORATED_AVERAGER
2 | """
3 | A coroutine to compute a running average
4 |
5 | >>> coro_avg = averager() # <1>
6 | >>> from inspect import getgeneratorstate
7 | >>> getgeneratorstate(coro_avg) # <2>
8 | 'GEN_SUSPENDED'
9 | >>> coro_avg.send(10) # <3>
10 | 10.0
11 | >>> coro_avg.send(30)
12 | 20.0
13 | >>> coro_avg.send(5)
14 | 15.0
15 |
16 | """
17 |
18 | from coroutil import coroutine # <4>
19 |
20 | @coroutine # <5>
21 | def averager(): # <6>
22 | total = 0.0
23 | count = 0
24 | average = None
25 | while True:
26 | term = yield average
27 | total += term
28 | count += 1
29 | average = total/count
30 | # END DECORATED_AVERAGER
31 |
--------------------------------------------------------------------------------
/16-coroutine/coroutil.py:
--------------------------------------------------------------------------------
1 | # BEGIN CORO_DECO
2 | from functools import wraps
3 |
4 | def coroutine(func):
5 | """Decorator: primes `func` by advancing to first `yield`"""
6 | @wraps(func)
7 | def primer(*args,**kwargs): # <1>
8 | gen = func(*args,**kwargs) # <2>
9 | next(gen) # <3>
10 | return gen # <4>
11 | return primer
12 | # END CORO_DECO
13 |
--------------------------------------------------------------------------------
/16-coroutine/yield_from_expansion_simplified.py:
--------------------------------------------------------------------------------
1 | # Code below is a very simplified expansion of the statement:
2 | #
3 | # RESULT = yield from EXPR
4 | #
5 | # This code assumes that the subgenerator will run to completion,
6 | # without the client ever calling ``.throw()`` or ``.close()``.
7 | # Also, this code makes no distinction between the client
8 | # calling ``next(subgen)`` or ``subgen.send(...)``
9 | #
10 | # The full expansion is in:
11 | # PEP 380 -- Syntax for Delegating to a Subgenerator
12 | #
13 | # https://www.python.org/dev/peps/pep-0380/#formal-semantics
14 |
15 |
16 | # BEGIN YIELD_FROM_EXPANSION_SIMPLIFIED
17 | _i = iter(EXPR) # <1>
18 | try:
19 | _y = next(_i) # <2>
20 | except StopIteration as _e:
21 | _r = _e.value # <3>
22 | else:
23 | while 1: # <4>
24 | _s = yield _y # <5>
25 | try:
26 | _y = _i.send(_s) # <6>
27 | except StopIteration as _e: # <7>
28 | _r = _e.value
29 | break
30 |
31 | RESULT = _r # <8>
32 | # END YIELD_FROM_EXPANSION_SIMPLIFIED
33 |
--------------------------------------------------------------------------------
/17-futures/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 17 - "Concurrency with futures"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/17-futures/countries/country_codes.txt:
--------------------------------------------------------------------------------
1 | AD AE AF AG AL AM AO AR AT AU AZ BA BB BD BE BF BG BH BI BJ BN BO BR BS BT
2 | BW BY BZ CA CD CF CG CH CI CL CM CN CO CR CU CV CY CZ DE DJ DK DM DZ EC EE
3 | EG ER ES ET FI FJ FM FR GA GB GD GE GH GM GN GQ GR GT GW GY HN HR HT HU ID
4 | IE IL IN IQ IR IS IT JM JO JP KE KG KH KI KM KN KP KR KW KZ LA LB LC LI LK
5 | LR LS LT LU LV LY MA MC MD ME MG MH MK ML MM MN MR MT MU MV MW MX MY MZ NA
6 | NE NG NI NL NO NP NR NZ OM PA PE PG PH PK PL PT PW PY QA RO RS RU RW SA SB
7 | SC SD SE SG SI SK SL SM SN SO SR SS ST SV SY SZ TD TG TH TJ TL TM TN TO TR
8 | TT TV TW TZ UA UG US UY UZ VA VC VE VN VU WS YE ZA ZM ZW
9 |
--------------------------------------------------------------------------------
/17-futures/countries/flags.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/17-futures/countries/flags.zip
--------------------------------------------------------------------------------
/17-futures/countries/flags_asyncio.py:
--------------------------------------------------------------------------------
1 | """Download flags of top 20 countries by population
2 |
3 | asyncio + aiottp version
4 |
5 | Sample run::
6 |
7 | $ python3 flags_asyncio.py
8 | EG VN IN TR RU ID US DE CN MX JP BD NG ET FR BR PH PK CD IR
9 | 20 flags downloaded in 1.07s
10 |
11 | """
12 | # BEGIN FLAGS_ASYNCIO
13 | import asyncio
14 |
15 | import aiohttp # <1>
16 |
17 | from flags import BASE_URL, save_flag, show, main # <2>
18 |
19 |
20 | @asyncio.coroutine # <3>
21 | def get_flag(cc):
22 | url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
23 | resp = yield from aiohttp.request('GET', url) # <4>
24 | image = yield from resp.read() # <5>
25 | return image
26 |
27 |
28 | @asyncio.coroutine
29 | def download_one(cc): # <6>
30 | image = yield from get_flag(cc) # <7>
31 | show(cc)
32 | save_flag(image, cc.lower() + '.gif')
33 | return cc
34 |
35 |
36 | def download_many(cc_list):
37 | loop = asyncio.get_event_loop() # <8>
38 | to_do = [download_one(cc) for cc in sorted(cc_list)] # <9>
39 | wait_coro = asyncio.wait(to_do) # <10>
40 | res, _ = loop.run_until_complete(wait_coro) # <11>
41 | loop.close() # <12>
42 |
43 | return len(res)
44 |
45 |
46 | if __name__ == '__main__':
47 | main(download_many)
48 | # END FLAGS_ASYNCIO
49 |
--------------------------------------------------------------------------------
/17-futures/countries/flags_await.py:
--------------------------------------------------------------------------------
1 | """Download flags of top 20 countries by population
2 |
3 | asyncio + aiottp version
4 |
5 | Sample run::
6 |
7 | $ python3 flags_asyncio.py
8 | EG VN IN TR RU ID US DE CN MX JP BD NG ET FR BR PH PK CD IR
9 | 20 flags downloaded in 1.07s
10 |
11 | """
12 | # BEGIN FLAGS_ASYNCIO
13 | import asyncio
14 |
15 | import aiohttp # <1>
16 |
17 | from flags import BASE_URL, save_flag, show, main # <2>
18 |
19 |
20 | async def get_flag(cc): # <3>
21 | url = '{}/{cc}/{cc}.gif'.format(BASE_URL, cc=cc.lower())
22 | resp = await aiohttp.request('GET', url) # <4>
23 | image = await resp.read() # <5>
24 | return image
25 |
26 |
27 | async def download_one(cc): # <6>
28 | image = await get_flag(cc) # <7>
29 | show(cc)
30 | save_flag(image, cc.lower() + '.gif')
31 | return cc
32 |
33 |
34 | def download_many(cc_list):
35 | loop = asyncio.get_event_loop() # <8>
36 | to_do = [download_one(cc) for cc in sorted(cc_list)] # <9>
37 | wait_coro = asyncio.wait(to_do) # <10>
38 | res, _ = loop.run_until_complete(wait_coro) # <11>
39 | loop.close() # <12>
40 |
41 | return len(res)
42 |
43 |
44 | if __name__ == '__main__':
45 | main(download_many)
46 | # END FLAGS_ASYNCIO
47 |
--------------------------------------------------------------------------------
/17-futures/countries/flags_threadpool.py:
--------------------------------------------------------------------------------
1 | """Download flags of top 20 countries by population
2 |
3 | ThreadPoolExecutor version
4 |
5 | Sample run::
6 |
7 | $ python3 flags_threadpool.py
8 | BD retrieved.
9 | EG retrieved.
10 | CN retrieved.
11 | ...
12 | PH retrieved.
13 | US retrieved.
14 | IR retrieved.
15 | 20 flags downloaded in 0.93s
16 |
17 | """
18 | # BEGIN FLAGS_THREADPOOL
19 | from concurrent import futures
20 |
21 | from flags import save_flag, get_flag, show, main # <1>
22 |
23 | MAX_WORKERS = 20 # <2>
24 |
25 |
26 | def download_one(cc): # <3>
27 | image = get_flag(cc)
28 | show(cc)
29 | save_flag(image, cc.lower() + '.gif')
30 | return cc
31 |
32 |
33 | def download_many(cc_list):
34 | workers = min(MAX_WORKERS, len(cc_list)) # <4>
35 | with futures.ThreadPoolExecutor(workers) as executor: # <5>
36 | res = executor.map(download_one, sorted(cc_list)) # <6>
37 |
38 | return len(list(res)) # <7>
39 |
40 |
41 | if __name__ == '__main__':
42 | main(download_many) # <8>
43 | # END FLAGS_THREADPOOL
44 |
--------------------------------------------------------------------------------
/17-futures/countries/requirements.txt:
--------------------------------------------------------------------------------
1 | aiohttp==0.13.1
2 | requests==2.5.1
3 | tqdm==1.0
4 |
--------------------------------------------------------------------------------
/17-futures/countries/vaurien_delay.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | vaurien --protocol http --backend localhost:8001 \
3 | --proxy localhost:8002 \
4 | --behavior 100:delay --behavior-delay-sleep .5
5 |
--------------------------------------------------------------------------------
/17-futures/countries/vaurien_error_delay.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | vaurien --protocol http --backend localhost:8001 \
3 | --proxy localhost:8003 \
4 | --behavior 25:error,50:delay --behavior-delay-sleep .5
--------------------------------------------------------------------------------
/17-futures/crypto/arcfour-timings.txt:
--------------------------------------------------------------------------------
1 | workers|time
2 | 4|4.96
3 | 3|5.40
4 | 2|8.35
5 | 1|11.25
6 | 1|11.17
7 | 2|8.45
8 | 3|6.08
9 | 4|5.83
10 | 4|6.22
11 | 3|7.33
12 | 2|9.48
13 | 1|11.86
14 | 1|11.72
15 | 2|9.22
16 | 3|6.74
17 | 4|6.37
18 | 4|4.94
19 | 3|5.51
20 | 2|8.25
21 | 1|11.47
22 | 1|12.90
23 | 2|8.94
24 | 3|6.44
25 | 4|5.90
26 | 4|5.94
27 | 3|6.46
28 | 2|9.10
29 | 1|11.66
30 | 1|11.48
31 | 2|9.08
32 | 3|6.31
33 | 4|5.99
34 | 4|5.02
35 | 3|5.46
36 | 2|8.26
37 | 1|11.18
38 | 1|11.23
39 | 2|8.52
40 | 3|5.64
41 | 4|5.39
42 | 4|5.53
43 | 3|6.07
44 | 2|8.66
45 | 1|11.42
46 | 1|11.34
47 | 2|8.44
48 | 3|5.88
49 | 4|5.57
50 | 4|4.93
51 | 3|5.47
52 | 2|8.65
53 | 1|11.23
54 | 1|11.12
55 | 2|7.83
56 | 3|5.81
57 | 4|5.45
58 | 4|5.54
59 | 3|6.09
60 | 2|8.84
61 | 1|11.45
62 | 1|11.25
63 | 2|8.32
64 | 3|6.02
65 | 4|5.74
66 |
--------------------------------------------------------------------------------
/17-futures/crypto/arcfour_futures.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 | from concurrent import futures
4 | from random import randrange
5 | from arcfour import arcfour
6 |
7 | JOBS = 12
8 | SIZE = 2**18
9 |
10 | KEY = b"'Twas brillig, and the slithy toves\nDid gyre"
11 | STATUS = '{} workers, elapsed time: {:.2f}s'
12 |
13 |
14 | def arcfour_test(size, key):
15 | in_text = bytearray(randrange(256) for i in range(size))
16 | cypher_text = arcfour(key, in_text)
17 | out_text = arcfour(key, cypher_text)
18 | assert in_text == out_text, 'Failed arcfour_test'
19 | return size
20 |
21 |
22 | def main(workers=None):
23 | if workers:
24 | workers = int(workers)
25 | t0 = time.time()
26 |
27 | with futures.ProcessPoolExecutor(workers) as executor:
28 | actual_workers = executor._max_workers
29 | to_do = []
30 | for i in range(JOBS, 0, -1):
31 | size = SIZE + int(SIZE / JOBS * (i - JOBS/2))
32 | job = executor.submit(arcfour_test, size, KEY)
33 | to_do.append(job)
34 |
35 | for future in futures.as_completed(to_do):
36 | res = future.result()
37 | print('{:.1f} KB'.format(res/2**10))
38 |
39 | print(STATUS.format(actual_workers, time.time() - t0))
40 |
41 | if __name__ == '__main__':
42 | if len(sys.argv) == 2:
43 | workers = int(sys.argv[1])
44 | else:
45 | workers = None
46 | main(workers)
47 |
--------------------------------------------------------------------------------
/17-futures/crypto/sha-timings.txt:
--------------------------------------------------------------------------------
1 | workers|time
2 | 4|8.88
3 | 3|11.14
4 | 2|13.66
5 | 1|22.80
6 | 1|25.42
7 | 2|16.37
8 | 3|12.09
9 | 4|11.06
10 | 4|11.40
11 | 3|11.51
12 | 2|15.20
13 | 1|24.18
14 | 1|22.09
15 | 2|12.48
16 | 3|10.78
17 | 4|10.48
18 | 4|8.48
19 | 3|10.07
20 | 2|12.42
21 | 1|20.24
22 | 1|20.31
23 | 2|11.39
24 | 3|10.88
25 | 4|10.44
26 | 4|10.43
27 | 3|11.11
28 | 2|12.39
29 | 1|20.69
30 | 1|20.53
31 | 2|11.80
32 | 3|11.01
33 | 4|10.52
34 | 4|11.50
35 | 3|14.45
36 | 2|16.95
37 | 1|24.77
38 | 1|22.71
39 | 2|18.35
40 | 3|12.66
41 | 4|12.20
42 | 4|12.37
43 | 3|13.37
44 | 2|19.30
45 | 1|24.30
46 | 1|23.93
47 | 2|18.51
48 | 3|13.88
49 | 4|12.97
50 |
51 |
--------------------------------------------------------------------------------
/17-futures/crypto/sha_futures.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import time
3 | import hashlib
4 | from concurrent import futures
5 | from random import randrange
6 |
7 | JOBS = 12
8 | SIZE = 2**20
9 | STATUS = '{} workers, elapsed time: {:.2f}s'
10 |
11 |
12 | def sha(size):
13 | data = bytearray(randrange(256) for i in range(size))
14 | algo = hashlib.new('sha256')
15 | algo.update(data)
16 | return algo.hexdigest()
17 |
18 |
19 | def main(workers=None):
20 | if workers:
21 | workers = int(workers)
22 | t0 = time.time()
23 |
24 | with futures.ProcessPoolExecutor(workers) as executor:
25 | actual_workers = executor._max_workers
26 | to_do = (executor.submit(sha, SIZE) for i in range(JOBS))
27 | for future in futures.as_completed(to_do):
28 | res = future.result()
29 | print(res)
30 |
31 | print(STATUS.format(actual_workers, time.time() - t0))
32 |
33 | if __name__ == '__main__':
34 | if len(sys.argv) == 2:
35 | workers = int(sys.argv[1])
36 | else:
37 | workers = None
38 | main(workers)
39 |
--------------------------------------------------------------------------------
/17-futures/demo_executor_map.py:
--------------------------------------------------------------------------------
1 | """
2 | Experiment with ``ThreadPoolExecutor.map``
3 | """
4 | # BEGIN EXECUTOR_MAP
5 | from time import sleep, strftime
6 | from concurrent import futures
7 |
8 |
9 | def display(*args): # <1>
10 | print(strftime('[%H:%M:%S]'), end=' ')
11 | print(*args)
12 |
13 |
14 | def loiter(n): # <2>
15 | msg = '{}loiter({}): doing nothing for {}s...'
16 | display(msg.format('\t'*n, n, n))
17 | sleep(n)
18 | msg = '{}loiter({}): done.'
19 | display(msg.format('\t'*n, n))
20 | return n * 10 # <3>
21 |
22 |
23 | def main():
24 | display('Script starting.')
25 | executor = futures.ThreadPoolExecutor(max_workers=3) # <4>
26 | results = executor.map(loiter, range(5)) # <5>
27 | display('results:', results) # <6>.
28 | display('Waiting for individual results:')
29 | for i, result in enumerate(results): # <7>
30 | display('result {}: {}'.format(i, result))
31 |
32 |
33 | main()
34 | # END EXECUTOR_MAP
35 |
--------------------------------------------------------------------------------
/18-asyncio/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 18 - "Concurrency with asyncio"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/18-asyncio/charfinder/http_charfinder.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Charfinder
6 |
7 |
8 | Examples: {links}
9 |
10 |
14 | | |
15 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/18-asyncio/countries/README.rst:
--------------------------------------------------------------------------------
1 | The ``asyncio`` flag download examples are in the
2 | ``../../17-futures/countries/`` directory together
3 | with the sequential and threadpool examples.
4 |
--------------------------------------------------------------------------------
/18b-async-await/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 18 - "Concurrency with asyncio"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
6 | ##################################################################
7 | NOTE: this "18b" directory contains the examples of chapter 18
8 | rewritten using the new async/await syntax available in Python 3.5
9 | ONLY, instead of the "yield-from" syntax which works since Python
10 | 3.3 (and will still work with Python 3.5).
11 | ##################################################################
12 |
--------------------------------------------------------------------------------
/18b-async-await/charfinder/.gitignore:
--------------------------------------------------------------------------------
1 | charfinder_index.pickle
2 |
--------------------------------------------------------------------------------
/18b-async-await/charfinder/http_charfinder.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Charfinder
6 |
7 |
8 | Examples: {links}
9 |
10 |
14 |
15 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/18b-async-await/countries/README.rst:
--------------------------------------------------------------------------------
1 | The ``asyncio`` flag download examples are in the
2 | ``../../17-futures/countries/`` directory together
3 | with the sequential and threadpool examples.
4 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 19 - "Dynamic attributes and properties"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/bulkfood/bulkfood_v1.py:
--------------------------------------------------------------------------------
1 | """
2 | A line item for a bulk food order has description, weight and price fields.
3 | A ``subtotal`` method gives the total price for that line item::
4 |
5 | >>> raisins = LineItem('Golden raisins', 10, 6.95)
6 | >>> raisins.weight, raisins.description, raisins.price
7 | (10, 'Golden raisins', 6.95)
8 | >>> raisins.subtotal()
9 | 69.5
10 |
11 | But, without validation, these public attributes can cause trouble::
12 |
13 | # BEGIN LINEITEM_PROBLEM_V1
14 |
15 | >>> raisins = LineItem('Golden raisins', 10, 6.95)
16 | >>> raisins.subtotal()
17 | 69.5
18 | >>> raisins.weight = -20 # garbage in...
19 | >>> raisins.subtotal() # garbage out...
20 | -139.0
21 |
22 | # END LINEITEM_PROBLEM_V1
23 |
24 | """
25 |
26 |
27 | # BEGIN LINEITEM_V1
28 | class LineItem:
29 |
30 | def __init__(self, description, weight, price):
31 | self.description = description
32 | self.weight = weight
33 | self.price = price
34 |
35 | def subtotal(self):
36 | return self.weight * self.price
37 | # END LINEITEM_V1
38 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/doc_property.py:
--------------------------------------------------------------------------------
1 | """
2 | Example of property documentation
3 |
4 | >>> f = Foo()
5 | >>> f.bar = 77
6 | >>> f.bar
7 | 77
8 | >>> Foo.bar.__doc__
9 | 'The bar attribute'
10 | """
11 |
12 | # BEGIN DOC_PROPERTY
13 | class Foo:
14 |
15 | @property
16 | def bar(self):
17 | '''The bar attribute'''
18 | return self.__dict__['bar']
19 |
20 | @bar.setter
21 | def bar(self, value):
22 | self.__dict__['bar'] = value
23 | # END DOC_PROPERTY
24 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/oscon/demo_schedule2.py:
--------------------------------------------------------------------------------
1 | import shelve
2 |
3 | from schedule2 import DB_NAME, CONFERENCE, load_db
4 | from schedule2 import DbRecord, Event
5 |
6 | with shelve.open(DB_NAME) as db:
7 | if CONFERENCE not in db:
8 | load_db(db)
9 |
10 | DbRecord.set_db(db)
11 | event = DbRecord.fetch('event.33950')
12 | print(event)
13 | print(event.venue)
14 | print(event.venue.name)
15 | for spkr in event.speakers:
16 | print('{0.serial}: {0.name}'.format(spkr))
17 |
18 | print(repr(Event.venue))
19 |
20 | event2 = DbRecord.fetch('event.33451')
21 | print(event2)
22 | print(event2.fetch)
23 | print(event2.venue)
--------------------------------------------------------------------------------
/19-dyn-attr-prop/oscon/osconfeed-sample.json:
--------------------------------------------------------------------------------
1 | { "Schedule":
2 | { "conferences": [{"serial": 115 }],
3 | "events": [
4 | { "serial": 34505,
5 | "name": "Why Schools Don´t Use Open Source to Teach Programming",
6 | "event_type": "40-minute conference session",
7 | "time_start": "2014-07-23 11:30:00",
8 | "time_stop": "2014-07-23 12:10:00",
9 | "venue_serial": 1462,
10 | "description": "Aside from the fact that high school programming...",
11 | "website_url": "http://oscon.com/oscon2014/public/schedule/detail/34505",
12 | "speakers": [157509],
13 | "categories": ["Education"] }
14 | ],
15 | "speakers": [
16 | { "serial": 157509,
17 | "name": "Robert Lefkowitz",
18 | "photo": null,
19 | "url": "http://sharewave.com/",
20 | "position": "CTO",
21 | "affiliation": "Sharewave",
22 | "twitter": "sharewaveteam",
23 | "bio": "Robert ´r0ml´ Lefkowitz is the CTO at Sharewave, a startup..." }
24 | ],
25 | "venues": [
26 | { "serial": 1462,
27 | "name": "F151",
28 | "category": "Conference Venues" }
29 | ]
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/oscon/osconfeed.py:
--------------------------------------------------------------------------------
1 | """
2 | osconfeed.py: Script to download the OSCON schedule feed
3 |
4 | # BEGIN OSCONFEED_DEMO
5 |
6 | >>> feed = load() # <1>
7 | >>> sorted(feed['Schedule'].keys()) # <2>
8 | ['conferences', 'events', 'speakers', 'venues']
9 | >>> for key, value in sorted(feed['Schedule'].items()):
10 | ... print('{:3} {}'.format(len(value), key)) # <3>
11 | ...
12 | 1 conferences
13 | 484 events
14 | 357 speakers
15 | 53 venues
16 | >>> feed['Schedule']['speakers'][-1]['name'] # <4>
17 | 'Carina C. Zona'
18 | >>> feed['Schedule']['speakers'][-1]['serial'] # <5>
19 | 141590
20 | >>> feed['Schedule']['events'][40]['name']
21 | 'There *Will* Be Bugs'
22 | >>> feed['Schedule']['events'][40]['speakers'] # <6>
23 | [3471, 5199]
24 |
25 |
26 | # END OSCONFEED_DEMO
27 | """
28 |
29 | # BEGIN OSCONFEED
30 | from urllib.request import urlopen
31 | import warnings
32 | import os
33 | import json
34 |
35 | URL = 'http://www.oreilly.com/pub/sc/osconfeed'
36 | JSON = 'data/osconfeed.json'
37 |
38 |
39 | def load():
40 | if not os.path.exists(JSON):
41 | msg = 'downloading {} to {}'.format(URL, JSON)
42 | warnings.warn(msg) # <1>
43 | with urlopen(URL) as remote, open(JSON, 'wb') as local: # <2>
44 | local.write(remote.read())
45 |
46 | with open(JSON) as fp:
47 | return json.load(fp) # <3>
48 |
49 | # END OSCONFEED
50 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/oscon/schedule1.py:
--------------------------------------------------------------------------------
1 | """
2 | schedule1.py: traversing OSCON schedule data
3 |
4 | # BEGIN SCHEDULE1_DEMO
5 | >>> import shelve
6 | >>> db = shelve.open(DB_NAME) # <1>
7 | >>> if CONFERENCE not in db: # <2>
8 | ... load_db(db) # <3>
9 | ...
10 | >>> speaker = db['speaker.3471'] # <4>
11 | >>> type(speaker) # <5>
12 |
13 | >>> speaker.name, speaker.twitter # <6>
14 | ('Anna Martelli Ravenscroft', 'annaraven')
15 | >>> db.close() # <7>
16 |
17 | # END SCHEDULE1_DEMO
18 |
19 | """
20 |
21 | # BEGIN SCHEDULE1
22 | import warnings
23 |
24 | import osconfeed # <1>
25 |
26 | DB_NAME = 'data/schedule1_db'
27 | CONFERENCE = 'conference.115'
28 |
29 |
30 | class Record:
31 | def __init__(self, **kwargs):
32 | self.__dict__.update(kwargs) # <2>
33 |
34 |
35 | def load_db(db):
36 | raw_data = osconfeed.load() # <3>
37 | warnings.warn('loading ' + DB_NAME)
38 | for collection, rec_list in raw_data['Schedule'].items(): # <4>
39 | record_type = collection[:-1] # <5>
40 | for record in rec_list:
41 | key = '{}.{}'.format(record_type, record['serial']) # <6>
42 | record['serial'] = key # <7>
43 | db[key] = Record(**record) # <8>
44 |
45 | # END SCHEDULE1
46 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/oscon/test_schedule1.py:
--------------------------------------------------------------------------------
1 | import shelve
2 | import pytest
3 |
4 | import schedule1 as schedule
5 |
6 |
7 | @pytest.yield_fixture
8 | def db():
9 | with shelve.open(schedule.DB_NAME) as the_db:
10 | if schedule.CONFERENCE not in the_db:
11 | schedule.load_db(the_db)
12 | yield the_db
13 |
14 |
15 | def test_record_class():
16 | rec = schedule.Record(spam=99, eggs=12)
17 | assert rec.spam == 99
18 | assert rec.eggs == 12
19 |
20 |
21 | def test_conference_record(db):
22 | assert schedule.CONFERENCE in db
23 |
24 |
25 | def test_speaker_record(db):
26 | speaker = db['speaker.3471']
27 | assert speaker.name == 'Anna Martelli Ravenscroft'
28 |
29 |
30 | def test_event_record(db):
31 | event = db['event.33950']
32 | assert event.name == 'There *Will* Be Bugs'
33 |
34 |
35 | def test_event_venue(db):
36 | event = db['event.33950']
37 | assert event.venue_serial == 1449
38 |
--------------------------------------------------------------------------------
/19-dyn-attr-prop/pseudo_construction.py:
--------------------------------------------------------------------------------
1 | # pseudo-code for object construction
2 | def object_maker(the_class, some_arg):
3 | new_object = the_class.__new__(some_arg)
4 | if isinstance(new_object, the_class):
5 | the_class.__init__(new_object, some_arg)
6 | return new_object
7 |
8 | # the following statements are roughly equivalent
9 | x = Foo('bar')
10 | x = object_maker(Foo, 'bar')
11 |
--------------------------------------------------------------------------------
/20-descriptor/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 20 - "Attribute descriptors"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/20-descriptor/bulkfood/model_v4c.py:
--------------------------------------------------------------------------------
1 | # BEGIN MODEL_V4
2 | class Quantity:
3 | __counter = 0
4 |
5 | def __init__(self):
6 | cls = self.__class__
7 | prefix = cls.__name__
8 | index = cls.__counter
9 | self.storage_name = '_{}#{}'.format(prefix, index)
10 | cls.__counter += 1
11 |
12 | def __get__(self, instance, owner):
13 | if instance is None:
14 | return self
15 | else:
16 | return getattr(instance, self.storage_name)
17 |
18 | def __set__(self, instance, value):
19 | if value > 0:
20 | setattr(instance, self.storage_name, value)
21 | else:
22 | raise ValueError('value must be > 0')
23 | # END MODEL_V4
24 |
--------------------------------------------------------------------------------
/20-descriptor/method_is_descriptor.py:
--------------------------------------------------------------------------------
1 | """
2 | # BEGIN FUNC_DESCRIPTOR_DEMO
3 |
4 | >>> word = Text('forward')
5 | >>> word # <1>
6 | Text('forward')
7 | >>> word.reverse() # <2>
8 | Text('drawrof')
9 | >>> Text.reverse(Text('backward')) # <3>
10 | Text('drawkcab')
11 | >>> type(Text.reverse), type(word.reverse) # <4>
12 | (, )
13 | >>> list(map(Text.reverse, ['repaid', (10, 20, 30), Text('stressed')])) # <5>
14 | ['diaper', (30, 20, 10), Text('desserts')]
15 | >>> Text.reverse.__get__(word) # <6>
16 |
17 | >>> Text.reverse.__get__(None, Text) # <7>
18 |
19 | >>> word.reverse # <8>
20 |
21 | >>> word.reverse.__self__ # <9>
22 | Text('forward')
23 | >>> word.reverse.__func__ is Text.reverse # <10>
24 | True
25 |
26 | # END FUNC_DESCRIPTOR_DEMO
27 | """
28 |
29 | # BEGIN FUNC_DESCRIPTOR_EX
30 | import collections
31 |
32 |
33 | class Text(collections.UserString):
34 |
35 | def __repr__(self):
36 | return 'Text({!r})'.format(self.data)
37 |
38 | def reverse(self):
39 | return self[::-1]
40 |
41 | # END FUNC_DESCRIPTOR_EX
42 |
--------------------------------------------------------------------------------
/21-class-metaprog/README.rst:
--------------------------------------------------------------------------------
1 | Sample code for Chapter 21 - "Class metaprogramming"
2 |
3 | From the book "Fluent Python" by Luciano Ramalho (O'Reilly, 2015)
4 | http://shop.oreilly.com/product/0636920032519.do
5 |
--------------------------------------------------------------------------------
/21-class-metaprog/evalsupport.py:
--------------------------------------------------------------------------------
1 | print('<[100]> evalsupport module start')
2 |
3 | def deco_alpha(cls):
4 | print('<[200]> deco_alpha')
5 |
6 | def inner_1(self):
7 | print('<[300]> deco_alpha:inner_1')
8 |
9 | cls.method_y = inner_1
10 | return cls
11 |
12 |
13 | class MetaAleph(type):
14 | print('<[400]> MetaAleph body')
15 |
16 | def __init__(cls, name, bases, dic):
17 | print('<[500]> MetaAleph.__init__')
18 |
19 | def inner_2(self):
20 | print('<[600]> MetaAleph.__init__:inner_2')
21 |
22 | cls.method_z = inner_2
23 |
24 |
25 | print('<[700]> evalsupport module end')
26 |
--------------------------------------------------------------------------------
/21-class-metaprog/evaltime.py:
--------------------------------------------------------------------------------
1 | from evalsupport import deco_alpha
2 |
3 | print('<[1]> evaltime module start')
4 |
5 |
6 | class ClassOne():
7 | print('<[2]> ClassOne body')
8 |
9 | def __init__(self):
10 | print('<[3]> ClassOne.__init__')
11 |
12 | def __del__(self):
13 | print('<[4]> ClassOne.__del__')
14 |
15 | def method_x(self):
16 | print('<[5]> ClassOne.method_x')
17 |
18 | class ClassTwo(object):
19 | print('<[6]> ClassTwo body')
20 |
21 |
22 | @deco_alpha
23 | class ClassThree():
24 | print('<[7]> ClassThree body')
25 |
26 | def method_y(self):
27 | print('<[8]> ClassThree.method_y')
28 |
29 |
30 | class ClassFour(ClassThree):
31 | print('<[9]> ClassFour body')
32 |
33 | def method_y(self):
34 | print('<[10]> ClassFour.method_y')
35 |
36 |
37 | if __name__ == '__main__':
38 | print('<[11]> ClassOne tests', 30 * '.')
39 | one = ClassOne()
40 | one.method_x()
41 | print('<[12]> ClassThree tests', 30 * '.')
42 | three = ClassThree()
43 | three.method_y()
44 | print('<[13]> ClassFour tests', 30 * '.')
45 | four = ClassFour()
46 | four.method_y()
47 |
48 |
49 | print('<[14]> evaltime module end')
50 |
--------------------------------------------------------------------------------
/21-class-metaprog/evaltime_meta.py:
--------------------------------------------------------------------------------
1 | from evalsupport import deco_alpha
2 | from evalsupport import MetaAleph
3 |
4 | print('<[1]> evaltime_meta module start')
5 |
6 |
7 | @deco_alpha
8 | class ClassThree():
9 | print('<[2]> ClassThree body')
10 |
11 | def method_y(self):
12 | print('<[3]> ClassThree.method_y')
13 |
14 |
15 | class ClassFour(ClassThree):
16 | print('<[4]> ClassFour body')
17 |
18 | def method_y(self):
19 | print('<[5]> ClassFour.method_y')
20 |
21 |
22 | class ClassFive(metaclass=MetaAleph):
23 | print('<[6]> ClassFive body')
24 |
25 | def __init__(self):
26 | print('<[7]> ClassFive.__init__')
27 |
28 | def method_z(self):
29 | print('<[8]> ClassFive.method_y')
30 |
31 |
32 | class ClassSix(ClassFive):
33 | print('<[9]> ClassSix body')
34 |
35 | def method_z(self):
36 | print('<[10]> ClassSix.method_y')
37 |
38 |
39 | if __name__ == '__main__':
40 | print('<[11]> ClassThree tests', 30 * '.')
41 | three = ClassThree()
42 | three.method_y()
43 | print('<[12]> ClassFour tests', 30 * '.')
44 | four = ClassFour()
45 | four.method_y()
46 | print('<[13]> ClassFive tests', 30 * '.')
47 | five = ClassFive()
48 | five.method_z()
49 | print('<[14]> ClassSix tests', 30 * '.')
50 | six = ClassSix()
51 | six.method_z()
52 |
53 | print('<[15]> evaltime_meta module end')
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Luciano Ramalho
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Fluent Python: notebooks
2 | ========================
3 |
4 | This repository contains example code for the book `Fluent Python`_ by Luciano Ramalho (O'Reilly, 2014), with Jupyter notebooks added by Allen Downey.
5 |
6 | You can run the notebooks by cloning this repo and running your own Jupyter server. Or you can run them on Binder by pressing the button below.
7 |
8 | [](http://mybinder.org/repo/AllenDowney/fluent-python-notebooks)
9 |
10 |
11 |
12 | **BEWARE**: This is a work in progress, like the book itself.
13 |
14 | * Code here may change and disappear without warning.
15 |
16 | * If a piece of code is not yet in the ebook, it's likely to be broken.
17 |
18 | * A major reorganization may happen when the last chapter is done.
19 |
20 | * No promises. No guarantees. Use at own risk.
21 |
22 | [*Fluent Python*](http://shop.oreilly.com/product/0636920032519.do)
23 |
--------------------------------------------------------------------------------
/attic/attributes/exists_truthy.py:
--------------------------------------------------------------------------------
1 | import timeit
2 |
3 | def exists_and_truthy_hasattr(obj, attr_name):
4 | if hasattr(obj, attr_name):
5 | return bool(getattr(obj, attr_name))
6 | else:
7 | return False
8 |
9 | def exists_and_truthy_getattr(obj, attr_name):
10 | return bool(getattr(obj, attr_name, False))
11 |
12 | def exists_and_truthy_tryget(obj, attr_name):
13 | try:
14 | return bool(getattr(obj, attr_name))
15 | except AttributeError:
16 | return False
17 |
18 |
19 | class Gizmo:
20 | def __init__(self):
21 | self.gadget = True
22 |
23 | gizmo = Gizmo()
24 |
25 | test_keys = 'hasattr', 'getattr', 'tryget'
26 |
27 | def average(timings):
28 | sample = timings[1:-1]
29 | return sum(sample) / len(sample)
30 |
31 | def do_tests():
32 | for test_key in test_keys:
33 | func_name = 'exists_and_truthy_' + test_key
34 | test = func_name + '(gizmo, "gadget")'
35 | setup = 'from __main__ import gizmo, ' + func_name
36 | elapsed = average(timeit.repeat(test, repeat=5, setup=setup))
37 | print(test_key.rjust(7), format(elapsed, '0.5f'))
38 |
39 | if __name__ == '__main__':
40 | do_tests()
41 | del gizmo.gadget
42 | do_tests()
43 |
44 |
--------------------------------------------------------------------------------
/attic/attributes/hasattr.py:
--------------------------------------------------------------------------------
1 | import timeit
2 |
3 | test_hasattr = """
4 | if hasattr(gizmo, 'gadget'):
5 | feature = gizmo.gadget
6 | else:
7 | feature = None
8 | """
9 |
10 | test_getattr = """
11 | feature = getattr(gizmo, 'gadget', None)
12 | """
13 |
14 | test_tryget = """
15 | try:
16 | feature = getattr(gizmo, 'gadget')
17 | except AttributeError:
18 | feature = None
19 | """
20 |
21 |
22 | class Gizmo:
23 | def __init__(self):
24 | self.gadget = True
25 |
26 | gizmo = Gizmo()
27 |
28 | test_keys = 'hasattr', 'getattr', 'tryget'
29 |
30 |
31 | def test():
32 | for test_key in test_keys:
33 | test_name = 'test_' + test_key
34 | test = globals()[test_name]
35 | setup = 'from __main__ import gizmo'
36 | t_present = min(timeit.repeat(test, setup=setup))
37 | del gizmo.gadget
38 | t_absent = min(timeit.repeat(test, setup=setup))
39 | gizmo.gadget = True
40 | print('{:7} {:.3f} {:.3f}'.format(test_key, t_present, t_absent))
41 |
42 | if __name__ == '__main__':
43 | test()
44 |
45 |
--------------------------------------------------------------------------------
/attic/classes/test_vector_spherical.py:
--------------------------------------------------------------------------------
1 | """
2 | Test spherical coordinates in ``Vector`` class
3 | """
4 |
5 | import sys
6 | from vector_v5 import Vector
7 |
8 | FIXTURE = 'spherical-coordinates.txt'
9 | EPSILON = 10**-8
10 |
11 | def parse_float_cells(cells):
12 | floats = []
13 | for cell in cells:
14 | try:
15 | floats.append(float(cell))
16 | except ValueError:
17 | continue
18 | return floats
19 |
20 | def load_fixture(verbose=False):
21 | with open(FIXTURE, encoding='utf8') as text:
22 | for line in text:
23 | if line.startswith('#'): # comment line
24 | continue
25 | cells = line.split('\t')
26 | cartesian = parse_float_cells(cells[:5])
27 | spherical = parse_float_cells(cells[5:])
28 | v = Vector(cartesian)
29 | if verbose:
30 | print(repr(v), '\t->', spherical)
31 | diff = abs(abs(v) - spherical[0])
32 | assert diff < EPSILON, 'expected {}, got {}'.format(spherical[0], abs(v))
33 | assert all(abs(av - af) < EPSILON for av, af in zip(v.angles(), spherical[1:])), (
34 | 'expected {}, got {}'.format(spherical[1:], list(v.angles())))
35 |
36 | if __name__=='__main__':
37 | load_fixture('-v' in sys.argv)
38 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/README.rst:
--------------------------------------------------------------------------------
1 | =========================================
2 | Setting up the test environment
3 | =========================================
4 |
5 | Some of the concurrency examples in this book require a local HTTP
6 | server. These instructions show how I setup Ngnix on GNU/Linux,
7 | Mac OS X 10.9 and Windows 7.
8 |
9 | Nginx setup on Mac OS X
10 | ========================
11 |
12 | Homebrew (copy & paste code at the bottom of http://brew.sh/)::
13 |
14 | $ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
15 | $ brew doctor
16 | $ brew install nginx
17 |
18 | Download and unpack::
19 |
20 | Docroot is: /usr/local/var/www
21 | /usr/local/etc/nginx/nginx.conf
22 |
23 | To have launchd start nginx at login:
24 | ln -sfv /usr/local/opt/nginx/*.plist ~/Library/LaunchAgents
25 | Then to load nginx now:
26 | launchctl load ~/Library/LaunchAgents/homebrew.mxcl.nginx.plist
27 | Or, if you don't want/need launchctl, you can just run:
28 | nginx
29 |
30 | Nginx setup on Lubuntu 14.04.1 LTS
31 | ==================================
32 |
33 | /usr/share/nginx/html
34 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/add_continent.py:
--------------------------------------------------------------------------------
1 | # Source for continent listings:
2 | # United Nations Statistics Division
3 | # http://unstats.un.org/unsd/cr/ctryreg/default.asp?Lg=1
4 |
5 | CONTINENTS = dict(AF='Africa',
6 | AS='Asia',
7 | EU='Europe',
8 | NA='North America',
9 | SA='South America',
10 | OC='Oceania')
11 |
12 | COUNTRY_CONTINENT = {}
13 |
14 | for cont_code, cont_name in CONTINENTS.items():
15 | cont_suffix = cont_name.lower().replace(' ', '_')
16 | with open('continent-' + cont_suffix + '.txt') as fp:
17 | for country in fp:
18 | COUNTRY_CONTINENT[country.strip()] = cont_code
19 |
20 | with open('country-codes.tab') as fp:
21 | for lin in fp:
22 | if lin.startswith('#'):
23 | continue
24 | lin = lin.strip()
25 | cc, gec, name = lin.split('\t')
26 | cont = COUNTRY_CONTINENT.get(name, '??')
27 | print(cc, gec, cont, name, sep='\t')
28 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/build_fixture.py:
--------------------------------------------------------------------------------
1 | """
2 | Build flags fixture
3 | """
4 |
5 | import shutil
6 | import os
7 | import json
8 |
9 | SRC = 'img/'
10 | DEST = 'fixture/'
11 |
12 | with open('country-codes.tab') as cc_fp:
13 | for line in cc_fp:
14 | if line.startswith('#'):
15 | continue
16 | iso_cc, gec_cc, name = line.strip().split('\t')
17 | print(iso_cc, name)
18 | cc = iso_cc.lower()
19 | img_name = cc + '.gif'
20 | from_file = os.path.join(SRC, img_name)
21 | to_path = os.path.join(DEST, cc)
22 | os.mkdir(to_path)
23 | to_file = os.path.join(to_path, img_name)
24 | shutil.copyfile(from_file, to_file)
25 | tld_cc = 'uk' if cc == 'gb' else cc
26 | metadata = {'country': name, 'iso_cc': iso_cc,
27 | 'tld_cc': '.'+tld_cc, 'gec_cc': gec_cc}
28 |
29 | with open(os.path.join(to_path, 'metadata.json'), 'wt') as json_fp:
30 | json.dump(metadata, json_fp, ensure_ascii=True)
31 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/build_fixture_with_continents.py:
--------------------------------------------------------------------------------
1 | """
2 | Build flags fixture
3 | """
4 |
5 | import shutil
6 | import os
7 | import json
8 |
9 | SRC = 'img/'
10 | DEST = 'fixture/'
11 | CONTINENTS = dict(AF='Africa',
12 | AS='Asia',
13 | EU='Europe',
14 | NA='North America',
15 | SA='South America',
16 | OC='Oceania')
17 |
18 | with open('countries-continents.tab') as cc_fp:
19 | for line in cc_fp:
20 | if line.startswith('#'):
21 | continue
22 | iso_cc, gec_cc, cont, name = line.strip().split('\t')
23 | print(iso_cc, name)
24 | cc = iso_cc.lower()
25 | img_name = cc + '.gif'
26 | from_file = os.path.join(SRC, img_name)
27 | to_path = os.path.join(DEST, cc)
28 | os.mkdir(to_path)
29 | to_file = os.path.join(to_path, img_name)
30 | shutil.copyfile(from_file, to_file)
31 | tld_cc = 'uk' if cc == 'gb' else cc
32 | metadata = {'country': name, 'continent':CONTINENTS[cont],
33 | 'iso_cc': iso_cc, 'tld_cc': '.'+tld_cc, 'gec_cc': gec_cc}
34 |
35 | with open(os.path.join(to_path, 'metadata.json'), 'wt') as json_fp:
36 | json.dump(metadata, json_fp, ensure_ascii=True)
37 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/cc_count.py:
--------------------------------------------------------------------------------
1 |
2 | from collections import Counter
3 | from operator import itemgetter
4 | from string import ascii_uppercase
5 |
6 | with open('country-codes.tab') as fp:
7 | ct = Counter()
8 | for line in fp:
9 | if line.startswith('#'):
10 | continue
11 | cc, _, _ = line.split('\t')
12 | ct[cc[0]] += 1
13 | print(cc, end=' ')
14 |
15 | for key, value in sorted(ct.items(), key=itemgetter(1), reverse=True):
16 | print(key, value)
17 |
18 | print('Total:', sum(ct.values()))
19 | print('Missing:', ', '.join(set(ascii_uppercase) - ct.keys()))
20 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/cc_tlds.py:
--------------------------------------------------------------------------------
1 | """
2 | Check country code TLDs
3 | """
4 |
5 | import shutil
6 | import os
7 | import json
8 |
9 | iso_cc_db = {}
10 |
11 | with open('country-codes.tab') as cc_fp:
12 | for line in cc_fp:
13 | if line.startswith('#'):
14 | continue
15 | iso_cc, gec_cc, name = line.strip().split('\t')
16 | iso_cc_db[iso_cc.lower()] = name
17 |
18 | tld_cc_db = {}
19 |
20 | with open('tlds.tab') as cc_fp:
21 | for line in cc_fp:
22 | if line.startswith('#'):
23 | continue
24 | tld_cc, category, entity = line.strip().split('\t')
25 | if category.strip() != 'country-code':
26 | continue
27 | if ascii(tld_cc) != repr(tld_cc):
28 | continue
29 | tld_cc_db[tld_cc[1:].strip()] = entity
30 |
31 | not_tld = iso_cc_db.keys() - tld_cc_db.keys()
32 | print(sorted(not_tld))
33 |
34 | for iso_cc, name in sorted(iso_cc_db.items()):
35 | entity = tld_cc_db[iso_cc]
36 | print('{}\t{}\t{}'.format(iso_cc, name, entity))
37 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/continents.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/attic/concurrency/flags/continents.zip
--------------------------------------------------------------------------------
/attic/concurrency/flags/count_colors.py:
--------------------------------------------------------------------------------
1 | import tkinter
2 |
3 | class Test:
4 | def __init__(self, master):
5 |
6 | canvas = tkinter.Canvas(master)
7 |
8 | canvas.image = tkinter.PhotoImage(file = 'img/br.gif')
9 | print(vars(canvas.image))
10 |
11 | canvas.create_image(0,0, image=canvas.image, anchor=tkinter.NW)
12 | canvas.bind('', self.right_click)
13 |
14 | canvas.grid(row=0, column=0)
15 |
16 | def right_click(self, event):
17 | print(vars(event))
18 | raise SystemExit()
19 |
20 | root = tkinter.Tk()
21 | test = Test(root)
22 | root.mainloop()
23 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/fixture.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/attic/concurrency/flags/fixture.tar.gz
--------------------------------------------------------------------------------
/attic/concurrency/flags/graphs.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/attic/concurrency/flags/graphs.ods
--------------------------------------------------------------------------------
/attic/concurrency/flags/img.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/attic/concurrency/flags/img.zip
--------------------------------------------------------------------------------
/attic/concurrency/flags/img/README.txt:
--------------------------------------------------------------------------------
1 | This file exists so that the directory is stored by git.
2 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/vaurien_delay.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | vaurien --protocol http --proxy localhost:8000 --backend localhost:8080 \
3 | --behavior 100:delay --behavior-delay-sleep 1
4 |
--------------------------------------------------------------------------------
/attic/concurrency/flags/vaurien_error_delay.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | vaurien --protocol http --proxy localhost:8000 --backend localhost:8080 \
3 | --behavior 50:error,50:delay --behavior-delay-sleep .5
4 |
--------------------------------------------------------------------------------
/attic/concurrency/parallel/lelo_ex.py:
--------------------------------------------------------------------------------
1 | import os
2 | from time import sleep, time
3 |
4 | from lelo import parallel
5 |
6 | DELAY = .2
7 |
8 | @parallel
9 | def loiter(serial, delay):
10 | pid = os.getpid()
11 | print('%2d pid = %d' % (serial, pid))
12 | sleep(delay)
13 | return pid
14 |
15 | t0 = time()
16 |
17 | results = []
18 | for i in range(15):
19 | res = loiter(i, DELAY)
20 | results.append(res)
21 |
22 | print('Processes used: ', list(set(results)))
23 |
24 | print('### Elapsed time: %0.2f' % (time() - t0))
25 |
--------------------------------------------------------------------------------
/attic/concurrency/parallel/llize.py:
--------------------------------------------------------------------------------
1 | import os
2 | from parallelize import parallelize
3 | from time import sleep, time
4 |
5 | print('one process:')
6 | t0 = time()
7 | for i in range(12):
8 | print('%2d pid = %d' % (i, os.getpid()))
9 | sleep(.2)
10 | print('elapsed time: %0.2f' % (time() - t0))
11 |
12 | print()
13 |
14 | print('several processes:')
15 | t0 = time()
16 | for i in parallelize(range(12)):
17 | print('%2d pid = %d' % (i, os.getpid()))
18 | sleep(.2)
19 | print('elapsed time: %0.2f' % (time() - t0))
20 |
--------------------------------------------------------------------------------
/attic/concurrency/parallel/llize_ex.py:
--------------------------------------------------------------------------------
1 | import os
2 | from time import sleep, time
3 |
4 | from parallelize import parallelize, per_item
5 |
6 | DELAY = .2
7 |
8 | def loiter(serial, delay):
9 | pid = os.getpid()
10 | print('%2d pid = %d' % (serial, pid))
11 | sleep(delay)
12 | return pid
13 |
14 | t0 = time()
15 |
16 | results = []
17 | for i in parallelize(range(15), fork=per_item):
18 | res = loiter(i, DELAY)
19 | results.append(res)
20 |
21 | print('Processes used: ', list(set(results)))
22 |
23 | print('### Elapsed time: %0.2f' % (time() - t0))
24 |
--------------------------------------------------------------------------------
/attic/concurrency/spinner_asyncio.py:
--------------------------------------------------------------------------------
1 | # spinner_asyncio.py
2 |
3 | # credits: Example by Luciano Ramalho inspired by
4 | # Michele Simionato's multiprocessing example
5 | # source:
6 | # http://python-3-patterns-idioms-test.readthedocs.org/en/latest/CoroutinesAndConcurrency.html
7 |
8 | import sys
9 | import asyncio
10 |
11 | DELAY = 0.1
12 | DISPLAY = '|/-\\'
13 |
14 | @asyncio.coroutine
15 | def spinner_func(before='', after=''):
16 | write, flush = sys.stdout.write, sys.stdout.flush
17 | while True:
18 | for char in DISPLAY:
19 | msg = '{} {} {}'.format(before, char, after)
20 | write(msg)
21 | flush()
22 | write('\x08' * len(msg))
23 | try:
24 | yield from asyncio.sleep(DELAY)
25 | except asyncio.CancelledError:
26 | return
27 |
28 |
29 | @asyncio.coroutine
30 | def long_computation(delay):
31 | # emulate a long computation
32 | yield from asyncio.sleep(delay)
33 |
34 |
35 | if __name__ == '__main__':
36 | loop = asyncio.get_event_loop()
37 | spinner = loop.create_task(spinner_func('Please wait...', 'thinking!'))
38 | long_task = loop.create_task(long_computation(3))
39 | long_task.add_done_callback(lambda f: spinner.cancel())
40 | loop.run_until_complete(spinner)
41 | loop.close()
42 |
43 |
--------------------------------------------------------------------------------
/attic/concurrency/spinner_asyncio2.py:
--------------------------------------------------------------------------------
1 | # spinner_asyncio2.py
2 |
3 | # credits: Example by Luciano Ramalho inspired by
4 | # Michele Simionato's multiprocessing example
5 | # source:
6 | # http://python-3-patterns-idioms-test.readthedocs.org/en/latest/CoroutinesAndConcurrency.html
7 |
8 | import sys
9 | import asyncio
10 |
11 | DELAY = 0.1
12 | DISPLAY = '|/-\\'
13 |
14 | @asyncio.coroutine
15 | def spinner_func(before='', after=''):
16 | write, flush = sys.stdout.write, sys.stdout.flush
17 | while True:
18 | for char in DISPLAY:
19 | msg = '{} {} {}'.format(before, char, after)
20 | write(msg)
21 | flush()
22 | write('\x08' * len(msg))
23 | try:
24 | yield from asyncio.sleep(DELAY)
25 | except asyncio.CancelledError:
26 | return
27 |
28 |
29 | @asyncio.coroutine
30 | def long_computation(delay):
31 | # emulate a long computation
32 | yield from asyncio.sleep(delay)
33 |
34 |
35 | @asyncio.coroutine
36 | def supervisor(delay):
37 | spinner = loop.create_task(spinner_func('Please wait...', 'thinking!'))
38 | yield from long_computation(delay)
39 | spinner.cancel()
40 |
41 |
42 | if __name__ == '__main__':
43 | loop = asyncio.get_event_loop()
44 | loop.run_until_complete(supervisor(3))
45 | loop.close()
46 |
47 |
--------------------------------------------------------------------------------
/attic/concurrency/spinner_proc.py:
--------------------------------------------------------------------------------
1 | # spinner_proc.py
2 | # credit: Example by Michele Simionato in comp lang python.
3 | # source:
4 | # http://python-3-patterns-idioms-test.readthedocs.org/en/latest/CoroutinesAndConcurrency.html
5 |
6 | import sys
7 | import time
8 | import multiprocessing
9 |
10 | DELAY = 0.1
11 | DISPLAY = '|/-\\'
12 |
13 |
14 | def spinner_func(before='', after=''):
15 | write, flush = sys.stdout.write, sys.stdout.flush
16 | while True:
17 | for char in DISPLAY:
18 | msg = '{} {} {}'.format(before, char, after)
19 | write(msg)
20 | flush()
21 | write('\x08' * len(msg))
22 | time.sleep(DELAY)
23 |
24 |
25 | def long_computation():
26 | # emulate a long computation
27 | time.sleep(3)
28 |
29 | if __name__ == '__main__':
30 | spinner = multiprocessing.Process(
31 | None, spinner_func, args=('Please wait ... ', ' thinking!'))
32 | spinner.start()
33 |
34 | try:
35 | long_computation()
36 | print('\nComputation done')
37 | finally:
38 | spinner.terminate()
39 |
--------------------------------------------------------------------------------
/attic/concurrency/spinner_thread.py:
--------------------------------------------------------------------------------
1 | # spinner_thread.py
2 | # adapted from spinner_proc.py to use threads
3 |
4 | import sys
5 | import time
6 | import threading
7 |
8 | DELAY = 0.1
9 | DISPLAY = '|/-\\'
10 |
11 |
12 | def spinner_func(before='', after=''):
13 | write, flush = sys.stdout.write, sys.stdout.flush
14 | while True:
15 | for char in DISPLAY:
16 | msg = '{} {} {}'.format(before, char, after)
17 | write(msg)
18 | flush()
19 | write('\x08' * len(msg))
20 | time.sleep(DELAY)
21 |
22 |
23 | def long_computation():
24 | # emulate a long computation
25 | time.sleep(3)
26 |
27 |
28 | if __name__ == '__main__':
29 | spinner = threading.Thread(
30 | None, spinner_func, args=('Please wait...', 'thinking!'))
31 | spinner.daemon = True
32 | spinner.start()
33 |
34 | long_computation()
35 | print('\nComputation done')
36 |
--------------------------------------------------------------------------------
/attic/concurrency/timer.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | @asyncio.coroutine
4 | def show_remaining():
5 | for remaining in range(5, 0, -1):
6 | print('Remaining: ', remaining)
7 | yield from asyncio.sleep(1)
8 |
9 | def main():
10 | loop = asyncio.get_event_loop()
11 | try:
12 | loop.run_until_complete(show_remaining())
13 | finally:
14 | loop.close()
15 |
16 | if __name__ == '__main__':
17 | main()
18 |
--------------------------------------------------------------------------------
/attic/concurrency/timer2.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import sys
3 | import contextlib
4 |
5 | @asyncio.coroutine
6 | def show_remaining(dots_task):
7 | remaining = 5
8 | while remaining:
9 | print('Remaining: ', remaining)
10 | sys.stdout.flush()
11 | yield from asyncio.sleep(1)
12 | remaining -= 1
13 | dots_task.cancel()
14 | print()
15 |
16 | @asyncio.coroutine
17 | def dots():
18 | while True:
19 | print('.', sep='', end='')
20 | sys.stdout.flush()
21 | yield from asyncio.sleep(.1)
22 |
23 | def main():
24 | with contextlib.closing(asyncio.get_event_loop()) as loop:
25 | dots_task = asyncio.Task(dots())
26 | coros = [show_remaining(dots_task), dots_task]
27 | loop.run_until_complete(asyncio.wait(coros))
28 |
29 | if __name__ == '__main__':
30 | main()
31 |
--------------------------------------------------------------------------------
/attic/concurrency/timer_cb.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 |
3 | def show_remaining(loop):
4 | if not hasattr(show_remaining, 'remaining'):
5 | show_remaining.remaining = 5
6 |
7 | print('Remaining: ', show_remaining.remaining)
8 | show_remaining.remaining -= 1
9 | if show_remaining.remaining:
10 | loop.call_later(1, show_remaining, loop)
11 | else:
12 | loop.stop()
13 |
14 | def main():
15 | loop = asyncio.get_event_loop()
16 | try:
17 | loop.call_soon(show_remaining, loop)
18 | loop.run_forever()
19 | finally:
20 | loop.close()
21 |
22 | if __name__ == '__main__':
23 | main()
24 |
--------------------------------------------------------------------------------
/attic/concurrency/timer_clo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import asyncio
3 |
4 | def make_show_remaining(seconds):
5 | remaining = seconds
6 |
7 | def show_remaining(loop):
8 | nonlocal remaining
9 | print('Remaining: ', remaining)
10 | remaining -= 1
11 | if remaining:
12 | loop.call_later(1, show_remaining, loop)
13 | else:
14 | loop.stop()
15 |
16 | return show_remaining
17 |
18 |
19 | def main(seconds=5):
20 | seconds = int(seconds)
21 | loop = asyncio.get_event_loop()
22 | try:
23 | loop.call_soon(make_show_remaining(seconds), loop)
24 | loop.run_forever()
25 | finally:
26 | loop.close()
27 |
28 | if __name__ == '__main__':
29 | main(*sys.argv[1:])
30 |
--------------------------------------------------------------------------------
/attic/concurrency/timer_seq.py:
--------------------------------------------------------------------------------
1 | import time
2 |
3 | def main():
4 | for remaining in range(5, 0, -1):
5 | print('Remaining: ', remaining)
6 |
7 | if __name__ == '__main__':
8 | main()
9 |
--------------------------------------------------------------------------------
/attic/concurrency/wikipedia/daypicts_threads.py:
--------------------------------------------------------------------------------
1 | """
2 | Wikipedia Picture of the Day (POTD) download example
3 | """
4 |
5 | import sys
6 | from concurrent import futures
7 |
8 | from daypicts import main, get_picture_url, NoPictureForDate
9 |
10 | GLOBAL_TIMEOUT = 300 # seconds
11 | MAX_CONCURRENT_REQUESTS = 30
12 |
13 |
14 | def get_picture_urls(dates, verbose=False):
15 | pool = futures.ThreadPoolExecutor(MAX_CONCURRENT_REQUESTS)
16 |
17 | pending = {}
18 | for date in dates:
19 | job = pool.submit(get_picture_url, date)
20 | pending[job] = date
21 |
22 | urls = []
23 | count = 0
24 |
25 | # get results as jobs are done
26 | for job in futures.as_completed(pending, timeout=GLOBAL_TIMEOUT):
27 | try:
28 | url = job.result()
29 | except NoPictureForDate as exc:
30 | if verbose:
31 | print('*** {!r} ***'.format(exc))
32 | continue
33 | count += 1
34 | if verbose:
35 | print(format(count, '3d'), end=' ')
36 | print(url.split('/')[-1])
37 | else:
38 | print(url)
39 | urls.append(url)
40 | return urls
41 |
42 |
43 | if __name__ == '__main__':
44 | main(sys.argv[1:], get_picture_urls)
45 |
--------------------------------------------------------------------------------
/attic/concurrency/wikipedia/delay.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | vaurien --protocol http --proxy localhost:8002 --backend localhost:8001 \
3 | --behavior 100:delay --behavior-delay-sleep .1
--------------------------------------------------------------------------------
/attic/concurrency/wikipedia/fast_tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # run tests skipping @pytest.mark.network
4 | py.test test_daypicts.py -m 'not network' $1 $2 $3
5 |
--------------------------------------------------------------------------------
/attic/concurrency/wikipedia/fixture/docroot.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/attic/concurrency/wikipedia/fixture/docroot.zip
--------------------------------------------------------------------------------
/attic/concurrency/wikipedia/orig/README.rst:
--------------------------------------------------------------------------------
1 | =====================================
2 | Wikipedia Picture of the Day examples
3 | =====================================
4 |
5 | These examples use various asynchronous programming techniques to download
6 | images and metadata from the English Wikipedia `Picture of the Day`_ archive.
7 |
8 | .. _Picture of the Day: http://en.wikipedia.org/wiki/Wikipedia:Picture_of_the_day/Archive
9 |
10 |
11 | --------
12 | Timings
13 | --------
14 |
15 | ``sync.py``
16 | ===========
17 |
18 | ::
19 |
20 | $ time python sync.py 2014-06 -q 5
21 | 5 images downloaded (167.8 Kbytes total)
22 |
23 | real 0m6.272s
24 | user 0m0.065s
25 | sys 0m0.039s
26 |
27 | $ time python sync.py 2014-06 -q 5
28 | 5 images downloaded (167.8 Kbytes total)
29 |
30 | real 0m5.447s
31 | user 0m0.068s
32 | sys 0m0.040s
33 |
34 | $ time python sync.py 2014-06 -q 5
35 | 5 images downloaded (167.8 Kbytes total)
36 |
37 | real 0m6.314s
38 | user 0m0.068s
39 | sys 0m0.040s
40 |
--------------------------------------------------------------------------------
/attic/concurrency/wikipedia/orig/futureprocs.py:
--------------------------------------------------------------------------------
1 | """
2 | Wikipedia Picture of the Day (POTD) download example
3 |
4 | Inspired by example at:
5 | https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor-example
6 | """
7 |
8 | from concurrent import futures
9 |
10 | import potd
11 |
12 | def save_month(year_month, verbose):
13 | year, month = [int(s) for s in year_month.split('-')]
14 | total_size = 0
15 | img_count = 0
16 | dates = potd.list_days_of_month(year, month)
17 |
18 | with futures.ProcessPoolExecutor(max_workers=100) as executor:
19 | downloads = dict((executor.submit(potd.save_one, date, verbose), date)
20 | for date in dates)
21 |
22 | for future in futures.as_completed(downloads):
23 | date = downloads[future]
24 | if future.exception() is not None:
25 | print('%r generated an exception: %s' % (date,
26 | future.exception()))
27 | else:
28 | img_size = future.result()
29 | total_size += img_size
30 | img_count += 1
31 | print('%r OK: %r' % (date, img_size))
32 |
33 | return img_count, total_size
34 |
35 | if __name__ == '__main__':
36 | potd.main(save_month=save_month)
37 |
--------------------------------------------------------------------------------
/attic/concurrency/wikipedia/orig/futurethreads.py:
--------------------------------------------------------------------------------
1 | """
2 | Wikipedia Picture of the Day (POTD) download example
3 |
4 | Inspired by example at:
5 | https://docs.python.org/3/library/concurrent.futures.html#threadpoolexecutor-example
6 | """
7 |
8 | from concurrent import futures
9 |
10 | import potd
11 |
12 | def save_month(year_month, verbose):
13 | year, month = [int(s) for s in year_month.split('-')]
14 | total_size = 0
15 | img_count = 0
16 | dates = potd.list_days_of_month(year, month)
17 |
18 | with futures.ThreadPoolExecutor(max_workers=100) as executor:
19 | downloads = dict((executor.submit(potd.save_one, date, verbose), date)
20 | for date in dates)
21 |
22 | for future in futures.as_completed(downloads):
23 | date = downloads[future]
24 | if future.exception() is not None:
25 | print('%r generated an exception: %s' % (date,
26 | future.exception()))
27 | else:
28 | img_size = future.result()
29 | total_size += img_size
30 | img_count += 1
31 | print('%r OK: %r' % (date, img_size))
32 |
33 | return img_count, total_size
34 |
35 | if __name__ == '__main__':
36 | potd.main(save_month=save_month)
37 |
--------------------------------------------------------------------------------
/attic/control/adder/coroadder0.py:
--------------------------------------------------------------------------------
1 | """
2 | Closing a generator raises ``GeneratorExit`` at the pending ``yield``
3 |
4 | >>> adder = adder_coro()
5 | >>> next(adder)
6 | 0
7 | >>> adder.send(10)
8 | 10
9 | >>> adder.send(20)
10 | 30
11 | >>> adder.send(30)
12 | 60
13 | >>> adder.close()
14 | -> total: 60 terms: 3 average: 20.0
15 |
16 |
17 | Other exceptions propagate to the caller:
18 |
19 | >>> adder = adder_coro()
20 | >>> next(adder)
21 | 0
22 | >>> adder.send(10)
23 | 10
24 | >>> adder.send('spam')
25 | Traceback (most recent call last):
26 | ...
27 | TypeError: unsupported operand type(s) for +=: 'int' and 'str'
28 |
29 |
30 | """
31 |
32 | def adder_coro(initial=0):
33 | total = initial
34 | count = 0
35 | try:
36 | while True:
37 | term = yield total
38 | total += term
39 | count += 1
40 | except GeneratorExit:
41 | average = total / count
42 | msg = '-> total: {} terms: {} average: {}'
43 | print(msg.format(total, count, average))
44 |
--------------------------------------------------------------------------------
/attic/control/adder/soma.py:
--------------------------------------------------------------------------------
1 |
2 | if 'raw_input' in dir(__builtins__):
3 | input = raw_input # para funcionar com Python 2
4 |
5 | def ler_num():
6 | num = input('+: ')
7 | try:
8 | num = float(num)
9 | except ValueError:
10 | return 0
11 | return num
12 |
13 | def somadora():
14 | qt_parcelas = 0
15 | total = 0
16 | try:
17 | while True:
18 | parcela = yield
19 | qt_parcelas += 1
20 | total += parcela
21 | print('parcelas: %d total: %d' % (qt_parcelas, total))
22 |
23 | finally:
24 | print('parcelas: %d total: %d media: %d' % (qt_parcelas, total, total/qt_parcelas))
25 |
26 | def main():
27 | coro = somadora()
28 | next(coro)
29 | while True:
30 | item = ler_num()
31 | if item:
32 | coro.send(item)
33 | else:
34 | print('Fechando corotina...')
35 | coro.close()
36 | break
37 |
38 | if __name__=='__main__':
39 | main()
40 |
--------------------------------------------------------------------------------
/attic/control/adder/soma_deco.py:
--------------------------------------------------------------------------------
1 |
2 | if 'raw_input' in dir(__builtins__):
3 | input = raw_input # para funcionar com Python 2
4 |
5 | def ler_parcela():
6 | parcela = input('+: ')
7 | try:
8 | parcela = float(parcela)
9 | except ValueError:
10 | return 0
11 | return parcela
12 |
13 | # decorator
14 | def coro(func):
15 | def start(*args, **kwargs):
16 | g = func(*args, **kwargs)
17 | next(g)
18 | return g
19 | return start
20 |
21 | @coro
22 | def somadora():
23 | qt_parcelas = 0
24 | total = 0
25 | try:
26 | while True:
27 | parcela = yield
28 | qt_parcelas += 1
29 | total += parcela
30 |
31 | print('parcelas: %d total: %d' % (qt_parcelas, total))
32 | finally:
33 | print('parcelas: %d total: %d media: %d' % (qt_parcelas, total, total/qt_parcelas))
34 |
35 | def main():
36 | coro = somadora()
37 | while True:
38 | parcela = ler_parcela()
39 | if parcela:
40 | coro.send(parcela)
41 | else:
42 | print('Fechando corotina...')
43 | coro.close()
44 | break
45 |
46 | if __name__=='__main__':
47 | main()
48 |
--------------------------------------------------------------------------------
/attic/control/adder/yetanother.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import collections
3 |
4 | Result = collections.namedtuple('Result', 'total average')
5 |
6 | def adder():
7 | total = 0
8 | count = 0
9 | while True:
10 | term = yield
11 | try:
12 | term = float(term)
13 | except (ValueError, TypeError):
14 | break
15 | else:
16 | total += term
17 | count += 1
18 | return Result(total, total/count)
19 |
20 | def process_args(coro, args):
21 | for arg in args:
22 | coro.send(arg)
23 | try:
24 | next(coro)
25 | except StopIteration as exc:
26 | return exc.value
27 |
28 |
29 | def prompt(coro):
30 | while True:
31 | term = input('+> ')
32 | try:
33 | coro.send(term)
34 | except StopIteration as exc:
35 | return exc.value
36 |
37 |
38 | def main():
39 | coro = adder()
40 | next(coro) # prime it
41 | if len(sys.argv) > 1:
42 | res = process_args(coro, sys.argv[1:])
43 | else:
44 | res = prompt(coro)
45 | print(res)
46 |
47 | main()
48 |
--------------------------------------------------------------------------------
/attic/control/adder/yield_from_input.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 |
4 | def ask():
5 | prompt = '>'
6 | while True:
7 | response = input(prompt)
8 | if not response:
9 | return 0
10 | yield response
11 |
12 |
13 | def parse_args():
14 | yield from iter(sys.argv[1:])
15 |
16 |
17 | def fetch(producer):
18 | gen = producer()
19 | next(gen)
20 | yield from gen
21 |
22 |
23 | def main(args):
24 | if args:
25 | producer = parse_args
26 | else:
27 | producer = ask
28 |
29 | total = 0
30 | count = 0
31 | gen = fetch(producer())
32 | while True:
33 | term = yield from gen
34 | term = float(term)
35 | total += term
36 | count += 1
37 | average = total / count
38 | print('total: {} average: {}'.format(total, average))
39 |
40 |
41 | if __name__ == '__main__':
42 | main(sys.argv[1:])
43 |
--------------------------------------------------------------------------------
/attic/control/coro_demo.rst:
--------------------------------------------------------------------------------
1 | >>> def coroutine():
2 | ... print('coroutine started')
3 | ... x = yield
4 | ... print('coroutine received: {!r}'.format(x))
5 | ...
6 | >>> coro = coroutine()
7 | >>> next(coro)
8 | coroutine started
9 | >>> coro.send(42)
10 | coroutine received: 42
11 | Traceback (most recent call last):
12 | ...
13 | StopIteration
14 |
--------------------------------------------------------------------------------
/attic/control/coro_simple_demo.rst:
--------------------------------------------------------------------------------
1 | >>> def coroutine():
2 | ... print('coroutine started')
3 | ... x = yield
4 | ... print('coroutine received: {!r}'.format(x))
5 | ...
6 | >>> coro = coroutine()
7 | >>> next(coro)
8 | coroutine started
9 | >>> coro.send(42)
10 | coroutine received: 42
11 | Traceback (most recent call last):
12 | ...
13 | StopIteration
14 |
--------------------------------------------------------------------------------
/attic/control/coroaverager.py:
--------------------------------------------------------------------------------
1 | """
2 | Closing a generator raises ``GeneratorExit`` at the pending ``yield``
3 |
4 | >>> coro_avg = averager()
5 | >>> next(coro_avg)
6 | 0.0
7 | >>> coro_avg.send(10)
8 | 10.0
9 | >>> coro_avg.send(20)
10 | 15.0
11 | >>> coro_avg.send(30)
12 | 20.0
13 | >>> coro_avg.close()
14 | -> total: 60.0 average: 20.0 terms: 3
15 |
16 |
17 | Other exceptions propagate to the caller:
18 |
19 | >>> coro_avg = averager()
20 | >>> next(coro_avg)
21 | 0.0
22 | >>> coro_avg.send(10)
23 | 10.0
24 | >>> coro_avg.send('spam')
25 | Traceback (most recent call last):
26 | ...
27 | TypeError: unsupported operand type(s) for +=: 'float' and 'str'
28 |
29 |
30 | """
31 |
32 | # BEGIN CORO_AVERAGER
33 | def averager():
34 | total = average = 0.0
35 | count = 0
36 | try:
37 | while True:
38 | term = yield average
39 | total += term
40 | count += 1
41 | average = total/count
42 | except GeneratorExit:
43 | msg = '-> total: {} average: {} terms: {}'
44 | print(msg.format(total, average, count))
45 | # END CORO_AVERAGER
46 |
--------------------------------------------------------------------------------
/attic/control/countdown_yf.py:
--------------------------------------------------------------------------------
1 | from time import sleep
2 |
3 | def countdown(n):
4 | while n:
5 | print('\tn ->', n)
6 | yield n
7 | n -= 1
8 | sleep(1)
9 |
10 | def foo():
11 | for i in range(6, 3, -1):
12 | yield i
13 | yield from countdown(3)
14 |
15 | #for j in foo():
16 | # print('j ->', j)
17 |
18 |
19 | def squares(n):
20 | yield from [i for i in range(n)]
21 | yield from [i*i for i in range(n)]
22 |
23 | def squares_stupid(n):
24 | for i in range(n):
25 | yield i
26 |
27 | for i in range(n):
28 | yield i*i
29 |
30 | #for s in squares(10):
31 | # print(s)
32 |
33 |
34 | def tokenize():
35 | while True:
36 | source = input('> ')
37 | try:
38 | obj = eval(source)
39 | except BaseException:
40 | print('*crash*')
41 | return
42 | try:
43 | it = iter(obj)
44 | except TypeError:
45 | yield obj
46 | return
47 | else:
48 | yield from it
49 |
50 | #g = tokenize()
51 |
52 | #for res in g:
53 | # print(res)
54 |
55 |
56 | from concurrent.futures import Future
57 |
58 | def f():
59 | f = future()
60 |
61 | def foo(fut):
62 | print(fut, fut.result())
63 | f = Future()
64 | f.add_done_callback(foo)
65 | f.set_result(42)
66 |
67 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/attic/control/demo_coro.py:
--------------------------------------------------------------------------------
1 | >>> def coro():
2 | ... print 'iniciando corotina...'
3 | ... while True:
4 | ... x = yield
5 | ... print 'recebido: ', x
6 | ... if x == -1: break
7 | ... print 'terminando corotina.'
8 | ...
9 | >>> c = coro()
10 | >>> next(c)
11 | iniciando corotina...
12 | >>> c.send(7)
13 | recebido: 7
14 | >>> c.send(3)
15 | recebido: 3
16 | >>> c.send(10)
17 | recebido: 10
18 | >>> c.send(-1)
19 | recebido: -1
20 | terminando corotina.
21 | Traceback (most recent call last):
22 | File "", line 1, in
23 | StopIteration
24 | >>>
25 |
--------------------------------------------------------------------------------
/attic/control/exemplo0.py:
--------------------------------------------------------------------------------
1 | def corrotina():
2 | print('\t(corrotina) inciciando...')
3 | x = yield
4 | print('\t(corrotina) recebeu x: %r' % x)
5 | y = yield
6 | print('\t(corrotina) recebeu y: %r' % y)
7 | print('\t(corrotina) terminando.')
8 |
9 |
10 | def principal():
11 | print('(principal) iniciando...')
12 | co = corrotina()
13 | print('(principal) invocando next(co)...')
14 | next(co)
15 | print('(principal) invocando co.send(88)...')
16 | co.send(88)
17 | try:
18 | print('(principal) invocando co.send(99)...')
19 | co.send(99)
20 | # o print a seguir nunca vai acontecer
21 | print('(principal) invocado co.send(99)')
22 | except StopIteration:
23 | print('(principal) a corotina nao tem mais valores a produzir')
24 |
25 | principal()
26 |
--------------------------------------------------------------------------------
/attic/control/exemplo1.py:
--------------------------------------------------------------------------------
1 | def corrotina():
2 | print('\t(corrotina) inciciando...')
3 | x = yield 1
4 | print('\t(corrotina) recebeu x: %r' % x)
5 | y = yield 2
6 | print('\t(corrotina) recebeu y: %r' % y)
7 | print('\t(corrotina) terminando.')
8 |
9 |
10 | def principal():
11 | print('(principal) iniciando...')
12 | co = corrotina()
13 | print('(principal) invocando next(co)...')
14 | res = next(co)
15 | print('(principal) produzido por next(co): %r' % res)
16 | print('(principal) invocando co.send(88)...')
17 | res2 = co.send(88)
18 | print('(principal) produzido por co.send(88): %r' % res2)
19 | try:
20 | print('(principal) invocando co.send(99)...')
21 | res3 = co.send(99)
22 | # o print a seguir nunca vai acontecer
23 | print('(principal) produzido por co.send(99): %r' % res3)
24 | except StopIteration:
25 | print('(principal) a corotina nao tem mais valores a produzir')
26 | principal()
27 |
28 |
--------------------------------------------------------------------------------
/attic/control/flatten.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | >>> items = [1, 2, [3, 4, [5, 6], 7], 8]
4 | >>> flatten(items)
5 |
6 | >>> list(flatten(items))
7 | [1, 2, 3, 4, 5, 6, 7, 8]
8 | >>> mixed_bag = [1, 'spam', 2, [3, 'eggs', 4], {'x': 1, 'y': 2}]
9 | >>> list(flatten(mixed_bag))
10 | [1, 'spam', 2, 3, 'eggs', 4, 'y', 'x']
11 | """
12 |
13 |
14 | from collections import Iterable
15 |
16 | def flatten(items):
17 | for x in items:
18 | if isinstance(x, Iterable) and not isinstance(x, (str, bytes)):
19 | yield from flatten(x)
20 | else:
21 | yield x
22 |
--------------------------------------------------------------------------------
/attic/control/guido/guido0.py:
--------------------------------------------------------------------------------
1 | """
2 | Exemplo adaptado da mensagem do Guido van Rossum em:
3 | https://groups.google.com/forum/#!msg/python-tulip/bmphRrryuFk/aB45sEJUomYJ
4 | http://bit.ly/yieldfrom
5 |
6 | >>> principal(ger1())
7 | OK
8 | 42
9 |
10 | Visualização no PythonTutor: http://goo.gl/FQWq2F
11 |
12 | """
13 |
14 | def ger1():
15 | val = yield 'OK'
16 | print(val)
17 | yield # para evitar o StopIteration
18 |
19 | def principal(g):
20 | print(next(g))
21 | g.send(42)
22 |
23 |
24 | # auto-teste
25 | import doctest
26 | doctest.testmod()
27 |
--------------------------------------------------------------------------------
/attic/control/guido/guido1.py:
--------------------------------------------------------------------------------
1 | """
2 | Exemplo adaptado da mensagem do Guido van Rossum em:
3 | https://groups.google.com/forum/#!msg/python-tulip/bmphRrryuFk/aB45sEJUomYJ
4 | http://bit.ly/yieldfrom
5 |
6 | >>> principal(ger2())
7 | OK
8 | 42
9 |
10 | Visualização no PythonTutor: http://goo.gl/pWrlkm
11 |
12 | """
13 |
14 | def ger1():
15 | val = yield 'OK'
16 | print(val)
17 | yield # para evitar o StopIteration
18 |
19 | def ger2():
20 | yield from ger1()
21 |
22 | def principal(g):
23 | print(next(g))
24 | g.send(42)
25 |
26 |
27 | # auto-teste
28 | import doctest
29 | doctest.testmod()
30 |
--------------------------------------------------------------------------------
/attic/control/guido/guido1b.py:
--------------------------------------------------------------------------------
1 | """
2 | Exemplo adaptado da mensagem do Guido van Rossum em:
3 | https://groups.google.com/forum/#!msg/python-tulip/bmphRrryuFk/aB45sEJUomYJ
4 | http://bit.ly/yieldfrom
5 |
6 | >>> principal(ger2())
7 | OK
8 | None
9 |
10 | Visualização no PythonTutor: http://goo.gl/61CUcA
11 |
12 | """
13 |
14 | def ger1():
15 | val = yield 'OK'
16 | print(val)
17 | yield # para evitar o StopIteration
18 |
19 | def ger2():
20 | for i in ger1():
21 | yield i
22 |
23 | def principal(g):
24 | print(next(g))
25 | g.send(42)
26 |
27 |
28 | # auto-teste
29 | import doctest
30 | doctest.testmod()
31 |
--------------------------------------------------------------------------------
/attic/control/guido/guido2.py:
--------------------------------------------------------------------------------
1 | """
2 | Exemplo adaptado da mensagem do Guido van Rossum em:
3 | https://groups.google.com/forum/#!msg/python-tulip/bmphRrryuFk/aB45sEJUomYJ
4 | http://bit.ly/yieldfrom
5 |
6 | >>> principal_susto(ger1())
7 | OK
8 | Bu!
9 |
10 | Visualização no PythonTutor: http://goo.gl/m6p2Bc
11 |
12 | """
13 |
14 | def ger1():
15 | try:
16 | val = yield 'OK'
17 | except RuntimeError as exc:
18 | print(exc)
19 | else:
20 | print(val)
21 | yield # para evitar o StopIteration
22 |
23 |
24 | def principal_susto(g):
25 | print(next(g))
26 | g.throw(RuntimeError('Bu!'))
27 |
28 |
29 | # auto-teste
30 | import doctest
31 | doctest.testmod()
32 |
--------------------------------------------------------------------------------
/attic/control/guido/guido3.py:
--------------------------------------------------------------------------------
1 | """
2 | Exemplo adaptado da mensagem do Guido van Rossum em:
3 | https://groups.google.com/forum/#!msg/python-tulip/bmphRrryuFk/aB45sEJUomYJ
4 | http://bit.ly/yieldfrom
5 |
6 | >>> principal_susto(ger2())
7 | OK
8 | Bu!
9 |
10 | Visualização no PythonTutor: http://goo.gl/QXzQHS
11 |
12 | """
13 |
14 | def ger1():
15 | try:
16 | val = yield 'OK'
17 | except RuntimeError as exc:
18 | print(exc)
19 | else:
20 | print(val)
21 | yield # para evitar o StopIteration
22 |
23 |
24 | def ger2():
25 | yield from ger1()
26 |
27 |
28 | def principal_susto(g):
29 | print(next(g))
30 | g.throw(RuntimeError('Bu!'))
31 |
32 |
33 | # auto-teste
34 | import doctest
35 | doctest.testmod()
36 |
--------------------------------------------------------------------------------
/attic/control/http_cli0.py:
--------------------------------------------------------------------------------
1 | # adaptado de:
2 | # https://github.com/feihong/tulip-talk/blob/master/examples/2-tulip-download.py
3 |
4 | import asyncio
5 | import aiohttp
6 |
7 | @asyncio.coroutine
8 | def download(url):
9 | response = yield from aiohttp.request('GET', url)
10 | for k, v in response.items():
11 | print('{}: {}'.format(k, v[:80]))
12 |
13 | data = yield from response.read()
14 | print('\nReceived {} bytes.\n'.format(len(data)))
15 |
16 | if __name__ == '__main__':
17 | loop = asyncio.get_event_loop()
18 | url = 'https://www.cia.gov/library/publications/the-world-factbook/geos/br.html'
19 | coroutine = download(url)
20 | loop.run_until_complete(coroutine)
21 |
--------------------------------------------------------------------------------
/attic/control/kwcombos.py:
--------------------------------------------------------------------------------
1 | from keyword import kwlist
2 | from itertools import combinations
3 |
4 | for combo in combinations(kwlist, 2):
5 | print(*combo)
6 |
--------------------------------------------------------------------------------
/attic/decorators/average_broken.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> avg = make_averager()
3 | >>> avg(10)
4 | Traceback (most recent call last):
5 | ...
6 | UnboundLocalError: local variable 'num_items' referenced before assignment
7 |
8 | """
9 |
10 |
11 | def make_averager():
12 | num_items = 0
13 | total = 0
14 |
15 | def averager(new_value):
16 | num_items += 1
17 | total += new_value
18 | return total / num_items
19 |
20 | return averager
21 |
--------------------------------------------------------------------------------
/attic/decorators/average_fixed.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> avg = make_averager()
3 | >>> other_avg = make_averager()
4 | >>> avg(10)
5 | 10.0
6 | >>> avg(11)
7 | 10.5
8 | >>> avg(12)
9 | 11.0
10 | >>> avg.__code__.co_varnames
11 | ('new_value',)
12 | >>> avg.__code__.co_freevars
13 | ('num_items', 'total')
14 | >>> avg.__closure__ # doctest: +ELLIPSIS
15 | (, )
16 | >>> avg.__closure__[0].cell_contents
17 | 3
18 | >>> avg.__closure__[1].cell_contents
19 | 33
20 | >>> other_avg(5)
21 | 5.0
22 | >>> other_avg(10)
23 | 7.5
24 | >>> other_avg(15)
25 | 10.0
26 | """
27 |
28 | DEMO = """
29 | >>> avg.__closure__
30 | (,
31 | )
32 | """
33 |
34 |
35 | def make_averager():
36 | num_items = 0
37 | total = 0
38 |
39 | def averager(new_value):
40 | nonlocal num_items, total
41 | num_items += 1
42 | total += new_value
43 | return total / num_items
44 |
45 | return averager
46 |
--------------------------------------------------------------------------------
/attic/decorators/average_fixed_py2.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> avg = make_averager()
3 | >>> avg(10)
4 | 10.0
5 | >>> avg(11)
6 | 10.5
7 | >>> avg(12)
8 | 11.0
9 | >>> avg.__code__.co_varnames
10 | ('new_value',)
11 | >>> avg.__code__.co_freevars
12 | ('ns',)
13 | >>> avg.__closure__ # doctest: +ELLIPSIS
14 | (,)
15 | >>> avg.__closure__[0].cell_contents.__dict__
16 | {'total': 33, 'num_items': 3}
17 | """
18 |
19 | DEMO = """
20 | >>> avg.__closure__
21 | (,)
22 | """
23 |
24 |
25 | class Namespace(object):
26 | pass
27 |
28 |
29 | def make_averager():
30 | ns = Namespace()
31 | ns.num_items = 0
32 | ns.total = 0
33 |
34 | def averager(new_value):
35 | ns.num_items += 1
36 | ns.total += new_value
37 | return float(ns.total) / ns.num_items
38 |
39 | return averager
40 |
--------------------------------------------------------------------------------
/attic/decorators/average_partial.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> import functools
3 | >>> avg = functools.partial(averager, series=[])
4 | >>> avg(10)
5 | 10.0
6 | >>> avg(11)
7 | 10.5
8 | >>> avg(12)
9 | 11.0
10 | >>> avg.args
11 | ()
12 | >>> avg.keywords
13 | {'series': [10, 11, 12]}
14 | >>> avg.func # doctest: +ELLIPSIS
15 |
16 | >>> avg.func.__code__.co_varnames
17 | ('new_value', 'series', 'total')
18 | """
19 |
20 | DEMO = """
21 | >>> avg.func
22 |
23 | >>> avg.func.__code__.co_varnames
24 | ('new_value',)
25 | >>> avg.__code__.co_freevars
26 | ('num_items', 'total')
27 | >>> avg.__closure__
28 | """
29 |
30 | def averager(new_value, series):
31 | series.append(new_value)
32 | total = sum(series)
33 | return float(total)/len(series)
34 |
35 |
--------------------------------------------------------------------------------
/attic/decorators/average_py2.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> avg = make_averager()
3 | >>> avg(10)
4 | 10.0
5 | >>> avg(11)
6 | 10.5
7 | >>> avg(12)
8 | 11.0
9 | >>> avg.__code__.co_varnames
10 | ('new_value', 'total')
11 | >>> avg.__code__.co_freevars
12 | ('series',)
13 | >>> avg.__closure__ # doctest: +ELLIPSIS
14 | (,)
15 | >>> avg.__closure__[0].cell_contents
16 | [10, 11, 12]
17 | """
18 |
19 |
20 | def make_averager():
21 | series = []
22 |
23 | def averager(new_value):
24 | series.append(new_value)
25 | total = sum(series)
26 | return float(total)/len(series)
27 |
28 | return averager
29 |
--------------------------------------------------------------------------------
/attic/decorators/clockdeco2.py:
--------------------------------------------------------------------------------
1 | # clockdeco2.py
2 |
3 | import time
4 | import functools
5 |
6 |
7 | def clock(func):
8 | @functools.wraps(func)
9 | def clocked(*args, **kwargs):
10 | t0 = time.time()
11 | result = func(*args, **kwargs)
12 | elapsed = time.time() - t0
13 | name = func.__name__
14 | arg_lst = []
15 | if args:
16 | arg_lst.append(', '.join(repr(arg) for arg in args))
17 | if kwargs:
18 | pairs = ['%s=%r' % (k, w) for k, w in sorted(kwargs.items())]
19 | arg_lst.append(', '.join(pairs))
20 | arg_str = ', '.join(arg_lst)
21 | print('[%0.8fs] %s(%s) -> %r ' % (elapsed, name, arg_str, result))
22 | return result
23 | return clocked
24 |
--------------------------------------------------------------------------------
/attic/decorators/clockdeco2_demo.py:
--------------------------------------------------------------------------------
1 | # clockdec2o_demo.py
2 |
3 | """
4 | >>> pythagoras(3, 4) # doctest: +ELLIPSIS
5 | [0.0...s] pythagoras(3, 4) -> 5.0
6 | 5.0
7 | >>> pythagoras(9, h=15) # doctest: +ELLIPSIS
8 | [0.0...s] pythagoras(9, h=15) -> 12.0
9 | 12.0
10 |
11 | """
12 | import time
13 | import math
14 | from clockdeco2 import clock
15 |
16 |
17 | @clock
18 | def pythagoras(a, b=None, h=None):
19 | if b is None and h is None:
20 | raise TypeError('must provide second leg (b) or hypotenuse (h)')
21 | if h is None:
22 | return math.sqrt(a*a + b*b)
23 | else:
24 | return math.sqrt(h*h - a*a)
25 |
26 |
27 | if __name__=='__main__':
28 | print('*' * 40, 'Calling pythagoras(3, 4)')
29 | pythagoras(3, 4)
30 | print('*' * 40, 'Calling pythagoras(9, h=15)')
31 | pythagoras(9, h=15)
32 |
--------------------------------------------------------------------------------
/attic/decorators/clockdeco_demo2.py:
--------------------------------------------------------------------------------
1 | from clockdeco import clock
2 |
3 | import time
4 |
5 | @clock
6 | def snooze(milis):
7 | time.sleep(milis/1000)
8 |
9 | @clock
10 | def factorial(n):
11 | return 1 if n < 2 else n*factorial(n-1)
12 |
13 |
14 | @clock
15 | def fibonacci(n):
16 | if n < 2:
17 | return n
18 | return fibonacci(n-2) + fibonacci(n-1)
19 |
20 | snooze(123)
21 | print(factorial(6))
22 | print(fibonacci(4))
23 |
--------------------------------------------------------------------------------
/attic/decorators/clockdeco_demo3.py:
--------------------------------------------------------------------------------
1 | import functools
2 | from clockdeco import clock
3 |
4 |
5 | @functools.lru_cache()
6 | @clock
7 | def factorial(n):
8 | return 1 if n < 2 else n*factorial(n-1)
9 |
10 |
11 | @functools.lru_cache()
12 | @clock
13 | def fibonacci(n):
14 | if n < 2:
15 | return n
16 | return fibonacci(n-2) + fibonacci(n-1)
17 |
18 |
19 | print(factorial(6))
20 | print(fibonacci(6))
21 |
--------------------------------------------------------------------------------
/attic/decorators/currency.py:
--------------------------------------------------------------------------------
1 | # currency.py
2 |
3 | """
4 | >>> convert(1, 'BRL', 'USD')
5 | 0.4591
6 | >>> convert(1, 'USD', 'BRL')
7 | 2.1784
8 | >>> convert(1, 'EUR', 'USD')
9 | 1.3482
10 | >>> convert(1, 'USD', 'EUR')
11 | 0.7417
12 | >>> convert(1, 'EUR', 'BRL')
13 | 2.9369
14 | >>> convert(1, 'BRL', 'EUR')
15 | 0.3405
16 |
17 | >>> from functools import partial
18 | >>> eur = partial(convert, cur_to='EUR')
19 | >>> eur(1, 'USD')
20 | 0.7417
21 | >>> eur(1, 'BRL')
22 | 0.3405
23 | >>> eur2brl = partial(convert, cur_from='EUR', cur_to='BRL')
24 | >>> eur2brl(100)
25 | 293.6864
26 | >>> type(eur2brl)
27 |
28 | """
29 |
30 | DEMO = """
31 | >>> eur2brl.func
32 |
33 | >>> eur2brl.args, eur2brl.keywords
34 | ((), {'cur_from': 'EUR', 'cur_to': 'BRL'})
35 | """
36 |
37 | rates = {'BRL': 2.17836,
38 | 'CAD': 1.03615,
39 | 'CNY': 6.10562,
40 | 'EUR': 0.74173,
41 | 'GBP': 0.62814,
42 | 'INR': 61.8685,
43 | 'JPY': 98.6002,
44 | 'USD': 1.0}
45 |
46 | reference = rates['USD']
47 |
48 | def convert(amount, cur_from, cur_to):
49 | ref_amount = reference / rates[cur_from] * amount
50 | return round(ref_amount * rates[cur_to], 4)
51 |
--------------------------------------------------------------------------------
/attic/decorators/local_demo.py:
--------------------------------------------------------------------------------
1 | """
2 | >>> f1(3)
3 | >>> b = 8
4 | >>> f1(3)
5 | a = 3
6 | b = 8
7 | >>> f2(3)
8 | Traceback (most recent call last):
9 | ...
10 | UnboundLocalError: local variable 'b' referenced before assignment
11 | >>> f3(3)
12 | a = 3
13 | b = 7
14 | b = 6
15 | >>> b = -5
16 | >>> ff = f4()
17 | >>> ff(3)
18 | a = 3
19 | b = 11
20 | b = 6
21 | >>> print('b =', b)
22 | b = -5
23 | """
24 |
25 | def f1(a):
26 | print('a =', a)
27 | print('b =', b)
28 |
29 | def f2(a):
30 | print('a =', a)
31 | print('b =', b)
32 | b = a * 10
33 | print('b =', b)
34 |
35 | def f3(a):
36 | global b
37 | print('a =', a)
38 | print('b =', b)
39 | b = a * 10
40 | print('b =', b)
41 |
42 | def f3b(a):
43 | nonlocal b
44 | print('a =', a)
45 | print('b =', b)
46 | b = a * 10
47 | print('b =', b)
48 |
49 | def f4():
50 | b = 11
51 | def f5(a):
52 | nonlocal b
53 | print('a =', a)
54 | print('b =', b)
55 | b = a * 2
56 | print('b =', b)
57 | return f5
58 |
59 | import doctest
60 | doctest.testmod(optionflags=doctest.ELLIPSIS | doctest.NORMALIZE_WHITESPACE)
61 |
62 |
63 |
64 |
--------------------------------------------------------------------------------
/attic/decorators/stacked_demo.py:
--------------------------------------------------------------------------------
1 | def d1(f):
2 | def wrapped():
3 | print('d1/wrapped')
4 | return f()
5 | return wrapped
6 |
7 |
8 | def d2(f):
9 | def wrapped():
10 | print('d2/wrapped')
11 | return f()
12 | return wrapped
13 |
14 |
15 | @d1
16 | @d2
17 | def f():
18 | print('f')
19 |
20 | f()
21 |
22 | def g():
23 | print('g')
24 |
25 | g = d1(d2(g))
26 |
27 | g()
28 |
--------------------------------------------------------------------------------
/attic/dicts/dict_perftest.py:
--------------------------------------------------------------------------------
1 | """
2 | Dict performance test
3 | """
4 |
5 | import timeit
6 |
7 | SETUP = '''
8 | import array
9 | selected = array.array('d')
10 | with open('selected.arr', 'rb') as fp:
11 | selected.fromfile(fp, {size})
12 | haystack = dict((n, n.as_integer_ratio()) for n in selected)
13 | print('haystack: %10d' % len(haystack), end=' ')
14 | needles = array.array('d')
15 | with open('not_selected.arr', 'rb') as fp:
16 | needles.fromfile(fp, 500)
17 | needles.extend(selected[:500])
18 | # print(' needles: %10d' % len(needles), end=' ')
19 | '''
20 |
21 | TEST = '''
22 | found = 0
23 | for n in needles:
24 | if n in haystack:
25 | found += 1
26 | # print(' found: %10d' % found)
27 | '''
28 |
29 | MAX_EXPONENT = 7
30 | for n in range(3, MAX_EXPONENT + 1):
31 | size = 10**n
32 | setup = SETUP.format(size=size)
33 | tt = timeit.repeat(stmt=TEST, setup=setup, repeat=5, number=1)
34 | print('|{:{}d}|{:f}'.format(size, MAX_EXPONENT + 1, min(tt)))
35 |
--------------------------------------------------------------------------------
/attic/dicts/index_alex.py:
--------------------------------------------------------------------------------
1 | # adapted from Alex Martelli's example in "Re-learning Python"
2 | # http://www.aleax.it/Python/accu04_Relearn_Python_alex.pdf
3 | # (slide 41) Ex: lines-by-word file index
4 |
5 |
6 | """Build a map word -> list-of-line-numbers"""
7 |
8 | import sys
9 | import re
10 |
11 | NONWORD_RE = re.compile('\W+')
12 |
13 | idx = {}
14 | with open(sys.argv[1], encoding='utf-8') as fp:
15 | for n, line in enumerate(fp, 1):
16 | for word in NONWORD_RE.split(line):
17 | if word.strip():
18 | idx.setdefault(word, []).append(n)
19 |
20 | # print in alphabetical order
21 | for word in sorted(idx, key=str.upper):
22 | print(word, idx[word])
23 |
--------------------------------------------------------------------------------
/attic/dicts/set_perftest.py:
--------------------------------------------------------------------------------
1 | """
2 | Set performance test
3 | """
4 |
5 | import timeit
6 |
7 | SETUP = '''
8 | import array
9 | selected = array.array('d')
10 | with open('selected.arr', 'rb') as fp:
11 | selected.fromfile(fp, {size})
12 | haystack = {type}(selected)
13 | # print('haystack: %10d' % len(haystack), end=' ')
14 | needles = array.array('d')
15 | with open('not_selected.arr', 'rb') as fp:
16 | needles.fromfile(fp, 500)
17 | needles.extend(selected[:500])
18 | needles = set(needles)
19 | # print(' needles: %10d' % len(needles), end=' ')
20 | '''
21 |
22 | tests = [
23 | ('FOR_LOOP_TEST', '''
24 | found = 0
25 | for n in needles:
26 | if n in haystack:
27 | found += 1
28 | assert found == 500
29 | '''),
30 | ('SET_&_TEST', '''
31 | found = len(needles & haystack)
32 | assert found == 500
33 | '''
34 | )]
35 |
36 | MAX_EXPONENT = 7
37 | for collection_type in 'dict.fromkeys set list'.split():
38 | if collection_type == 'set':
39 | available_tests = tests
40 | else:
41 | available_tests = tests[:1]
42 | for test_name, test in available_tests:
43 | print('*' * 25, collection_type, test_name)
44 | for n in range(3, MAX_EXPONENT + 1):
45 | size = 10**n
46 | setup = SETUP.format(type=collection_type, size=size)
47 | tt = timeit.repeat(stmt=test, setup=setup, repeat=5, number=1)
48 | print('|{:{}d}|{:9.6f}'.format(size, MAX_EXPONENT + 1, min(tt)))
49 |
--------------------------------------------------------------------------------
/attic/dicts/strkeydict0_userdictsub.py:
--------------------------------------------------------------------------------
1 | """StrKeyDict0 converts non-string keys to `str` on lookup
2 |
3 | # BEGIN STRKEYDICT0_TESTS
4 |
5 | Tests for item retrieval using `d[key]` notation::
6 |
7 | >>> d = StrKeyDict0([('2', 'two'), ('4', 'four')])
8 | >>> d['2']
9 | 'two'
10 | >>> d[4]
11 | 'four'
12 | >>> d[1]
13 | Traceback (most recent call last):
14 | ...
15 | KeyError: '1'
16 |
17 |
18 | Tests for item retrieval using `d.get(key)` notation::
19 |
20 | >>> d.get('2')
21 | 'two'
22 | >>> d.get(4)
23 | 'four'
24 | >>> d.get(1, 'N/A')
25 | 'N/A'
26 |
27 |
28 | Tests for the `in` operator::
29 |
30 | >>> 2 in d
31 | True
32 | >>> 1 in d
33 | False
34 |
35 | # END STRKEYDICT0_TESTS
36 | """
37 |
38 | # BEGIN STRKEYDICT0
39 |
40 | import collections
41 |
42 |
43 | class StrKeyDict0(collections.UserDict): # <1>
44 |
45 | def __missing__(self, key):
46 | if isinstance(key, str): # <2>
47 | raise KeyError(key)
48 | return self[str(key)] # <3>
49 |
50 | def __contains__(self, key):
51 | return str(key) in self.data # <3>
52 |
53 | # END STRKEYDICT0
54 |
--------------------------------------------------------------------------------
/attic/functions/accgen.py:
--------------------------------------------------------------------------------
1 | """
2 | Accumulator generator examples
3 |
4 | http://www.paulgraham.com/accgen.html
5 |
6 | >>> f3 = foo(3)
7 | >>> f3(2)
8 | 5
9 | >>> f3(2)
10 | 7
11 | >>> f3(2)
12 | 9
13 |
14 |
15 | """
16 |
17 | class foo0:
18 | def __init__(self, n):
19 | self.n = n
20 | def __call__(self, i):
21 | self.n += i
22 | return self.n
23 |
24 | def foo0(n):
25 | def bar(i):
26 | bar.s += i
27 | return bar.s
28 | bar.s = n
29 | return bar
30 |
31 | def foo(n):
32 | def bar(i):
33 | nonlocal n
34 | n += i
35 | return n
36 | return bar
37 |
--------------------------------------------------------------------------------
/attic/functions/attrgetter_demo.py:
--------------------------------------------------------------------------------
1 |
2 | metro_data = [
3 | ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
4 | ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
5 | ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
6 | ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
7 | ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
8 | ]
9 |
10 | from collections import namedtuple
11 |
12 | LatLong = namedtuple('LatLong', 'lat long')
13 | Metropolis = namedtuple('Metropolis', 'name cc pop coord')
14 |
15 | metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long_))
16 | for name, cc, pop, (lat, long_) in metro_data]
17 |
18 | metro_areas[0]
19 | metro_areas[0].coord.lat
20 |
21 | from operator import attrgetter
22 | name_lat = attrgetter('name', 'coord.lat')
23 |
24 | for city in sorted(metro_areas, key=attrgetter('coord.lat')):
25 | print(name_lat(city))
26 |
--------------------------------------------------------------------------------
/attic/functions/attrgetter_demo.rst:
--------------------------------------------------------------------------------
1 | >>> metro_data = [
2 | ... ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)),
3 | ... ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)),
4 | ... ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)),
5 | ... ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)),
6 | ... ('Sao Paulo', 'BR', 19.649, (-23.547778, -46.635833)),
7 | ... ]
8 | # BEGIN ATTRGETTER_DEMO
9 |
10 | >>> from collections import namedtuple
11 | >>> LatLong = namedtuple('LatLong', 'lat long') # <1>
12 | >>> Metropolis = namedtuple('Metropolis', 'name cc pop coord') # <2>
13 | >>> metro_areas = [Metropolis(name, cc, pop, LatLong(lat, long_)) # <3>
14 | ... for name, cc, pop, (lat, long_) in metro_data]
15 | >>> metro_areas[0]
16 | Metropolis(name='Tokyo', cc='JP', pop=36.933, coord=LatLong(lat=35.689722, long=139.691667))
17 | >>> metro_areas[0].coord.lat # <4>
18 | 35.689722
19 | >>> from operator import attrgetter
20 | >>> name_lat = attrgetter('name', 'coord.lat') # <5>
21 | >>>
22 | >>> for city in sorted(metro_areas, key=attrgetter('coord.lat')): # <6>
23 | ... print(name_lat(city)) # <7>
24 | ...
25 | ('Sao Paulo', -23.547778)
26 | ('Mexico City', 19.433333)
27 | ('Delhi NCR', 28.613889)
28 | ('Tokyo', 35.689722)
29 | ('New York-Newark', 40.808611)
30 |
31 | # END ATTRGETTER_DEMO
32 |
--------------------------------------------------------------------------------
/attic/functions/hello.py:
--------------------------------------------------------------------------------
1 | import bobo
2 |
3 | @bobo.query('/')
4 | def hello(person):
5 | return 'Hello %s!' % person
6 |
--------------------------------------------------------------------------------
/attic/futures/callbackhell.js:
--------------------------------------------------------------------------------
1 | fetch1(request1, function (response1) {
2 | // phase 1
3 | var request2 = step1(response1);
4 |
5 | fetch2(request2, function (response2) {
6 | // phase 2
7 | var request3 = step2(response2);
8 |
9 | fetch3(request3, function (response3) {
10 | // phase 3
11 | step3(response3);
12 | });
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/attic/futures/callbackhell.py:
--------------------------------------------------------------------------------
1 | def phase1(response1):
2 | request2 = step1(response1)
3 | fetch2(request2, phase2)
4 |
5 |
6 | def phase2(response2):
7 | request3 = step2(response2)
8 | fetch3(request3, phase3)
9 |
10 |
11 | def phase3(response3):
12 | step3(response3)
13 |
14 |
15 | fetch1(request1, phase1)
16 |
--------------------------------------------------------------------------------
/attic/futures/charfinder/charfinder_index.pickle:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/attic/futures/charfinder/charfinder_index.pickle
--------------------------------------------------------------------------------
/attic/futures/coroutine_purgatory.py:
--------------------------------------------------------------------------------
1 | @asyncio.coroutine
2 | def three_phases():
3 | response1 = yield from fetch1(request1)
4 | # phase 1
5 | request2 = step1(response1)
6 | response2 = yield from fetch2(request2)
7 | # phase 2
8 | request3 = step2(response2)
9 | response3 = yield from fetch3(request3)
10 | # phase 3
11 | step3(response3)
12 |
13 |
14 | loop.create_task(three_phases)
15 |
--------------------------------------------------------------------------------
/attic/futures/countries/flags_processpool.py:
--------------------------------------------------------------------------------
1 | """Download flags of top 20 countries by population
2 |
3 | ProcessPoolExecutor version
4 |
5 | Sample run::
6 |
7 | $ python3 flags_threadpool.py
8 | BD retrieved.
9 | EG retrieved.
10 | CN retrieved.
11 | ...
12 | PH retrieved.
13 | US retrieved.
14 | IR retrieved.
15 | 20 flags downloaded in 0.93s
16 |
17 | """
18 | # BEGIN FLAGS_PROCESSPOOL
19 | from concurrent import futures
20 |
21 | from flags import save_flag, get_flag, show, main
22 |
23 | MAX_WORKERS = 20
24 |
25 |
26 | def download_one(cc):
27 | image = get_flag(cc)
28 | show(cc)
29 | save_flag(image, cc.lower() + '.gif')
30 | return cc
31 |
32 |
33 | def download_many(cc_list):
34 | with futures.ProcessPoolExecutor() as executor: # <1>
35 | res = executor.map(download_one, sorted(cc_list))
36 |
37 | return len(list(res))
38 |
39 |
40 | if __name__ == '__main__':
41 | main(download_many)
42 | # END FLAGS_PROCESSPOOL
43 |
--------------------------------------------------------------------------------
/attic/futures/demo_executor_submit.py:
--------------------------------------------------------------------------------
1 | """
2 | Experiments with futures
3 | """
4 |
5 | from time import sleep, strftime
6 | from concurrent import futures
7 |
8 | def display(*args):
9 | print(strftime('[%H:%M:%S]'), end=' ')
10 | print(*args)
11 |
12 |
13 | def loiter(n):
14 | msg = '{}loiter({}): doing nothing for {}s...'
15 | display(msg.format('\t'*n, n, n))
16 | sleep(n)
17 | msg = '{}loiter({}): done.'
18 | display(msg.format('\t'*n, n))
19 | return n * 10
20 |
21 |
22 | def demo_submit():
23 | executor = futures.ThreadPoolExecutor(3)
24 | future_list = [executor.submit(loiter, n) for n in range(5)]
25 | display('done?', [future.done() for future in future_list])
26 | display('Waiting for results...')
27 | for i, result in enumerate(future.result() for future in future_list):
28 | display('result[{}]: {}'.format(i, result))
29 |
30 |
31 | demo_submit()
32 |
--------------------------------------------------------------------------------
/attic/futures/future_yield.py:
--------------------------------------------------------------------------------
1 | """
2 | An experiment showing that ``asyncio.Future`` is an iterable (it
3 | implements `__iter__`) designed to be used with ``yield from``.
4 |
5 | Priming the future returns itself. After the result of the future
6 | is set, next iteration produces the result as the ``value`` attribute
7 | of ``StopIteration``.
8 |
9 | Sample run::
10 |
11 | $ python3 future_yield.py
12 | a, future: 0x66514c
13 | b, prime_res: 0x66514c
14 | b, exc.value: 42
15 |
16 | """
17 |
18 | import asyncio
19 |
20 | @asyncio.coroutine
21 | def a(future):
22 | print('a, future:\t', future, hex(id(future)))
23 | res = yield from future
24 | return res
25 |
26 | def b():
27 | future = asyncio.Future()
28 | coro = a(future)
29 | prime_res = next(coro)
30 | print('b, prime_res:\t', prime_res, hex(id(future)))
31 | # If next(coro) is called again before the result of
32 | # the future is set, we get:
33 | # AssertionError: yield from wasn't used with future
34 | #result = next(coro) # uncomment to see AssertionError
35 | future.set_result(42)
36 | try:
37 | next(coro)
38 | except StopIteration as exc:
39 | print('b, exc.value:\t', exc.value)
40 |
41 | b()
42 |
--------------------------------------------------------------------------------
/attic/futures/future_yield2.py:
--------------------------------------------------------------------------------
1 | @asyncio.coroutine
2 | def a(future):
3 | print('a, future:', future, hex(id(future)))
4 | res = yield from future
5 | return res
6 |
7 | def b():
8 | future = asyncio.Future()
9 | coro = a(future)
10 | prime_result = next(coro)
11 | print('b, prime_result:', prime_result, hex(id(future)))
12 |
13 | loop = asyncio.get_event_loop()
14 | future = asyncio.Future()
15 | print('future:', future, hex(id(future)))
16 | tasks = [asyncio.async(a(future))]
17 |
18 | res = loop.run_until_complete(b())
19 |
20 |
--------------------------------------------------------------------------------
/attic/interfaces/subclassing_builtins.rst:
--------------------------------------------------------------------------------
1 | ====================================
2 | Subclassing built-in caveats
3 | ====================================
4 |
5 | ::
6 |
7 | >>> class D1(dict):
8 | ... def __getitem__(self, key):
9 | ... return 42
10 | ...
11 | >>> d1 = D1(a='foo')
12 | >>> d1
13 | {'a': 'foo'}
14 | >>> d1['a']
15 | 42
16 | >>> d1.get('a')
17 | 'foo'
18 |
19 | ::
20 |
21 | >>> class D2(dict):
22 | ... def get(self, key):
23 | ... return 42
24 | ...
25 | >>> d2 = D2(a='foo')
26 | >>> d2
27 | {'a': 'foo'}
28 | >>> d2['a']
29 | 'foo'
30 | >>> d2.get('a')
31 | 42
32 |
--------------------------------------------------------------------------------
/attic/iterables/CACM/citation.txt:
--------------------------------------------------------------------------------
1 | Examples in this directory were adapted from:
2 |
3 | Erik Meijer. 2014. The curse of the excluded middle.
4 | Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
5 | http://doi.acm.org/10.1145/2605176
6 |
--------------------------------------------------------------------------------
/attic/iterables/CACM/closed_file.py:
--------------------------------------------------------------------------------
1 | """
2 | Erik Meijer. 2014. The curse of the excluded middle.
3 | Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
4 | http://doi.acm.org/10.1145/2605176
5 | """
6 |
7 | with open('citation.txt', encoding='ascii') as fp:
8 | get_contents = lambda: fp.read()
9 |
10 | print(get_contents())
11 |
--------------------------------------------------------------------------------
/attic/iterables/CACM/haha.py:
--------------------------------------------------------------------------------
1 | """
2 | Erik Meijer. 2014. The curse of the excluded middle.
3 | Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
4 | http://doi.acm.org/10.1145/2605176
5 | """
6 |
7 | def ha():
8 | ha = 'Ha'
9 | print(ha)
10 | return ha
11 |
12 | # prints 'Ha' twice before showing result
13 | print('result ->', ha() + ha())
14 |
15 | # prints 'Ha' only once before showing reult
16 | ha_res = ha() # common subexpression elimination
17 | print('result ->', ha_res + ha_res)
18 |
--------------------------------------------------------------------------------
/attic/iterables/CACM/less_more.py:
--------------------------------------------------------------------------------
1 | """
2 | Meijer, Erik - The Curse of the Excluded Middle
3 | DOI:10.1145/2605176
4 | CACM vol.57 no.06
5 | """
6 |
7 | def less_than_30(n):
8 | check = n < 30
9 | print('%d < 30 : %s' % (n, check))
10 | return check
11 |
12 | def more_than_20(n):
13 | check = n > 20
14 | print('%d > 20 : %s' % (n, check))
15 | return check
16 |
17 | l = [1, 25, 40, 5, 23]
18 | q0 = (n for n in l if less_than_30(n))
19 | q1 = (n for n in q0 if more_than_20(n))
20 |
21 | for n in q1:
22 | print('-> %d' % n)
23 |
--------------------------------------------------------------------------------
/attic/iterables/CACM/zero_div.py:
--------------------------------------------------------------------------------
1 | """
2 | Erik Meijer. 2014. The curse of the excluded middle.
3 | Commun. ACM 57, 6 (June 2014), 50-55. DOI=10.1145/2605176
4 | http://doi.acm.org/10.1145/2605176
5 | """
6 |
7 | l = range(10, -1, -1)
8 | try:
9 | res = (1/x for x in l)
10 | except ZeroDivisionError:
11 | res = []
12 |
13 | for z in res:
14 | print(z)
15 |
--------------------------------------------------------------------------------
/attic/iterables/almost_aritprog_v0.py:
--------------------------------------------------------------------------------
1 | """
2 | Arithmetic progression class
3 |
4 | >>> ap = ArithmeticProgression(1, .5, 3)
5 | >>> list(ap)
6 | [1.0, 1.5, 2.0, 2.5]
7 |
8 |
9 | """
10 |
11 | from collections import abc
12 |
13 |
14 | class ArithmeticProgression:
15 |
16 | def __init__(self, begin, step, end):
17 | self.begin = begin
18 | self.step = step
19 | self.end = end
20 |
21 | def __iter__(self):
22 | return ArithmeticProgressionIterator(self)
23 |
24 |
25 | class ArithmeticProgressionIterator(abc.Iterator):
26 |
27 | def __init__(self, arithmetic_progression):
28 | self._ap = arithmetic_progression
29 | self._index = 0
30 |
31 | def __next__(self):
32 | first = type(self._ap.begin + self._ap.step)(self._ap.begin)
33 | result = first + self._ap.step * self._index
34 | if result < self._ap.end:
35 | self._index += 1
36 | return result
37 | else:
38 | raise StopIteration
39 |
--------------------------------------------------------------------------------
/attic/iterables/almost_aritprog_v6.py:
--------------------------------------------------------------------------------
1 | """
2 | Arithmetic progression generator function.
3 |
4 | This is almost correct. The only problem is that the first
5 | item in the series may not be of the same type as the rest,
6 | an this may be important to the user::
7 |
8 | >>> ap = aritprog_gen(1, .5, 3)
9 | >>> list(ap)
10 | [1, 1.5, 2.0, 2.5]
11 | >>> ap = aritprog_gen(0, 1/3, 1)
12 | >>> list(ap)
13 | [0, 0.3333333333333333, 0.6666666666666666]
14 | >>> from fractions import Fraction
15 | >>> ap = aritprog_gen(0, Fraction(1, 3), 1)
16 | >>> list(ap)
17 | [0, Fraction(1, 3), Fraction(2, 3)]
18 | >>> from decimal import Decimal
19 | >>> ap = aritprog_gen(0, Decimal('.1'), .3)
20 | >>> list(ap)
21 | [0, Decimal('0.1'), Decimal('0.2')]
22 |
23 |
24 | """
25 |
26 | # BEGIN ALMOST_ARITPROG_ITERTOOLS
27 | import itertools
28 |
29 |
30 | def aritprog_gen(begin, step, end=None):
31 | ap_gen = itertools.count(begin, step)
32 | if end is not None:
33 | ap_gen = itertools.takewhile(lambda n: n < end, ap_gen)
34 | return ap_gen
35 | # END ALMOST_ARITPROG_ITERTOOLS
36 |
--------------------------------------------------------------------------------
/attic/iterables/aritprog_v4.py:
--------------------------------------------------------------------------------
1 | # BEGIN ARITPROG_ITERTOOLS
2 | import itertools
3 |
4 |
5 | def aritprog_gen(begin, step, end=None):
6 | if end is not None and begin >= end:
7 | return
8 | yield type(begin + step)(begin)
9 | tail_gen = itertools.count(begin+step, step)
10 | if end is not None:
11 | tail_gen = itertools.takewhile(lambda n: n < end, tail_gen)
12 | for x in tail_gen:
13 | yield x
14 | # END ARITPROG_ITERTOOLS
15 |
--------------------------------------------------------------------------------
/attic/iterables/aritprog_v5.py:
--------------------------------------------------------------------------------
1 | # BEGIN ARITPROG_ITERTOOLS
2 | import itertools
3 |
4 |
5 | def aritprog_gen(begin, step, end=None):
6 | if end is not None and begin >= end:
7 | return
8 | yield type(begin + step)(begin)
9 | tail_gen = itertools.count(begin+step, step)
10 | if end is not None:
11 | tail_gen = itertools.takewhile(lambda n: n < end, tail_gen)
12 | yield from tail_gen
13 | # END ARITPROG_ITERTOOLS
14 |
--------------------------------------------------------------------------------
/attic/iterables/paragraph.py:
--------------------------------------------------------------------------------
1 | """
2 | Paragraph: iterate over sentences and words with generator functions.
3 | The ``.words()`` generator shows the use of ``yield from``.
4 |
5 | ::
6 | >>> p = Paragraph("The cat. The mat. Is the cat on the mat?"
7 | ... " The cat is on the mat.")
8 | >>> for s in p:
9 | ... print(s)
10 | ...
11 | Sentence('The cat.')
12 | Sentence('The mat.')
13 | Sentence('Is the cat on the mat?')
14 | Sentence('The cat is on the mat.')
15 | >>> list(p.words()) # doctest: +NORMALIZE_WHITESPACE
16 | ['The', 'cat', 'The', 'mat', 'Is', 'the', 'cat', 'on',
17 | 'the', 'mat', 'The', 'cat', 'is', 'on', 'the', 'mat']
18 |
19 |
20 | .. Note:: sample text from `McGuffey's First Eclectic Reader`__
21 |
22 | __ http://www.gutenberg.org/cache/epub/14640/pg14640.txt
23 | """
24 |
25 | import re
26 | import reprlib
27 |
28 | from sentence_gen import Sentence
29 |
30 |
31 | RE_SENTENCE = re.compile('([^.!?]+[.!?]+)')
32 |
33 |
34 | class Paragraph:
35 |
36 | def __init__(self, text):
37 | self.text = text
38 |
39 | def __repr__(self):
40 | return 'Paragraph(%s)' % reprlib.repr(self.text)
41 |
42 | def __iter__(self):
43 | for match in RE_SENTENCE.finditer(self.text):
44 | yield Sentence(match.group().strip())
45 |
46 | def words(self):
47 | for sentence in self:
48 | yield from sentence
49 |
--------------------------------------------------------------------------------
/attic/iterables/simplest_generators.doctest:
--------------------------------------------------------------------------------
1 | >>> def gen_123():
2 | ... yield 1
3 | ... yield 2
4 | ... yield 3
5 | ...
6 | >>> gen_123 # doctest: +ELLIPSIS
7 |
8 | >>> gen_123() # doctest: +ELLIPSIS
9 |
10 | >>> for i in gen_123():
11 | ... print(i)
12 | 1
13 | 2
14 | 3
15 | >>> g = gen_123()
16 | >>> next(g)
17 | 1
18 | >>> next(g)
19 | 2
20 | >>> next(g)
21 | 3
22 | >>> next(g)
23 | Traceback (most recent call last):
24 | ...
25 | StopIteration
26 |
27 |
28 | >>> def gen_AB():
29 | ... print('start')
30 | ... yield 'A'
31 | ... print('continue')
32 | ... yield 'B'
33 | ... print('end.')
34 | ...
35 | >>> for c in gen_AB():
36 | ... print('--->', c)
37 | ...
38 | start
39 | ---> A
40 | continue
41 | ---> B
42 | end.
43 |
--------------------------------------------------------------------------------
/attic/iterables/vector_flex_init.py:
--------------------------------------------------------------------------------
1 | """
2 | A more flexible `__init__` for the Vector class
3 |
4 | A Vector can be built from an iterable:
5 |
6 | >>> Vector([10, 20, 30])
7 | Vector(10, 20, 30)
8 | >>> Vector(range(1, 5))
9 | Vector(1, 2, 3, 4)
10 |
11 | Or from two more arguments:
12 |
13 | >>> Vector(100, 200)
14 | Vector(100, 200)
15 | >>> Vector(1, 2, 3, 4, 5)
16 | Vector(1, 2, 3, 4, 5)
17 |
18 | One-dimensional vectors are not supported:
19 |
20 | >>> Vector(99)
21 | Traceback (most recent call last):
22 | ...
23 | TypeError: Vector() takes one iterable argument or at least 2 scalar arguments
24 |
25 | """
26 |
27 | import reprlib
28 |
29 |
30 | class Vector:
31 | """An n-dimensional vector"""
32 |
33 | def __init__(self, first, *rest): # <1>
34 | if rest:
35 | self._components = (first,) + tuple(rest) # <3>
36 | else:
37 | try:
38 | self._components = tuple(first) # <2>
39 | except TypeError:
40 | raise TypeError('Vector() takes one iterable argument or at least 2 scalar arguments')
41 |
42 | def __repr__(self):
43 | return 'Vector' + reprlib.repr(self._components)
44 |
--------------------------------------------------------------------------------
/attic/metaprog/plainpoint.py:
--------------------------------------------------------------------------------
1 | """
2 | A class equivalent to the class statement below would be generated by this code:
3 |
4 | >>> import collections
5 | >>> Point = collections.plainclass('Point', 'x y')
6 | """
7 |
8 | class Point(object):
9 | __slots__ = ['x', 'y'] # save memory in the likely event there are many instances
10 |
11 | def __init__(self, x, y):
12 | self.x = x
13 | self.y = y
14 |
15 | def __repr__(self):
16 | return 'Point({!r}, {!r})'.format(self.x, self.y)
17 |
18 | def __eq__(self, other):
19 | if not isinstance(other, Point):
20 | return NotImplemented
21 | return self.x == other.x and self.y == other.y
22 |
23 | def __iter__(self, other): # support unpacking
24 | yield self.x
25 | yield self.y
26 |
--------------------------------------------------------------------------------
/attic/metaprog/prop_inheritance.py:
--------------------------------------------------------------------------------
1 | """
2 | Alex Martelli, _Python in a Nutshell, 2e._ (O'Reilly, 2006), p. 101
3 |
4 | ==========================
5 | Properties and inheritance
6 | ==========================
7 |
8 | Properties are inherited normally, just like any other attribute.
9 | However, there’s a little trap for the unwary: the methods called
10 | upon to access a property are those that are defined in the class
11 | in which the property itself is defined, without intrinsic use of
12 | further overriding that may happen in subclasses. For example:
13 | """
14 |
15 | class B(object):
16 |
17 | def f(self):
18 | return 23
19 |
20 | g = property(f)
21 |
22 | class C(B):
23 |
24 | def f(self):
25 | return 42
26 |
27 | c = C()
28 |
29 | print(c.g) # prints 23, not 42
30 |
--------------------------------------------------------------------------------
/attic/metaprog/special_attrs.py:
--------------------------------------------------------------------------------
1 |
2 | '''
3 | 4.13. Special Attributes
4 |
5 | The implementation adds a few special read-only attributes to
6 | several object types, where they are relevant.
7 |
8 | Some of these are not reported by the dir() built-in function.
9 |
10 | https://docs.python.org/3/library/stdtypes.html#special-attributes
11 | '''
12 |
13 |
14 | obj_attrs = {'__dict__', '__class__'}
15 |
16 | cls_data_attrs = {'__slots__', '__bases__', '__name__', '__qualname__', '__mro__'}
17 |
18 | cls_methods = {'mro', '__subclasses__'}
19 |
20 | cls_attrs = cls_data_attrs | cls_methods
21 |
22 |
23 | an_object = object()
24 |
25 | class EmptyClass():
26 | pass
27 |
28 | an_instance = EmptyClass()
29 |
30 | class EmptySlots():
31 | __slots__ = ()
32 |
33 | a_slots_instance = EmptySlots()
34 |
35 |
36 | objs = EmptyClass, EmptySlots, an_object, an_instance, a_slots_instance
37 |
38 | for obj in objs:
39 | print('-' * 60)
40 | print(repr(obj), ':', type(obj))
41 | dir_obj = set(dir(obj))
42 | print('obj_attrs not listed:', sorted(obj_attrs - dir_obj))
43 | print('all cls_attrs :', sorted(cls_attrs))
44 | print('cls_attrs not listed:', sorted(cls_attrs - dir_obj))
45 |
--------------------------------------------------------------------------------
/attic/metaprog/spreadsheet.py:
--------------------------------------------------------------------------------
1 | """
2 | Spreadsheet example adapted from Raymond Hettinger's `recipe`__
3 |
4 | __ http://code.activestate.com/recipes/355045-spreadsheet/
5 |
6 | Demonstration::
7 |
8 | >>> from math import sin, pi
9 | >>> ss = Spreadsheet(sin=sin, pi=pi)
10 | >>> ss['a1'] = '-5'
11 | >>> ss['a2'] = 'a1*6'
12 | >>> ss['a3'] = 'a2*7'
13 | >>> ss['a3']
14 | -210
15 | >>> ss['b1'] = 'sin(pi/4)'
16 | >>> ss['b1'] # doctest:+ELLIPSIS
17 | 0.707106781186...
18 | >>> ss.getformula('b1')
19 | 'sin(pi/4)'
20 | >>> ss['c1'] = 'abs(a2)'
21 | >>> ss['c1']
22 | 30
23 | >>> ss['d1'] = '3*'
24 | >>> ss['d1']
25 | Traceback (most recent call last):
26 | ...
27 | SyntaxError: unexpected EOF while parsing
28 | """
29 |
30 |
31 | class Spreadsheet:
32 |
33 | def __init__(self, **tools):
34 | self._cells = {}
35 | self._tools = tools
36 |
37 | def __setitem__(self, key, formula):
38 | self._cells[key] = formula
39 |
40 | def getformula(self, key):
41 | return self._cells[key]
42 |
43 | def __getitem__(self, key):
44 | return eval(self._cells[key], self._tools, self)
45 |
--------------------------------------------------------------------------------
/attic/operator/Interest.java:
--------------------------------------------------------------------------------
1 | /***
2 | Compound interest function with ``BigDecimal``
3 |
4 | Equivalent in Python:
5 |
6 | def compound_interest(principal, rate, periods):
7 | return principal * ((1 + rate) ** periods - 1)
8 |
9 | ***/
10 |
11 | import java.math.BigDecimal;
12 |
13 | public class Interest {
14 |
15 | static BigDecimal compoundInterest(BigDecimal principal, BigDecimal rate, int periods) {
16 | return principal.multiply(BigDecimal.ONE.add(rate).pow(periods).subtract(BigDecimal.ONE));
17 | }
18 |
19 | public static void main(String[] args) {
20 | BigDecimal principal = new BigDecimal(1000);
21 | BigDecimal rate = new BigDecimal("0.06");
22 | int periods = 5;
23 | System.out.println(compoundInterest(principal, rate, periods));
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/attic/operator/dispatch.py:
--------------------------------------------------------------------------------
1 | """
2 | Experiments with infix operator dispatch
3 |
4 | >>> kadd = KnowsAdd()
5 | >>> kadd + 1
6 | (, 1)
7 | >>> kadd * 1
8 |
9 | """
10 |
11 |
12 | class KnowsAdd:
13 | def __add__(self, other):
14 | return self, other
15 | def __repr__(self):
16 | return '<{} object>'.format(type(self).__name__)
17 |
18 |
--------------------------------------------------------------------------------
/attic/operator/factorial/CorrectFactorial.java:
--------------------------------------------------------------------------------
1 | import java.math.BigInteger;
2 |
3 | class CorrectFactorial {
4 | public static BigInteger factorial(BigInteger n) {
5 | return n.compareTo(BigInteger.ONE) <= 0 ? BigInteger.ONE
6 | : n.multiply(factorial(n.subtract(BigInteger.ONE)));
7 | }
8 |
9 | public static void main(String args[]) {
10 | BigInteger upperBound = new BigInteger("25");
11 | for (BigInteger i = BigInteger.ONE;
12 | i.compareTo(upperBound) <= 0;
13 | i = i.add(BigInteger.ONE)) {
14 | System.out.println(i + "! = " + factorial(i));
15 | }
16 | }
17 | }
18 |
19 | /* output:
20 |
21 | 1! = 1
22 | 2! = 2
23 | 3! = 6
24 | 4! = 24
25 | 5! = 120
26 | 6! = 720
27 | 7! = 5040
28 | 8! = 40320
29 | 9! = 362880
30 | 10! = 3628800
31 | 11! = 39916800
32 | 12! = 479001600
33 | 13! = 6227020800
34 | 14! = 87178291200
35 | 15! = 1307674368000
36 | 16! = 20922789888000
37 | 17! = 355687428096000
38 | 18! = 6402373705728000
39 | 19! = 121645100408832000
40 | 20! = 2432902008176640000
41 | 21! = 51090942171709440000
42 | 22! = 1124000727777607680000
43 | 23! = 25852016738884976640000
44 | 24! = 620448401733239439360000
45 | 25! = 15511210043330985984000000
46 |
47 | */
48 |
--------------------------------------------------------------------------------
/attic/operator/factorial/SimpleFactorial.java:
--------------------------------------------------------------------------------
1 | class SimpleFactorial {
2 |
3 | public static long factorial(long n) {
4 | return n < 2 ? 1 : n * factorial(n-1);
5 | }
6 |
7 | public static void main(String args[]) {
8 | for (long i = 1; i <= 25; i++) {
9 | System.out.println(i + "! = " + factorial(i));
10 |
11 | }
12 | }
13 | }
14 |
15 | /* output: incorrect results starting with 21!
16 |
17 | 1! = 1
18 | 2! = 2
19 | 3! = 6
20 | 4! = 24
21 | 5! = 120
22 | 6! = 720
23 | 7! = 5040
24 | 8! = 40320
25 | 9! = 362880
26 | 10! = 3628800
27 | 11! = 39916800
28 | 12! = 479001600
29 | 13! = 6227020800
30 | 14! = 87178291200
31 | 15! = 1307674368000
32 | 16! = 20922789888000
33 | 17! = 355687428096000
34 | 18! = 6402373705728000
35 | 19! = 121645100408832000
36 | 20! = 2432902008176640000
37 | 21! = -4249290049419214848
38 | 22! = -1250660718674968576
39 | 23! = 8128291617894825984
40 | 24! = -7835185981329244160
41 | 25! = 7034535277573963776
42 |
43 | */
44 |
--------------------------------------------------------------------------------
/attic/operator/factorial/factorial.py:
--------------------------------------------------------------------------------
1 | def factorial(n):
2 | return 1 if n < 2 else n * factorial(n-1)
3 |
4 | if __name__=='__main__':
5 | for i in range(1, 26):
6 | print('%s! = %s' % (i, factorial(i)))
7 |
8 | """
9 | output:
10 |
11 | 1! = 1
12 | 2! = 2
13 | 3! = 6
14 | 4! = 24
15 | 5! = 120
16 | 6! = 720
17 | 7! = 5040
18 | 8! = 40320
19 | 9! = 362880
20 | 10! = 3628800
21 | 11! = 39916800
22 | 12! = 479001600
23 | 13! = 6227020800
24 | 14! = 87178291200
25 | 15! = 1307674368000
26 | 16! = 20922789888000
27 | 17! = 355687428096000
28 | 18! = 6402373705728000
29 | 19! = 121645100408832000
30 | 20! = 2432902008176640000
31 | 21! = 51090942171709440000
32 | 22! = 1124000727777607680000
33 | 23! = 25852016738884976640000
34 | 24! = 620448401733239439360000
35 | 25! = 15511210043330985984000000
36 | """
37 |
--------------------------------------------------------------------------------
/attic/operator/interest.py:
--------------------------------------------------------------------------------
1 | """
2 | Compound interest function with ``decimal.Decimal``
3 |
4 | """
5 |
6 | def compound_interest(principal, rate, periods):
7 | return principal * ((1 + rate) ** periods - 1)
8 |
9 | def test(verbose=False):
10 | from decimal import Decimal, getcontext
11 | getcontext().prec = 8
12 | fixture = [(1000, Decimal('0.05'), 1, Decimal('50')),
13 | (1000, Decimal('0.10'), 5, Decimal('610.51')),
14 | (1000, Decimal('0.10'), 15, Decimal('3177.2482')),
15 | (1000, Decimal('0.06'), 5, Decimal('338.2256')),
16 | ]
17 | for principal, rate, periods, future_value in fixture:
18 | computed = compound_interest(principal, rate, periods)
19 | if verbose:
20 | print('{!r}, {!r}, {!r} -> {!r}'.format(
21 | principal, rate, periods, computed))
22 | assert future_value == computed, '{!r} != {!r}'.format(future_value, computed)
23 |
24 | if __name__ == '__main__':
25 | test(True)
26 |
--------------------------------------------------------------------------------
/attic/sequences/bisect_demo_pos.py:
--------------------------------------------------------------------------------
1 | import random
2 | import collections
3 |
4 | SIZE = 15
5 |
6 | random.seed(1729)
7 |
8 | target_list = [random.randrange(SIZE*2) for i in range(SIZE)]
9 | target_list.sort()
10 |
11 | random.seed(1729)
12 | display_list = [' '] * SIZE
13 | occurrences = collections.Counter()
14 | for i in range(SIZE):
15 | new_item = random.randrange(SIZE*2)
16 | pos = target_list.index(new_item) + occurrences[new_item]
17 | occurrences[new_item] += 1
18 | display_list[pos] = '%2s, ' % new_item
19 | print('[' + ''.join(display_list) + ']')
20 |
21 |
--------------------------------------------------------------------------------
/attic/sequences/bisect_find.py:
--------------------------------------------------------------------------------
1 |
2 | """
3 | >>> bisect_find([], 1)
4 | -1
5 | >>> import array
6 | >>> import random
7 | >>> SIZE = 10
8 | >>> my_array = array.array('l', range(0, SIZE, 2))
9 | >>> random.seed(42)
10 | >>> for i in range(SIZE):
11 | ... print(i, bisect_find(my_array, i))
12 | 0 0
13 | 1 -1
14 | 2 1
15 | 3 -1
16 | 4 2
17 | 5 -1
18 | 6 3
19 | 7 -1
20 | 8 4
21 | 9 -1
22 | """
23 |
24 | from bisect import bisect
25 |
26 | def bisect_find(seq, item):
27 | left_pos = bisect(seq, item) - 1
28 | return left_pos if seq and seq[left_pos] == item else -1
29 |
30 |
--------------------------------------------------------------------------------
/attic/sequences/bisect_in.py:
--------------------------------------------------------------------------------
1 |
2 | """
3 | >>> bisect_in([], 1)
4 | False
5 | >>> import array
6 | >>> import random
7 | >>> SIZE = 10
8 | >>> my_array = array.array('l', range(0, SIZE, 2))
9 | >>> random.seed(42)
10 | >>> for i in range(SIZE):
11 | ... print(i, bisect_in(my_array, i))
12 | 0 True
13 | 1 False
14 | 2 True
15 | 3 False
16 | 4 True
17 | 5 False
18 | 6 True
19 | 7 False
20 | 8 True
21 | 9 False
22 | """
23 |
24 | from bisect import bisect
25 |
26 | def bisect_in(seq, item):
27 | pos = bisect(seq, item)
28 | return seq[pos-1] == item if seq else False
29 |
30 |
--------------------------------------------------------------------------------
/attic/sequences/bisect_time.py:
--------------------------------------------------------------------------------
1 | """
2 | bisect_time.py
3 | """
4 |
5 | import timeit
6 |
7 | SETUP = '''
8 | SIZE = 10**6
9 | import array
10 | import random
11 | from bisect_find import bisect_find
12 | random.seed(42)
13 | haystack = [random.randrange(SIZE)*2 for i in range(SIZE)]
14 | needles = [random.choice(haystack) + i % 2 for i in range(20)]
15 | '''
16 |
17 | BISECT = '''
18 | print('bisect:', end=' ')
19 | for n in needles:
20 | print(bisect_find(haystack, n), end=' ')
21 | print()
22 | '''
23 |
24 | SORT = '''
25 | print(' in:', end=' ')
26 | for n in needles:
27 | print(int(n in haystack), end=' ')
28 | print()
29 | '''
30 |
31 | print(min(timeit.Timer(BISECT, SETUP).repeat(7, 1)))
32 | print(min(timeit.Timer(SORT, SETUP).repeat(7, 1)))
33 |
--------------------------------------------------------------------------------
/attic/sequences/dis_iadd_to_item.ods:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AllenDowney/fluent-python-notebooks/b71e738e2b816c962369b6d5c1ecb62065a65454/attic/sequences/dis_iadd_to_item.ods
--------------------------------------------------------------------------------
/attic/sequences/dis_iadd_to_item.txt:
--------------------------------------------------------------------------------
1 | 1 0 LOAD_NAME 0 (s)
2 | 3 LOAD_NAME 1 (a)
3 | 6 DUP_TOP_TWO
4 | 7 BINARY_SUBSCR <1>
5 | 8 LOAD_NAME 2 (b)
6 | 11 INPLACE_ADD <2>
7 | 12 ROT_THREE
8 | 13 STORE_SUBSCR <3>
9 | 14 LOAD_CONST 0 (None)
10 | 17 RETURN_VALUE
11 |
--------------------------------------------------------------------------------
/attic/sequences/named_slices.py:
--------------------------------------------------------------------------------
1 | invoice = """
2 | 0.....6.................................40........52...55........
3 | 1909 Pimoroni PiBrella $17.50 3 $52.50
4 | 1489 6mm Tactile Switch x20 $4.95 2 $9.90
5 | 1510 Panavise Jr. - PV-201 $28.00 1 $28.00
6 | 1601 PiTFT Mini Kit 320x240 $34.95 1 $34.95
7 | """
8 |
9 | structure = dict(
10 | SKU = slice(0, 6),
11 | DESCRIPTION = slice(6, 40),
12 | UNIT_PRICE = slice(40, 52),
13 | QUANTITY = slice(52, 55),
14 | ITEM_TOTAL = slice(55, None),
15 | )
16 |
17 | for line in invoice.split('\n')[2:]:
18 | line_item = {}
19 | for field, chunk in structure.items():
20 | line_item[field] = line[chunk].strip()
21 | print(line_item)
22 |
--------------------------------------------------------------------------------
/attic/sequences/slice_dump.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | >>> sd = SliceDump()
4 | >>> sd[1]
5 | 1
6 | >>> sd[2:5]
7 | slice(2, 5, None)
8 | >>> sd[:2]
9 | slice(None, 2, None)
10 | >>> sd[7:]
11 | slice(7, None, None)
12 | >>> sd[:]
13 | slice(None, None, None)
14 | >>> sd[1:9:3]
15 | slice(1, 9, 3)
16 | >>> sd[1:9:3, 2:3]
17 | (slice(1, 9, 3), slice(2, 3, None))
18 | >>> s = sd[1:9:3]
19 | >>> s.indices(20)
20 | (1, 9, 3)
21 | >>> s.indices(5)
22 | (1, 5, 3)
23 | >>> s.indices(1)
24 | (1, 1, 3)
25 | >>> s.indices(0)
26 | (0, 0, 3)
27 |
28 | """
29 |
30 | class SliceDump:
31 |
32 | def __getitem__(self, pos):
33 | return pos
34 |
--------------------------------------------------------------------------------
/attic/sequences/slice_test.py:
--------------------------------------------------------------------------------
1 | """
2 |
3 | >>> s = SliceDemo()
4 | >>> s[1]
5 | __getitem__: 1
6 | 1
7 | >>> s[2:5]
8 | __getitem__: slice(2, 5, None)
9 | [2, 3, 4]
10 | >>> s[:2]
11 | __getitem__: slice(None, 2, None)
12 | [0, 1]
13 | >>> s[7:]
14 | __getitem__: slice(7, None, None)
15 | [7, 8, 9]
16 | >>> s[:]
17 | __getitem__: slice(None, None, None)
18 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
19 | >>> s[1:9:3]
20 | __getitem__: slice(1, 9, 3)
21 | [1, 4, 7]
22 | >>> s[1:9:3, 2:3]
23 | __getitem__: (slice(1, 9, 3), slice(2, 3, None))
24 | ERROR: list indices must be integers, not tuple
25 |
26 | """
27 |
28 | class SliceDemo:
29 |
30 | def __init__(self):
31 | self.items = list(range(10))
32 |
33 | def __getitem__(self, pos):
34 | print('__getitem__:', pos)
35 | try:
36 | return self.items.__getitem__(pos)
37 | except TypeError as e:
38 | print('ERROR:', e)
39 |
--------------------------------------------------------------------------------
/attic/sequences/slice_viewer.py:
--------------------------------------------------------------------------------
1 | """
2 | Extended slicing:
3 |
4 | >>> s = SliceViewer()
5 | >>> s[1]
6 | 1
7 | >>> s[:]
8 | slice(None, None, None)
9 | >>> s[1:2]
10 | slice(1, 2, None)
11 | >>> s[1:2:3]
12 | slice(1, 2, 3)
13 | >>> s[1:2:3:4]
14 | Traceback (most recent call last):
15 | ...
16 | SyntaxError: invalid syntax
17 |
18 | N-dimensional indexing:
19 |
20 | >>> s[1, 2]
21 | (1, 2)
22 |
23 | N-dimensional slicing:
24 |
25 | >>> s[1:3, 2]
26 | (slice(1, 3, None), 2)
27 | >>> s[1, :2:]
28 | (1, slice(None, 2, None))
29 | >>> s[:, :]
30 | (slice(None, None, None), slice(None, None, None))
31 |
32 | """
33 |
34 |
35 | class SliceViewer:
36 |
37 | def __getitem__(self, position):
38 | return position
39 |
--------------------------------------------------------------------------------
/attic/sequences/str_concat.py:
--------------------------------------------------------------------------------
1 | """string concatenation demos"""
2 |
3 | from time import perf_counter
4 |
5 | def load_lines():
6 | with open('war-and-peace.txt') as fp:
7 | return fp.readlines() * 100 # replace with 200 or more for surprises!!!
8 |
9 | def chrono(f):
10 | def inner(lines):
11 | t0 = perf_counter()
12 | text = f(lines)
13 | elapsed = perf_counter() - t0
14 | print('%15s: %fs' % (f.__name__, elapsed))
15 | return text
16 | return inner
17 |
18 | @chrono
19 | def iadd_joiner(lines):
20 | text = ''
21 | for line in lines:
22 | text += line
23 | return text
24 |
25 | @chrono
26 | def list_joiner(lines):
27 | parts = []
28 | for line in lines:
29 | parts.append(line)
30 | return ''.join(parts)
31 |
32 | @chrono
33 | def genexp_joiner(lines):
34 | return ''.join(line for line in lines)
35 |
36 | if __name__=='__main__':
37 | lines = load_lines()
38 | print('joining %s lines' % len(lines))
39 | text0 = iadd_joiner(lines)
40 | text1 = list_joiner(lines)
41 | text2 = genexp_joiner(lines)
42 | assert len(text0) == len(text1) == len(text2), repr(
43 | (len(text0), len(text1), len(text2)))
44 |
--------------------------------------------------------------------------------
/attic/sequences/tuples.doctest:
--------------------------------------------------------------------------------
1 | >>> coordinates = (-23.547778, -46.635833)
2 | >>> lat, long_ = coordinates
3 | >>> long_
4 | -46.635833
5 | >>> lat
6 | -23.547778
7 | >>> traveler_ids = [('USA', '311975855'), ('BRA', 'CE342567'), ('ESP', 'XDA205856')]
8 | >>> for country, passport_no in sorted(traveler_ids):
9 | ... print('%s:%s' % (country, passport_no))
10 | BRA:CE342567
11 | ESP:XDA205856
12 | USA:311975855
13 |
14 |
15 |
--------------------------------------------------------------------------------
/attic/strings-bytes/cafe-gr.txt:
--------------------------------------------------------------------------------
1 | καφέ
--------------------------------------------------------------------------------
/attic/strings-bytes/cafe.txt:
--------------------------------------------------------------------------------
1 | café
--------------------------------------------------------------------------------
/attic/strings-bytes/casefold_demo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from unicodedata import name, normalize
3 |
4 | changed = 0
5 | assigned = 0
6 | for i in range(sys.maxunicode):
7 | char = chr(i)
8 | char_name = name(char, None)
9 | if char_name is None:
10 | continue
11 | cf = char.casefold()
12 | assigned += 1
13 | if cf != char.lower():
14 | cf_display = ' '.join(cf)
15 | cf_names = ';'.join(name(c) for c in cf)
16 | changed += 1
17 | print('%4d U+%04x' % (changed, i), char, cf_display, char_name + ' -> ' + cf_names, sep='\t')
18 |
19 | print(changed, '/', assigned, '=', changed/assigned*100)
20 |
--------------------------------------------------------------------------------
/attic/strings-bytes/category_demo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import unicodedata
3 |
4 | categories = set()
5 |
6 | for i in range(sys.maxunicode):
7 | char = chr(i)
8 | name = unicodedata.name(char, None)
9 | if name is None:
10 | continue
11 | cat = unicodedata.category(char)
12 | if cat[0] not in categories:
13 | print('U+%04x' % i, char.center(6),
14 | cat, name, sep='\t')
15 | categories.add(cat[0])
16 |
--------------------------------------------------------------------------------
/attic/strings-bytes/charfinder.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | from unicodedata import name
3 | import sys
4 |
5 | if len(sys.argv) > 1:
6 | query = sys.argv[1:]
7 | else:
8 | query = input('search words: ').split()
9 |
10 | query = [s.upper() for s in query]
11 |
12 | count = 0
13 | for i in range(20, sys.maxunicode):
14 | car = chr(i)
15 | descr = name(car, None)
16 | if descr is None:
17 | continue
18 | words = descr.split()
19 | if all(word in words for word in query):
20 | print('{i:5d} {i:04x} {car:^5} {descr}'.format(**locals()))
21 | count += 1
22 |
23 | print('{0} character(s) found'.format(count))
24 |
--------------------------------------------------------------------------------
/attic/strings-bytes/currency_demo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import unicodedata
3 |
4 | for i in range(sys.maxunicode):
5 | char = chr(i)
6 | if unicodedata.category(char) == 'Sc':
7 | name = unicodedata.name(char, None)
8 | print('U+%04x' % i, char.center(6),
9 | name, sep='\t')
10 |
--------------------------------------------------------------------------------
/attic/strings-bytes/identifier_norm.py:
--------------------------------------------------------------------------------
1 |
2 | café = 1
3 | café = 2
4 | names = {(name, tuple(name)):value
5 | for name, value in globals().items()
6 | if not name.startswith('__')}
7 | print(names)
8 |
--------------------------------------------------------------------------------
/attic/strings-bytes/identifier_norm_writer.py:
--------------------------------------------------------------------------------
1 |
2 | src = """
3 | café = 1
4 | cafe\u0301 = 2
5 | names = {(name, tuple(name)):value
6 | for name, value in globals().items()
7 | if not name.startswith('__')}
8 | print(names)
9 | """
10 |
11 | with open('identifier_norm.py', 'tw', encoding='utf8') as out:
12 | out.write(src)
13 |
--------------------------------------------------------------------------------
/attic/strings-bytes/nfc_demo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from unicodedata import name, normalize
3 |
4 | for i in range(sys.maxunicode):
5 | char = chr(i)
6 | char_name = name(char, None)
7 | if char_name is None:
8 | continue
9 | nfc = normalize('NFC', char)
10 | if nfc == char:
11 | continue
12 | if len(nfc) > 1:
13 | nfc_display = ' '.join(nfc)
14 | print('U+%04x' % i, char, nfc_display, char_name, sep='\t')
15 |
--------------------------------------------------------------------------------
/attic/strings-bytes/nfk_demo.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from unicodedata import name, normalize
3 |
4 | for i in range(sys.maxunicode):
5 | char = chr(i)
6 | char_name = name(char, None)
7 | if char_name is None:
8 | continue
9 | kc = normalize('NFKC', char)
10 | if kc == char:
11 | continue
12 | kd = normalize('NFKD', char)
13 | if kc != kd:
14 | kc_display = ' '.join(kc)
15 | kd_display = ' '.join(kd)
16 | print('U+%04x' % i, char, kc_display, kd_display, char_name, sep='\t')
17 |
--------------------------------------------------------------------------------
/attic/strings-bytes/numerics.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from unicodedata import name
3 |
4 | for i in range(sys.maxunicode):
5 | char = chr(i)
6 | try:
7 | char_name = name(char)
8 | except ValueError: # no such name
9 | continue
10 | flags = []
11 | flags.append('D' if char.isdigit() else '')
12 | flags.append('N' if char.isnumeric() else '')
13 | if any(flags):
14 | flags = '\t'.join(flags)
15 | print('U+%04x' % i, char, flags, char_name, sep='\t')
16 |
--------------------------------------------------------------------------------
/attic/strings-bytes/numerics_demo.txt:
--------------------------------------------------------------------------------
1 | U+0031 1 re_dig isdig isnum 1.00 DIGIT ONE
2 | U+00b2 ² - isdig isnum 2.00 SUPERSCRIPT TWO
3 | U+00bc ¼ - - isnum 0.25 VULGAR FRACTION ONE QUARTER
4 | U+0969 ३ re_dig isdig isnum 3.00 DEVANAGARI DIGIT THREE
5 | U+136b ፫ - isdig isnum 3.00 ETHIOPIC DIGIT THREE
6 | U+216b Ⅻ - - isnum 12.00 ROMAN NUMERAL TWELVE
7 | U+2466 ⑦ - isdig isnum 7.00 CIRCLED DIGIT SEVEN
8 | U+2480 ⒀ - - isnum 13.00 PARENTHESIZED NUMBER THIRTEEN
9 | U+3285 ㊅ - - isnum 6.00 CIRCLED IDEOGRAPH SIX
10 |
--------------------------------------------------------------------------------
/attic/strings-bytes/plane_count.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from unicodedata import name, normalize
3 |
4 | total_count = 0
5 | bmp_count = 0
6 |
7 | for i in range(sys.maxunicode):
8 | char = chr(i)
9 | char_name = name(char, None)
10 | if char_name is None:
11 | continue
12 | total_count += 1
13 | if i <= 0xffff:
14 | bmp_count += 1
15 |
16 | print(total_count, bmp_count, bmp_count/total_count, bmp_count/total_count*100)
17 |
--------------------------------------------------------------------------------
/attic/strings-bytes/sorting.py:
--------------------------------------------------------------------------------
1 | import locale
2 |
3 | def check(sorted_list):
4 | return 'CORRECT' if fruits == sorted_list else 'WRONG'
5 |
6 | fruits = ['açaí', 'acerola', 'atemoia', 'cajá', 'caju']
7 |
8 | print(locale.getlocale(locale.LC_COLLATE))
9 |
10 | print('manual_sort ', fruits)
11 |
12 | plain_sort = sorted(fruits)
13 |
14 | print('plain_sort ', plain_sort, check(plain_sort))
15 |
16 | locale_sort1 = sorted(fruits, key=locale.strxfrm)
17 |
18 | print('locale_sort1', locale_sort1, check(locale_sort1))
19 |
20 | locale.setlocale(locale.LC_COLLATE, 'pt_BR.UTF-8')
21 |
22 | print('locale set to:', locale.getlocale(locale.LC_COLLATE))
23 |
24 | locale_sort2 = sorted(fruits, key=locale.strxfrm)
25 |
26 | print('locale_sort2', locale_sort2, check(locale_sort2))
27 |
--------------------------------------------------------------------------------
/attic/strings-bytes/sorting_uca.py:
--------------------------------------------------------------------------------
1 | from pyuca import Collator
2 |
3 | def check(sorted_list):
4 | return 'CORRECT' if fruits == sorted_list else 'WRONG'
5 |
6 | fruits = ['açaí', 'acerola', 'atemoia', 'cajá', 'caju']
7 |
8 | print('manual_sort', fruits)
9 |
10 | plain_sort = sorted(fruits)
11 |
12 | print('plain_sort ', plain_sort, check(plain_sort))
13 |
14 | coll = Collator()
15 |
16 | pyuca_sort = sorted(fruits, key=coll.sort_key)
17 |
18 | print('pyuca_sort ', pyuca_sort, check(pyuca_sort))
19 |
--------------------------------------------------------------------------------
/attic/strings-bytes/sorting_uca.txt:
--------------------------------------------------------------------------------
1 | PS > pip install pyuca
2 | Downloading/unpacking pyuca
3 | Running setup.py (path:C:\Users\...) egg_info for package pyuca
4 | Installing collected packages: pyuca
5 | Running setup.py install for pyuca
6 | Successfully installed pyuca
7 | Cleaning up...
8 | PS > python .\sorting_uca.py
9 | manual_sort ['açaí', 'acaíba', 'acerola', 'cajá', 'caju']
10 | plain_sort ['acaíba', 'acerola', 'açaí', 'caju', 'cajá'] WRONG
11 | pyuca_sort ['açaí', 'acaíba', 'acerola', 'cajá', 'caju'] CORRECT
12 |
--------------------------------------------------------------------------------
/attic/strings-bytes/str_repr.py:
--------------------------------------------------------------------------------
1 |
2 | last_len = 0
3 | last_repr = ''
4 | lengths = set()
5 | for i in range(0x110000):
6 | r = repr(chr(i))[1:-1]
7 | if len(r) != last_len:
8 | lengths.add(len(r))
9 | last_len = len(r)
10 | if i > 0:
11 | prev_repr = repr(chr(i-1))[1:-1]
12 | print('{}'.format(prev_repr))
13 | print('U+{:04x} {:{max_len}} ...'.format(i, r, max_len=max(lengths)), end=' ')
14 | last_repr = r
15 |
--------------------------------------------------------------------------------
/attic/strings-bytes/strings-bytes-test.txt:
--------------------------------------------------------------------------------
1 | >>> s = 'naïve' <1>
2 | >>> b = b'naïve' <2>
3 | Traceback (most recent call last):
4 | ...
5 | SyntaxError: bytes can only contain ASCII literal characters.
6 | >>> b = bytes('naïve', 'iso8859-1') <3>
7 | >>> b <4>
8 | b'na\xefve'
9 | >>> s <5>
10 | 'naïve'
11 | >>> b == s.encode('iso8859-1') <6>
12 | True
13 | >>> s[2] <7>
14 | 'ï'
15 | >>> b[2] <8>
16 | 239
17 | >>> ord(s[2]) <9>
18 | 239
19 | >>> s.upper() <10>
20 | 'NAÏVE'
21 | >>> b.upper() <11>
22 | b'NA\xefVE'
23 |
--------------------------------------------------------------------------------
/localfiles.txt:
--------------------------------------------------------------------------------
1 | update.sh
2 | localfiles.txt
3 | .git
4 | *.pyc
5 | __pycache__
6 | .gitignore
7 | .DS_Store
8 | *.arr
9 | /README.rst
10 | LICENSE
11 |
--------------------------------------------------------------------------------
/update.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | rsync -av --delete --exclude-from localfiles.txt --exclude-from .gitignore ../atlas/code/ .
3 |
--------------------------------------------------------------------------------
| | | | | | |