├── .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' % 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 '
      \n
    • ' + inner + '
    • \n
    ' 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 |

    11 | 12 | {message} 13 |
    14 |

    15 | 16 | {result} 17 |
    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 |

    11 | 12 | {message} 13 |
    14 |

    15 | 16 | {result} 17 |
    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 | [![Binder](http://mybinder.org/badge.svg)](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 | --------------------------------------------------------------------------------