├── .github ├── dependabot.yaml └── workflows │ └── publish.yaml ├── .gitignore ├── .vscode └── settings.json ├── LICENSE ├── Livro.adoc ├── Posfacio.adoc ├── Prefacio.adoc ├── README.adoc ├── atributos-pt_BR.adoc ├── build.sh ├── callouts ├── 1.pdf ├── 1.png ├── 10.pdf ├── 10.png ├── 11.pdf ├── 11.png ├── 12.pdf ├── 12.png ├── 13.pdf ├── 13.png ├── 14.pdf ├── 14.png ├── 15.pdf ├── 15.png ├── 16.pdf ├── 16.png ├── 17.pdf ├── 17.png ├── 18.pdf ├── 18.png ├── 19.pdf ├── 19.png ├── 2.pdf ├── 2.png ├── 20.pdf ├── 20.png ├── 21.pdf ├── 21.png ├── 22.pdf ├── 22.png ├── 23.pdf ├── 23.png ├── 24.pdf ├── 24.png ├── 25.pdf ├── 25.png ├── 26.pdf ├── 26.png ├── 27.pdf ├── 27.png ├── 28.pdf ├── 28.png ├── 29.pdf ├── 29.png ├── 3.pdf ├── 3.png ├── 30.pdf ├── 30.png ├── 31.pdf ├── 31.png ├── 32.pdf ├── 32.png ├── 33.pdf ├── 33.png ├── 34.pdf ├── 34.png ├── 35.pdf ├── 35.png ├── 36.pdf ├── 36.png ├── 37.pdf ├── 37.png ├── 38.pdf ├── 38.png ├── 39.pdf ├── 39.png ├── 4.pdf ├── 4.png ├── 5.pdf ├── 5.png ├── 6.pdf ├── 6.png ├── 7.pdf ├── 7.png ├── 8.pdf ├── 8.png ├── 9.pdf └── 9.png ├── capitulos ├── cap01.adoc ├── cap02.adoc ├── cap03.adoc ├── cap04.adoc ├── cap05.adoc ├── cap06.adoc ├── cap07.adoc ├── cap08.adoc ├── cap09.adoc ├── cap10.adoc ├── cap11.adoc ├── cap12.adoc ├── cap13.adoc ├── cap14.adoc ├── cap15.adoc ├── cap16.adoc ├── cap17.adoc ├── cap18.adoc ├── cap19.adoc ├── cap20.adoc ├── cap21.adoc ├── cap22.adoc ├── cap23.adoc ├── cap24.adoc └── code │ ├── 01-data-model │ ├── README.md │ ├── data-model.ipynb │ ├── frenchdeck.doctest │ ├── frenchdeck.py │ ├── test.sh │ ├── vector2d.doctest │ └── vector2d.py │ ├── 02-array-seq │ ├── README.rst │ ├── array-seq.ipynb │ ├── bisect_demo.py │ ├── bisect_insort.py │ ├── lispy │ │ ├── py3.10 │ │ │ ├── examples_test.py │ │ │ ├── lis.py │ │ │ ├── lis_test.py │ │ │ └── quicksort.scm │ │ └── py3.9 │ │ │ ├── README.md │ │ │ ├── examples_test.py │ │ │ ├── lis.py │ │ │ └── lis_test.py │ ├── listcomp_speed.py │ ├── match_lat_lon.py │ ├── memoryviews.ipynb │ ├── metro_lat_lon.py │ └── test.sh │ ├── 03-dict-set │ ├── 03-dict-set.ipynb │ ├── README.md │ ├── dialcodes.py │ ├── index.py │ ├── index0.py │ ├── index_default.py │ ├── missing.py │ ├── py3.10 │ │ └── creator.py │ ├── strkeydict.py │ ├── strkeydict0.py │ ├── support │ │ ├── container_perftest.py │ │ ├── container_perftest_datagen.py │ │ └── hashdiff.py │ ├── transformdict.py │ └── zen.txt │ ├── 04-text-byte │ ├── .gitignore │ ├── README.rst │ ├── categories.py │ ├── charfinder │ │ ├── README.rst │ │ ├── cf.py │ │ └── test.sh │ ├── default_encodings.py │ ├── encodings-win10.txt │ ├── locale_sort.py │ ├── normeq.py │ ├── numerics_demo.py │ ├── ola.py │ ├── ramanujan.py │ ├── simplify.py │ ├── skin.py │ ├── stdout_check.py │ ├── syntax-msg.txt │ ├── two_flags.py │ ├── zwj_sample.ipynb │ ├── zwj_sample.png │ └── zwj_sample.py │ ├── 05-data-classes │ ├── README.asciidoc │ ├── cards.doctest │ ├── cards.py │ ├── cards_enum.py │ ├── class │ │ └── coordinates.py │ ├── dataclass │ │ ├── club.py │ │ ├── club_generic.py │ │ ├── club_wrong.py │ │ ├── coordinates.py │ │ ├── hackerclub.py │ │ ├── hackerclub_annotated.py │ │ ├── resource.py │ │ └── resource_repr.py │ ├── frenchdeck.doctest │ ├── frenchdeck.py │ ├── match_cities.py │ ├── meaning │ │ ├── demo_dc.py │ │ ├── demo_nt.py │ │ └── demo_plain.py │ └── typing_namedtuple │ │ ├── coordinates.py │ │ ├── coordinates2.py │ │ └── nocheck_demo.py │ ├── 06-obj-ref │ ├── bus.py │ ├── haunted_bus.py │ └── twilight_bus.py │ ├── 07-1class-func │ ├── README.rst │ ├── bingocall.py │ └── tagger.py │ ├── 08-def-type-hints │ ├── README.asciidoc │ ├── arg_lab.py │ ├── birds │ │ ├── birds.py │ │ ├── daffy.py │ │ ├── protocol │ │ │ ├── lake.py │ │ │ ├── parrot.py │ │ │ └── swan.py │ │ └── woody.py │ ├── bus.py │ ├── callable │ │ └── variance.py │ ├── charindex.py │ ├── colors.py │ ├── columnize.py │ ├── columnize_test.py │ ├── comparable │ │ ├── comparable.py │ │ ├── top.py │ │ └── top_test.py │ ├── coordinates │ │ ├── coordinates.py │ │ ├── coordinates_named.py │ │ ├── coordinates_named_test.py │ │ ├── coordinates_test.py │ │ └── requirements.txt │ ├── ctime.py │ ├── double │ │ ├── double_object.py │ │ ├── double_protocol.py │ │ ├── double_sequence.py │ │ └── double_test.py │ ├── messages │ │ ├── hints_1 │ │ │ ├── messages.py │ │ │ └── messages_test.py │ │ ├── hints_2 │ │ │ ├── messages.py │ │ │ └── messages_test.py │ │ └── no_hints │ │ │ ├── messages.py │ │ │ └── messages_test.py │ ├── mode │ │ ├── mode_float.py │ │ └── mode_hashable.py │ ├── mypy.ini │ ├── replacer.py │ ├── romans.py │ ├── romans_test.py │ ├── sample.py │ ├── typevar_bounded.py │ └── typevars_constrained.py │ ├── 09-closure-deco │ ├── README.rst │ ├── average.py │ ├── average_oo.py │ ├── clock │ │ ├── clockdeco.py │ │ ├── clockdeco0.py │ │ ├── clockdeco_cls.py │ │ ├── clockdeco_demo.py │ │ ├── clockdeco_param.py │ │ ├── clockdeco_param_demo1.py │ │ └── clockdeco_param_demo2.py │ ├── fibo_compare.py │ ├── fibo_demo.py │ ├── fibo_demo_cache.py │ ├── global_x_local.rst │ ├── htmlizer.py │ ├── registration.py │ ├── registration_abridged.py │ ├── registration_param.py │ └── stacked.py │ ├── 10-dp-1class-func │ ├── README.rst │ ├── classic_strategy.py │ ├── classic_strategy_test.py │ ├── monkeytype │ │ ├── classic_strategy.py │ │ ├── classic_strategy.pyi │ │ ├── classic_strategy_test.py │ │ └── run.py │ ├── promotions.py │ ├── pytypes │ │ ├── classic_strategy.py │ │ ├── classic_strategy_test.py │ │ └── typelogger_output │ │ │ └── classic_strategy.pyi │ ├── requirements.txt │ ├── strategy.py │ ├── strategy_best.py │ ├── strategy_best2.py │ ├── strategy_best3.py │ ├── strategy_best4.py │ ├── strategy_param.py │ ├── strategy_param_test.py │ ├── strategy_test.py │ └── untyped │ │ ├── classic_strategy.py │ │ ├── promotions.py │ │ ├── strategy.py │ │ ├── strategy_best.py │ │ ├── strategy_best2.py │ │ ├── strategy_best3.py │ │ ├── strategy_best4.py │ │ ├── strategy_param.py │ │ └── strategy_param2.py │ ├── 11-pythonic-obj │ ├── README.md │ ├── mem_test.py │ ├── patterns.py │ ├── private │ │ ├── .gitignore │ │ ├── Confidential.java │ │ ├── Expose.java │ │ ├── expose.py │ │ ├── leakprivate.py │ │ └── no_respect.py │ ├── slots.rst │ ├── 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 │ ├── 12-seq-hacking │ ├── vector_v1.py │ ├── vector_v2.py │ ├── vector_v3.py │ ├── vector_v4.py │ └── vector_v5.py │ ├── 13-protocol-abc │ ├── README.rst │ ├── bingo.py │ ├── double │ │ ├── double_object.py │ │ ├── double_protocol.py │ │ ├── double_sequence.py │ │ └── double_test.py │ ├── drum.py │ ├── frenchdeck2.py │ ├── lotto.py │ ├── tombola.py │ ├── tombola_runner.py │ ├── tombola_subhook.py │ ├── tombola_tests.rst │ ├── tombolist.py │ └── typing │ │ ├── randompick.py │ │ ├── randompick_test.py │ │ ├── randompickload.py │ │ ├── randompickload_test.py │ │ ├── vector2d_v4.py │ │ ├── vector2d_v4_test.py │ │ ├── vector2d_v5.py │ │ └── vector2d_v5_test.py │ ├── 14-inheritance │ ├── README.rst │ ├── diamond.py │ ├── diamond2.py │ ├── strkeydict_dictsub.py │ └── uppermixin.py │ ├── 15-more-types │ ├── cafeteria │ │ ├── cafeteria.py │ │ ├── contravariant.py │ │ ├── covariant.py │ │ └── invariant.py │ ├── cast │ │ ├── empty.py │ │ ├── find.py │ │ ├── tcp_echo.py │ │ └── tcp_echo_no_cast.py │ ├── clip_annot.py │ ├── clip_annot_demo.py │ ├── clip_annot_post.py │ ├── collections_variance.py │ ├── gen_contra.py │ ├── lotto │ │ ├── generic_lotto.py │ │ ├── generic_lotto_demo.py │ │ ├── generic_lotto_errors.py │ │ └── tombola.py │ ├── mysum.py │ ├── petbox │ │ ├── petbox.py │ │ └── petbox_demo.py │ ├── protocol │ │ ├── abs_demo.py │ │ ├── mymax │ │ │ ├── mymax.py │ │ │ ├── mymax_demo.py │ │ │ └── mymax_test.py │ │ └── random │ │ │ ├── erp.py │ │ │ ├── erp_test.py │ │ │ ├── generic_randompick.py │ │ │ ├── generic_randompick_test.py │ │ │ ├── randompop.py │ │ │ └── randompop_test.py │ └── typeddict │ │ ├── books.py │ │ ├── books_any.py │ │ ├── demo_books.py │ │ ├── demo_not_book.py │ │ ├── test_books.py │ │ └── test_books_check_fails.py │ ├── 16-op-overloading │ ├── README.rst │ ├── Untitled.ipynb │ ├── bingo.py │ ├── bingoaddable.py │ ├── tombola.py │ ├── unary_plus_decimal.py │ ├── vector2d_v3.py │ ├── vector_v6.py │ ├── vector_v7.py │ └── vector_v8.py │ ├── 17-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 │ ├── columnize_iter.py │ ├── coroaverager.py │ ├── coroaverager2.py │ ├── fibo_by_hand.py │ ├── fibo_gen.py │ ├── isis2json │ │ ├── README.rst │ │ ├── isis2json.py │ │ ├── iso2709.py │ │ └── subfield.py │ ├── iter_gen_type.py │ ├── sentence.py │ ├── sentence.rst │ ├── sentence_gen.py │ ├── sentence_gen2.py │ ├── sentence_genexp.py │ ├── sentence_iter.py │ ├── sentence_iter2.py │ ├── sentence_runner.py │ ├── tree │ │ ├── 4steps │ │ │ ├── tree_step0.py │ │ │ ├── tree_step1.py │ │ │ ├── tree_step2.py │ │ │ └── tree_step3.py │ │ ├── classtree │ │ │ ├── classtree.py │ │ │ └── classtree_test.py │ │ ├── extra │ │ │ ├── drawtree.py │ │ │ ├── test_drawtree.py │ │ │ ├── test_tree.py │ │ │ └── tree.py │ │ ├── step0 │ │ │ ├── test_tree.py │ │ │ └── tree.py │ │ ├── step1 │ │ │ ├── test_tree.py │ │ │ └── tree.py │ │ ├── step2 │ │ │ ├── test_tree.py │ │ │ └── tree.py │ │ ├── step3 │ │ │ ├── test_tree.py │ │ │ └── tree.py │ │ ├── step4 │ │ │ ├── test_tree.py │ │ │ └── tree.py │ │ ├── step5 │ │ │ ├── test_tree.py │ │ │ └── tree.py │ │ └── step6 │ │ │ ├── test_tree.py │ │ │ └── tree.py │ ├── yield_delegate_fail.py │ └── yield_delegate_fix.py │ ├── 18-with-match │ ├── README.rst │ ├── lispy │ │ ├── LICENSE │ │ ├── README.md │ │ ├── original │ │ │ ├── LICENSE │ │ │ ├── README.md │ │ │ ├── lis.py │ │ │ ├── lispy.py │ │ │ └── lispytest.py │ │ ├── py3.10 │ │ │ ├── examples_test.py │ │ │ ├── lis.py │ │ │ ├── lis_test.py │ │ │ └── quicksort.scm │ │ └── py3.9 │ │ │ ├── README.md │ │ │ ├── examples_test.py │ │ │ ├── lis.py │ │ │ └── lis_test.py │ ├── mirror.py │ ├── mirror_gen.py │ └── mirror_gen_exc.py │ ├── 19-concurrency │ ├── primes │ │ ├── README.md │ │ ├── log-procs.txt │ │ ├── primes.py │ │ ├── procs.py │ │ ├── procs_race_condition.py │ │ ├── py36 │ │ │ ├── primes.py │ │ │ └── procs.py │ │ ├── run_procs.sh │ │ ├── sequential.py │ │ ├── spinner_prime_async_broken.py │ │ ├── spinner_prime_async_nap.py │ │ ├── spinner_prime_proc.py │ │ ├── spinner_prime_thread.py │ │ ├── stats-procs.ipynb │ │ └── threads.py │ ├── spinner_async.py │ ├── spinner_async_experiment.py │ ├── spinner_proc.py │ └── spinner_thread.py │ ├── 20-executors │ ├── demo_executor_map.py │ ├── getflags │ │ ├── .gitignore │ │ ├── README.adoc │ │ ├── country_codes.txt │ │ ├── flags.py │ │ ├── flags.zip │ │ ├── flags2_asyncio.py │ │ ├── flags2_asyncio_executor.py │ │ ├── flags2_common.py │ │ ├── flags2_sequential.py │ │ ├── flags2_threadpool.py │ │ ├── flags3_asyncio.py │ │ ├── flags_asyncio.py │ │ ├── flags_threadpool.py │ │ ├── flags_threadpool_futures.py │ │ ├── httpx-error-tree │ │ │ ├── drawtree.py │ │ │ └── tree.py │ │ ├── requirements.txt │ │ └── slow_server.py │ └── primes │ │ ├── primes.py │ │ └── proc_pool.py │ ├── 21-async │ ├── README.rst │ ├── domains │ │ ├── README.rst │ │ ├── asyncio │ │ │ ├── blogdom.py │ │ │ ├── domaincheck.py │ │ │ └── domainlib.py │ │ └── curio │ │ │ ├── blogdom.py │ │ │ ├── domaincheck.py │ │ │ ├── domainlib.py │ │ │ └── requirements.txt │ └── mojifinder │ │ ├── README.md │ │ ├── bottle.py │ │ ├── charindex.py │ │ ├── requirements.txt │ │ ├── tcp_mojifinder.py │ │ ├── web_mojifinder.py │ │ └── web_mojifinder_bottle.py │ ├── 22-dyn-attr-prop │ ├── blackknight.py │ ├── bulkfood │ │ ├── bulkfood_v1.py │ │ ├── bulkfood_v2.py │ │ ├── bulkfood_v2b.py │ │ └── bulkfood_v2prop.py │ ├── doc_property.py │ ├── oscon │ │ ├── data │ │ │ └── osconfeed.json │ │ ├── explore0.py │ │ ├── explore1.py │ │ ├── explore2.py │ │ ├── osconfeed-sample.json │ │ ├── osconfeed-talk.json │ │ ├── osconfeed_explore.rst │ │ ├── runtests.sh │ │ ├── schedule_v1.py │ │ ├── schedule_v2.py │ │ ├── schedule_v3.py │ │ ├── schedule_v4.py │ │ ├── schedule_v4_hasattr.py │ │ ├── schedule_v5.py │ │ ├── test_schedule_v1.py │ │ ├── test_schedule_v2.py │ │ ├── test_schedule_v3.py │ │ ├── test_schedule_v4.py │ │ └── test_schedule_v5.py │ └── pseudo_construction.py │ ├── 23-descriptor │ ├── README.rst │ ├── bulkfood │ │ ├── bulkfood_v3.py │ │ ├── bulkfood_v4.py │ │ ├── bulkfood_v4c.py │ │ ├── bulkfood_v5.py │ │ ├── model_v4c.py │ │ └── model_v5.py │ ├── descriptorkinds.py │ ├── descriptorkinds_dump.py │ └── method_is_descriptor.py │ └── 24-class-metaprog │ ├── autoconst │ ├── autoconst.py │ └── autoconst_demo.py │ ├── bulkfood │ ├── README.md │ ├── bulkfood_v6.py │ ├── bulkfood_v7.py │ ├── bulkfood_v8.py │ ├── model_v6.py │ ├── model_v7.py │ └── model_v8.py │ ├── checked │ ├── decorator │ │ ├── checkeddeco.py │ │ ├── checkeddeco_demo.py │ │ └── checkeddeco_test.py │ ├── initsub │ │ ├── checked_demo.py │ │ ├── checkedlib.py │ │ └── checkedlib_test.py │ └── metaclass │ │ ├── checked_demo.py │ │ ├── checkedlib.py │ │ └── checkedlib_test.py │ ├── evaltime │ ├── builderlib.py │ ├── evaldemo.py │ ├── evaldemo_meta.py │ └── metalib.py │ ├── factories.py │ ├── factories_ducktyped.py │ ├── hours │ ├── hours.py │ └── hours_test.py │ ├── metabunch │ ├── README.md │ ├── from3.6 │ │ ├── bunch.py │ │ └── bunch_test.py │ ├── nutshell3e │ │ ├── bunch.py │ │ └── bunch_test.py │ ├── original │ │ ├── bunch.py │ │ └── bunch_test.py │ └── pre3.6 │ │ ├── bunch.py │ │ └── bunch_test.py │ ├── persistent │ ├── .gitignore │ ├── dblib.py │ ├── dblib_test.py │ ├── persistlib.py │ └── persistlib_test.py │ ├── qualname │ ├── fakedjango.py │ └── models.py │ ├── sentinel │ ├── sentinel.py │ └── sentinel_test.py │ ├── setattr │ └── example_from_leo.py │ ├── slots │ └── slots_timing.py │ ├── timeslice.py │ └── tinyenums │ ├── microenum.py │ ├── microenum_demo.py │ ├── nanoenum.py │ └── nanoenum_demo.py ├── como-gerar-o-livro.md ├── cover.jpg ├── deploy.sh ├── ferramentas ├── README.md ├── anchors.ini ├── fix_dunder.py ├── references_finder.py ├── toc-pt-br.txt └── xrefs_xparts.ini ├── gerar-epub.md ├── guia-de-estilo.adoc ├── images ├── despacho-duplo.graffle ├── flpy_0101.png ├── flpy_0102.png ├── flpy_0201.png ├── flpy_0202.png ├── flpy_0203.png ├── flpy_0204.png ├── flpy_0205.png ├── flpy_0301.png ├── flpy_0302.png ├── flpy_0401.png ├── flpy_0402.png ├── flpy_0403.png ├── flpy_0404.png ├── flpy_0405.png ├── flpy_0406.png ├── flpy_0407.png ├── flpy_0408.png ├── flpy_0601.png ├── flpy_0602.png ├── flpy_0603.png ├── flpy_0604.png ├── flpy_0701.png ├── flpy_0901.png ├── flpy_1001.png ├── flpy_1002.png ├── flpy_1101.png ├── flpy_1201.png ├── flpy_1202.png ├── flpy_1301.png ├── flpy_1302.png ├── flpy_1303.png ├── flpy_1304.png ├── flpy_1305.png ├── flpy_1306.png ├── flpy_1307.png ├── flpy_1308.png ├── flpy_1401.png ├── flpy_1402.png ├── flpy_1403.png ├── flpy_1404.png ├── flpy_1405.png ├── flpy_1601-EN.png ├── flpy_1601.png ├── flpy_1701.png ├── flpy_1801.png ├── flpy_1901.png ├── flpy_1902.png ├── flpy_1903.png ├── flpy_1904.png ├── flpy_2001.png ├── flpy_2101.png ├── flpy_2102.png ├── flpy_2103.png ├── flpy_2104.png ├── flpy_2105.png ├── flpy_2201.png ├── flpy_2301.png ├── flpy_2302.png ├── flpy_2303.png ├── flpy_2304.png ├── flpy_2401.png ├── flpy_2402.png ├── flpy_2403.png └── flpy_2404.png ├── links ├── FPY.LI.custom.htaccess ├── FPY.LI.htaccess ├── FPY.LI.htaccess.BACKUP ├── FPY.LI.short.htaccess ├── README.md ├── cap01-urls.txt ├── data │ ├── README.txt │ └── sample.htaccess ├── fpy-deploy.sh ├── prep1_shorten_urls.py ├── sample-urls.txt ├── shorten.py ├── shortener.py └── test_shortener.py ├── requirements.txt ├── ruff.toml └── volumes ├── 1 └── code │ ├── 01-data-model │ ├── README.md │ ├── data-model.ipynb │ ├── frenchdeck.doctest │ ├── frenchdeck.py │ ├── test.sh │ ├── vector2d.doctest │ └── vector2d.py │ ├── 02-array-seq │ ├── README.rst │ ├── array-seq.ipynb │ ├── bisect_demo.py │ ├── bisect_insort.py │ ├── lispy │ │ ├── py3.10 │ │ │ ├── examples_test.py │ │ │ ├── lis.py │ │ │ ├── lis_test.py │ │ │ └── quicksort.scm │ │ └── py3.9 │ │ │ ├── README.md │ │ │ ├── examples_test.py │ │ │ ├── lis.py │ │ │ └── lis_test.py │ ├── listcomp_speed.py │ ├── match_lat_lon.py │ ├── memoryviews.ipynb │ ├── metro_lat_lon.py │ └── test.sh │ ├── 03-dict-set │ ├── 03-dict-set.ipynb │ ├── README.md │ ├── dialcodes.py │ ├── index.py │ ├── index0.py │ ├── index_default.py │ ├── missing.py │ ├── py3.10 │ │ └── creator.py │ ├── strkeydict.py │ ├── strkeydict0.py │ ├── support │ │ ├── container_perftest.py │ │ ├── container_perftest_datagen.py │ │ └── hashdiff.py │ ├── transformdict.py │ └── zen.txt │ ├── 04-text-byte │ ├── .gitignore │ ├── README.rst │ ├── categories.py │ ├── charfinder │ │ ├── README.rst │ │ ├── cf.py │ │ └── test.sh │ ├── default_encodings.py │ ├── encodings-win10.txt │ ├── locale_sort.py │ ├── normeq.py │ ├── numerics_demo.py │ ├── ola.py │ ├── ramanujan.py │ ├── simplify.py │ ├── skin.py │ ├── stdout_check.py │ ├── syntax-msg.txt │ ├── two_flags.py │ ├── zwj_sample.ipynb │ ├── zwj_sample.png │ └── zwj_sample.py │ ├── 05-data-classes │ ├── README.asciidoc │ ├── cards.doctest │ ├── cards.py │ ├── cards_enum.py │ ├── class │ │ └── coordinates.py │ ├── dataclass │ │ ├── club.py │ │ ├── club_generic.py │ │ ├── club_wrong.py │ │ ├── coordinates.py │ │ ├── hackerclub.py │ │ ├── hackerclub_annotated.py │ │ ├── resource.py │ │ └── resource_repr.py │ ├── frenchdeck.doctest │ ├── frenchdeck.py │ ├── match_cities.py │ ├── meaning │ │ ├── demo_dc.py │ │ ├── demo_nt.py │ │ └── demo_plain.py │ └── typing_namedtuple │ │ ├── coordinates.py │ │ ├── coordinates2.py │ │ └── nocheck_demo.py │ └── 06-obj-ref │ ├── bus.py │ ├── haunted_bus.py │ └── twilight_bus.py ├── .gitignore ├── Prefacio.adoc ├── README.md ├── build-1.sh ├── experimentos ├── FPY_LI_errors.txt ├── all_urls.py ├── check_urls.py ├── fix_pythonfluente_urls.py ├── fix_xrefs_for_print.py ├── links-pyfl-web.txt ├── notas.txt ├── prep_print.py ├── pyfl_refs.txt ├── sample-urls.txt ├── sort_check_output.py ├── vol1-results-orca.txt ├── vol1-url-checked.txt ├── vol1-urls-checked.txt └── vol1-urls.txt ├── go_check.go ├── vol1.adoc └── xrefs-v1.adoc /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: weekly 8 | -------------------------------------------------------------------------------- /.github/workflows/publish.yaml: -------------------------------------------------------------------------------- 1 | name: Publish 2 | on: 3 | push: 4 | branches: 5 | - "main" 6 | 7 | jobs: 8 | Publish: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v4 12 | - uses: actions/setup-node@v4 13 | with: 14 | node-version: 18 15 | 16 | - name: Install asciidoctor 17 | run: npm install -g asciidoctor asciidoctor-chunker 18 | 19 | - name: Build 20 | run: asciidoctor Livro.adoc -o index.html 21 | 22 | # - name: Split 23 | # run: asciidoctor-chunker index.html -o output 24 | 25 | - name: Publish 26 | uses: appleboy/scp-action@master 27 | with: 28 | host: ${{ secrets.PYTHONFLUENTE_HOST }} 29 | username: ${{ secrets.PYTHONFLUENTE_USER }} 30 | password: ${{ secrets.PYTHONFLUENTE_PASSWORD }} 31 | port: 22 32 | source: "index.html" 33 | # source: "output/*" 34 | target: "~/pythonfluente.com/" 35 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "asciidoc.antora.enableAntoraSupport": true 3 | } -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e # exit when any command fails 3 | asciidoctor $1 Livro.adoc -o index.html 4 | open index.html 5 | -------------------------------------------------------------------------------- /callouts/1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/1.pdf -------------------------------------------------------------------------------- /callouts/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/1.png -------------------------------------------------------------------------------- /callouts/10.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/10.pdf -------------------------------------------------------------------------------- /callouts/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/10.png -------------------------------------------------------------------------------- /callouts/11.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/11.pdf -------------------------------------------------------------------------------- /callouts/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/11.png -------------------------------------------------------------------------------- /callouts/12.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/12.pdf -------------------------------------------------------------------------------- /callouts/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/12.png -------------------------------------------------------------------------------- /callouts/13.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/13.pdf -------------------------------------------------------------------------------- /callouts/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/13.png -------------------------------------------------------------------------------- /callouts/14.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/14.pdf -------------------------------------------------------------------------------- /callouts/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/14.png -------------------------------------------------------------------------------- /callouts/15.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/15.pdf -------------------------------------------------------------------------------- /callouts/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/15.png -------------------------------------------------------------------------------- /callouts/16.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/16.pdf -------------------------------------------------------------------------------- /callouts/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/16.png -------------------------------------------------------------------------------- /callouts/17.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/17.pdf -------------------------------------------------------------------------------- /callouts/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/17.png -------------------------------------------------------------------------------- /callouts/18.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/18.pdf -------------------------------------------------------------------------------- /callouts/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/18.png -------------------------------------------------------------------------------- /callouts/19.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/19.pdf -------------------------------------------------------------------------------- /callouts/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/19.png -------------------------------------------------------------------------------- /callouts/2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/2.pdf -------------------------------------------------------------------------------- /callouts/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/2.png -------------------------------------------------------------------------------- /callouts/20.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/20.pdf -------------------------------------------------------------------------------- /callouts/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/20.png -------------------------------------------------------------------------------- /callouts/21.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/21.pdf -------------------------------------------------------------------------------- /callouts/21.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/21.png -------------------------------------------------------------------------------- /callouts/22.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/22.pdf -------------------------------------------------------------------------------- /callouts/22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/22.png -------------------------------------------------------------------------------- /callouts/23.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/23.pdf -------------------------------------------------------------------------------- /callouts/23.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/23.png -------------------------------------------------------------------------------- /callouts/24.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/24.pdf -------------------------------------------------------------------------------- /callouts/24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/24.png -------------------------------------------------------------------------------- /callouts/25.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/25.pdf -------------------------------------------------------------------------------- /callouts/25.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/25.png -------------------------------------------------------------------------------- /callouts/26.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/26.pdf -------------------------------------------------------------------------------- /callouts/26.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/26.png -------------------------------------------------------------------------------- /callouts/27.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/27.pdf -------------------------------------------------------------------------------- /callouts/27.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/27.png -------------------------------------------------------------------------------- /callouts/28.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/28.pdf -------------------------------------------------------------------------------- /callouts/28.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/28.png -------------------------------------------------------------------------------- /callouts/29.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/29.pdf -------------------------------------------------------------------------------- /callouts/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/29.png -------------------------------------------------------------------------------- /callouts/3.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/3.pdf -------------------------------------------------------------------------------- /callouts/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/3.png -------------------------------------------------------------------------------- /callouts/30.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/30.pdf -------------------------------------------------------------------------------- /callouts/30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/30.png -------------------------------------------------------------------------------- /callouts/31.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/31.pdf -------------------------------------------------------------------------------- /callouts/31.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/31.png -------------------------------------------------------------------------------- /callouts/32.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/32.pdf -------------------------------------------------------------------------------- /callouts/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/32.png -------------------------------------------------------------------------------- /callouts/33.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/33.pdf -------------------------------------------------------------------------------- /callouts/33.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/33.png -------------------------------------------------------------------------------- /callouts/34.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/34.pdf -------------------------------------------------------------------------------- /callouts/34.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/34.png -------------------------------------------------------------------------------- /callouts/35.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/35.pdf -------------------------------------------------------------------------------- /callouts/35.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/35.png -------------------------------------------------------------------------------- /callouts/36.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/36.pdf -------------------------------------------------------------------------------- /callouts/36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/36.png -------------------------------------------------------------------------------- /callouts/37.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/37.pdf -------------------------------------------------------------------------------- /callouts/37.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/37.png -------------------------------------------------------------------------------- /callouts/38.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/38.pdf -------------------------------------------------------------------------------- /callouts/38.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/38.png -------------------------------------------------------------------------------- /callouts/39.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/39.pdf -------------------------------------------------------------------------------- /callouts/39.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/39.png -------------------------------------------------------------------------------- /callouts/4.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/4.pdf -------------------------------------------------------------------------------- /callouts/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/4.png -------------------------------------------------------------------------------- /callouts/5.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/5.pdf -------------------------------------------------------------------------------- /callouts/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/5.png -------------------------------------------------------------------------------- /callouts/6.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/6.pdf -------------------------------------------------------------------------------- /callouts/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/6.png -------------------------------------------------------------------------------- /callouts/7.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/7.pdf -------------------------------------------------------------------------------- /callouts/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/7.png -------------------------------------------------------------------------------- /callouts/8.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/8.pdf -------------------------------------------------------------------------------- /callouts/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/8.png -------------------------------------------------------------------------------- /callouts/9.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/9.pdf -------------------------------------------------------------------------------- /callouts/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/callouts/9.png -------------------------------------------------------------------------------- /capitulos/code/01-data-model/README.md: -------------------------------------------------------------------------------- 1 | # The Python Data Model 2 | 3 | Sample code for Chapter 1 of _Fluent Python 2e_ by Luciano Ramalho (O'Reilly, 2020) 4 | 5 | ## Running the tests 6 | 7 | ### Doctests 8 | 9 | Use Python's standard ``doctest`` module to check stand-alone doctest file: 10 | 11 | $ python3 -m doctest frenchdeck.doctest -v 12 | 13 | And to check doctests embedded in a module: 14 | 15 | $ python3 -m doctest vector2d.py -v 16 | 17 | ### Jupyter Notebook 18 | 19 | Install ``pytest`` and the ``nbval`` plugin: 20 | 21 | $ pip install pytest nbval 22 | 23 | Run: 24 | 25 | $ pytest --nbval 26 | -------------------------------------------------------------------------------- /capitulos/code/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 | -------------------------------------------------------------------------------- /capitulos/code/01-data-model/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 -m doctest frenchdeck.doctest 3 | python3 -m doctest vector2d.py 4 | pytest -q --nbval 5 | -------------------------------------------------------------------------------- /capitulos/code/01-data-model/vector2d.doctest: -------------------------------------------------------------------------------- 1 | >>> from vector2d import Vector 2 | >>> v1 = Vector(2, 4) 3 | >>> v2 = Vector(2, 1) 4 | >>> v1 + v2 5 | Vector(4, 5) 6 | >>> v = Vector(3, 4) 7 | >>> abs(v) 8 | 5.0 9 | >>> v * 3 10 | Vector(9, 12) 11 | >>> abs(v * 3) 12 | 15.0 13 | -------------------------------------------------------------------------------- /capitulos/code/02-array-seq/README.rst: -------------------------------------------------------------------------------- 1 | Sample code for Chapter 2 - "An array of sequences" 2 | 3 | From the book "Fluent Python 2e" by Luciano Ramalho (O'Reilly) 4 | http://shop.oreilly.com/product/0636920032519.do 5 | -------------------------------------------------------------------------------- /capitulos/code/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(f'{new_item:2d} -> {my_list}') 13 | -------------------------------------------------------------------------------- /capitulos/code/02-array-seq/lispy/py3.10/quicksort.scm: -------------------------------------------------------------------------------- 1 | (define (quicksort lst) 2 | (if (null? lst) 3 | lst 4 | (begin 5 | (define pivot (car lst)) 6 | (define rest (cdr lst)) 7 | (append 8 | (quicksort 9 | (filter (lambda (x) (< x pivot)) rest)) 10 | (list pivot) 11 | (quicksort 12 | (filter (lambda (x) (>= x pivot)) rest))) 13 | ) 14 | ) 15 | ) 16 | (display 17 | (quicksort (list 2 1 6 3 4 0 8 9 7 5))) 18 | -------------------------------------------------------------------------------- /capitulos/code/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, *(f'{x:.3f}' 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 | -------------------------------------------------------------------------------- /capitulos/code/02-array-seq/metro_lat_lon.py: -------------------------------------------------------------------------------- 1 | """ 2 | metro_lat_lon.py 3 | 4 | Demonstration of nested tuple unpacking:: 5 | 6 | >>> main() 7 | | latitude | longitude 8 | Mexico City | 19.4333 | -99.1333 9 | New York-Newark | 40.8086 | -74.0204 10 | São Paulo | -23.5478 | -46.6358 11 | 12 | """ 13 | 14 | # tag::MAIN[] 15 | metro_areas = [ 16 | ('Tokyo', 'JP', 36.933, (35.689722, 139.691667)), # <1> 17 | ('Delhi NCR', 'IN', 21.935, (28.613889, 77.208889)), 18 | ('Mexico City', 'MX', 20.142, (19.433333, -99.133333)), 19 | ('New York-Newark', 'US', 20.104, (40.808611, -74.020386)), 20 | ('São Paulo', 'BR', 19.649, (-23.547778, -46.635833)), 21 | ] 22 | 23 | def main(): 24 | print(f'{"":15} | {"latitude":>9} | {"longitude":>9}') 25 | for name, _, _, (lat, lon) in metro_areas: # <2> 26 | if lon <= 0: # <3> 27 | print(f'{name:15} | {lat:9.4f} | {lon:9.4f}') 28 | 29 | if __name__ == '__main__': 30 | main() 31 | # end::MAIN[] -------------------------------------------------------------------------------- /capitulos/code/02-array-seq/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 -m doctest bisect_demo.py 3 | python3 -m doctest metro_lat_long.py 4 | pytest -q --nbval 5 | -------------------------------------------------------------------------------- /capitulos/code/03-dict-set/README.md: -------------------------------------------------------------------------------- 1 | # Dictionaries and Sets 2 | 3 | Sample code for Chapter 3 of _Fluent Python 2e_ by Luciano Ramalho (O'Reilly, 2020) 4 | 5 | ## Running the tests 6 | 7 | ### Doctests 8 | 9 | Use Python's standard ``doctest`` module, for example: 10 | 11 | $ python3 -m doctest bisect_demo.py -v 12 | 13 | ### Jupyter Notebook 14 | 15 | Install ``pytest`` and the ``nbval`` plugin: 16 | 17 | $ pip install pytest nbval 18 | 19 | Run: 20 | 21 | $ pytest --nbval -------------------------------------------------------------------------------- /capitulos/code/03-dict-set/dialcodes.py: -------------------------------------------------------------------------------- 1 | # tag::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 | # tag::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 | -------------------------------------------------------------------------------- /capitulos/code/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 | # tag::INDEX[] 6 | """Build an index mapping word -> list of occurrences""" 7 | 8 | import re 9 | import sys 10 | 11 | WORD_RE = re.compile(r'\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 | # display in alphabetical order 23 | for word in sorted(index, key=str.upper): 24 | print(word, index[word]) 25 | # end::INDEX[] 26 | -------------------------------------------------------------------------------- /capitulos/code/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 | # tag::INDEX_DEFAULT[] 6 | """Build an index mapping word -> list of occurrences""" 7 | 8 | import collections 9 | import re 10 | import sys 11 | 12 | WORD_RE = re.compile(r'\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 | # display in alphabetical order 24 | for word in sorted(index, key=str.upper): 25 | print(word, index[word]) 26 | # end::INDEX_DEFAULT[] 27 | -------------------------------------------------------------------------------- /capitulos/code/03-dict-set/support/hashdiff.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | MAX_BITS = len(format(sys.maxsize, 'b')) 4 | print(f'{MAX_BITS + 1}-bit Python build') 5 | 6 | def hash_diff(o1, o2): 7 | h1 = f'{hash(o1):>0{MAX_BITS}b}' 8 | h2 = f'{hash(o2):>0{MAX_BITS}b}' 9 | diff = ''.join('!' if b1 != b2 else ' ' for b1, b2 in zip(h1, h2)) 10 | count = f'!= {diff.count("!")}' 11 | width = max(len(repr(o1)), len(repr(o2)), 8) 12 | sep = '-' * (width * 2 + MAX_BITS) 13 | return (f'{o1!r:{width}} {h1}\n{" ":{width}} {diff} {count}\n' 14 | f'{o2!r:{width}} {h2}\n{sep}') 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 | -------------------------------------------------------------------------------- /capitulos/code/03-dict-set/zen.txt: -------------------------------------------------------------------------------- 1 | The Zen of Python, by Tim Peters 2 | 3 | Beautiful is better than ugly. 4 | Explicit is better than implicit. 5 | Simple is better than complex. 6 | Complex is better than complicated. 7 | Flat is better than nested. 8 | Sparse is better than dense. 9 | Readability counts. 10 | Special cases aren't special enough to break the rules. 11 | Although practicality beats purity. 12 | Errors should never pass silently. 13 | Unless explicitly silenced. 14 | In the face of ambiguity, refuse the temptation to guess. 15 | There should be one-- and preferably only one --obvious way to do it. 16 | Although that way may not be obvious at first unless you're Dutch. 17 | Now is better than never. 18 | Although never is often better than *right* now. 19 | If the implementation is hard to explain, it's a bad idea. 20 | If the implementation is easy to explain, it may be a good idea. 21 | Namespaces are one honking great idea -- let's do more of those! 22 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/.gitignore: -------------------------------------------------------------------------------- 1 | dummy 2 | -------------------------------------------------------------------------------- /capitulos/code/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 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/charfinder/cf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import unicodedata 4 | 5 | START, END = ord(' '), sys.maxunicode + 1 # <1> 6 | 7 | def find(*query_words, start=START, end=END): # <2> 8 | query = {w.upper() for w in query_words} # <3> 9 | for code in range(start, end): 10 | char = chr(code) # <4> 11 | name = unicodedata.name(char, None) # <5> 12 | if name and query.issubset(name.split()): # <6> 13 | print(f'U+{code:04X}\t{char}\t{name}') # <7> 14 | 15 | def main(words): 16 | if words: 17 | find(*words) 18 | else: 19 | print('Please provide words to find.') 20 | 21 | if __name__ == '__main__': 22 | main(sys.argv[1:]) 23 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/charfinder/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 -m doctest README.rst $1 3 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/default_encodings.py: -------------------------------------------------------------------------------- 1 | import locale 2 | import sys 3 | 4 | expressions = """ 5 | locale.getpreferredencoding() 6 | type(my_file) 7 | my_file.encoding 8 | sys.stdout.isatty() 9 | sys.stdout.encoding 10 | sys.stdin.isatty() 11 | sys.stdin.encoding 12 | sys.stderr.isatty() 13 | sys.stderr.encoding 14 | sys.getdefaultencoding() 15 | sys.getfilesystemencoding() 16 | """ 17 | 18 | my_file = open('dummy', 'w') 19 | 20 | for expression in expressions.split(): 21 | value = eval(expression) 22 | print(f'{expression:>30} -> {value!r}') 23 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/encodings-win10.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/capitulos/code/04-text-byte/encodings-win10.txt -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/locale_sort.py: -------------------------------------------------------------------------------- 1 | import locale 2 | my_locale = locale.setlocale(locale.LC_COLLATE, 'pt_BR.UTF-8') 3 | print(my_locale) 4 | fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola'] 5 | sorted_fruits = sorted(fruits, key=locale.strxfrm) 6 | print(sorted_fruits) 7 | -------------------------------------------------------------------------------- /capitulos/code/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 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/numerics_demo.py: -------------------------------------------------------------------------------- 1 | # tag::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(f'U+{ord(char):04x}', # <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 | f'{unicodedata.numeric(char):5.2f}', # <6> 16 | unicodedata.name(char), # <7> 17 | sep='\t') 18 | # end::NUMERICS_DEMO[] 19 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/ola.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/capitulos/code/04-text-byte/ola.py -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/ramanujan.py: -------------------------------------------------------------------------------- 1 | # tag::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(f'Text\n {text_str!r}') 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 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/skin.py: -------------------------------------------------------------------------------- 1 | from unicodedata import name 2 | 3 | SKIN1 = 0x1F3FB # EMOJI MODIFIER FITZPATRICK TYPE-1-2 # <1> 4 | SKINS = [chr(i) for i in range(SKIN1, SKIN1 + 5)] # <2> 5 | THUMB = '\U0001F44d' # THUMBS UP SIGN 👍 6 | 7 | examples = [THUMB] # <3> 8 | examples.extend(THUMB + skin for skin in SKINS) # <4> 9 | 10 | for example in examples: 11 | print(example, end='\t') # <5> 12 | print(' + '.join(name(char) for char in example)) # <6> 13 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/stdout_check.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from unicodedata import name 3 | 4 | print(sys.version) 5 | print() 6 | print('sys.stdout.isatty():', sys.stdout.isatty()) 7 | print('sys.stdout.encoding:', sys.stdout.encoding) 8 | print() 9 | 10 | test_chars = [ 11 | '\N{HORIZONTAL ELLIPSIS}', # exists in cp1252, not in cp437 12 | '\N{INFINITY}', # exists in cp437, not in cp1252 13 | '\N{CIRCLED NUMBER FORTY TWO}', # not in cp437 or in cp1252 14 | ] 15 | 16 | for char in test_chars: 17 | print(f'Trying to output {name(char)}:') 18 | print(char) 19 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/syntax-msg.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Non-UTF-8 code starting with '\xe1' in file ola.py on line 2 | 1, but no encoding declared; see https://python.org/dev/peps/pep-0263/ 3 | for details -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/two_flags.py: -------------------------------------------------------------------------------- 1 | # REGIONAL INDICATOR SYMBOLS 2 | RIS_A = '\U0001F1E6' # LETTER A 3 | RIS_U = '\U0001F1FA' # LETTER U 4 | print(RIS_A + RIS_U) # AU: Australia 5 | print(RIS_U + RIS_A) # UA: Ukraine 6 | print(RIS_A + RIS_A) # AA: no such country 7 | -------------------------------------------------------------------------------- /capitulos/code/04-text-byte/zwj_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/capitulos/code/04-text-byte/zwj_sample.png -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/README.asciidoc: -------------------------------------------------------------------------------- 1 | == Record-like Structures 2 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/cards.doctest: -------------------------------------------------------------------------------- 1 | >>> from cards import Card 2 | >>> helen = Card('Q', 'hearts') 3 | >>> helen 4 | Card(rank='Q', suit='hearts') 5 | 6 | >>> cards = [Card(r, s) for s in Card.suits for r in Card.ranks] 7 | >>> cards[:3] 8 | [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')] 9 | >>> sorted(cards)[:3] 10 | [Card(rank='2', suit='clubs'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='hearts')] 11 | 12 | >>> from cards_enum import Card, Suit, Rank 13 | >>> helen = Card('Q', 'hearts') 14 | >>> helen 15 | Card(rank='Q', suit='hearts') 16 | 17 | >>> cards = [Card(r, s) for s in Suit for r in Rank] 18 | >>> cards[:3] 19 | [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')] 20 | >>> sorted(cards)[:3] 21 | [Card(rank='2', suit='clubs'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='hearts')] 22 | >>> for card in cards[12::13]: print(card) 23 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/cards.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | @dataclass(order=True) 4 | class Card: 5 | rank: str 6 | suit: str 7 | 8 | ranks = [str(n) for n in range(2, 10)] + list('JQKA') 9 | suits = 'spades diamonds clubs hearts'.split() 10 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/cards_enum.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | import enum 3 | 4 | Suit = enum.IntEnum('Suit', 'spades diamonds clubs hearts') 5 | Rank = enum.Enum('Rank', [str(n) for n in range(2, 10)] + list('JQKA')) 6 | 7 | @dataclass(order=True) 8 | class Card: 9 | rank: Suit 10 | suit: Rank 11 | 12 | def __str__(self): 13 | glyphs = [chr(x) for x in range(0x2660, 0x2664)] 14 | return f'{self.rank} of {glyphs[self.suit-1]}' 15 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/class/coordinates.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: a simple class with a custom ``__str__``:: 3 | 4 | >>> moscow = Coordinate(55.756, 37.617) 5 | >>> print(moscow) # doctest:+ELLIPSIS 6 | 7 | """ 8 | 9 | # tag::COORDINATE[] 10 | class Coordinate: 11 | 12 | def __init__(self, lat, lon): 13 | self.lat = lat 14 | self.lon = lon 15 | 16 | # end::COORDINATE[] -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/dataclass/club.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | 3 | @dataclass 4 | class ClubMember: 5 | name: str 6 | guests: list = field(default_factory=list) 7 | 8 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/dataclass/club_generic.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | 3 | @dataclass 4 | class ClubMember: 5 | name: str 6 | guests: list[str] = field(default_factory=list) # <1> 7 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/dataclass/club_wrong.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | # tag::CLUBMEMBER[] 4 | @dataclass 5 | class ClubMember: 6 | name: str 7 | guests: list = [] 8 | # end::CLUBMEMBER[] 9 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/dataclass/coordinates.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: simple class decorated with ``dataclass`` and a custom ``__str__``:: 3 | 4 | >>> moscow = Coordinate(55.756, 37.617) 5 | >>> print(moscow) 6 | 55.8°N, 37.6°E 7 | 8 | """ 9 | 10 | # tag::COORDINATE[] 11 | 12 | from dataclasses import dataclass 13 | 14 | @dataclass(frozen=True) 15 | class Coordinate: 16 | lat: float 17 | lon: float 18 | 19 | def __str__(self): 20 | ns = 'N' if self.lat >= 0 else 'S' 21 | we = 'E' if self.lon >= 0 else 'W' 22 | return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}' 23 | # end::COORDINATE[] 24 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/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 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/meaning/demo_dc.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | @dataclass 4 | class DemoDataClass: 5 | a: int # <1> 6 | b: float = 1.1 # <2> 7 | c = 'spam' # <3> 8 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/meaning/demo_nt.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | class DemoNTClass(typing.NamedTuple): 4 | a: int # <1> 5 | b: float = 1.1 # <2> 6 | c = 'spam' # <3> 7 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/meaning/demo_plain.py: -------------------------------------------------------------------------------- 1 | class DemoPlainClass: 2 | a: int # <1> 3 | b: float = 1.1 # <2> 4 | c = 'spam' # <3> 5 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/typing_namedtuple/coordinates.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: a simple ``NamedTuple`` subclass with a custom ``__str__``:: 3 | 4 | >>> moscow = Coordinate(55.756, 37.617) 5 | >>> print(moscow) 6 | 55.8°N, 37.6°E 7 | 8 | """ 9 | 10 | # tag::COORDINATE[] 11 | from typing import NamedTuple 12 | 13 | class Coordinate(NamedTuple): 14 | lat: float 15 | lon: float 16 | 17 | def __str__(self): 18 | ns = 'N' if self.lat >= 0 else 'S' 19 | we = 'E' if self.lon >= 0 else 'W' 20 | return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}' 21 | # end::COORDINATE[] 22 | -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/typing_namedtuple/coordinates2.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: a simple ``NamedTuple`` subclass 3 | 4 | This version has a field with a default value:: 5 | 6 | >>> moscow = Coordinate(55.756, 37.617) 7 | >>> moscow 8 | Coordinate(lat=55.756, lon=37.617, reference='WGS84') 9 | 10 | """ 11 | 12 | # tag::COORDINATE[] 13 | from typing import NamedTuple 14 | 15 | class Coordinate(NamedTuple): 16 | lat: float # <1> 17 | lon: float 18 | reference: str = 'WGS84' # <2> 19 | # end::COORDINATE[] -------------------------------------------------------------------------------- /capitulos/code/05-data-classes/typing_namedtuple/nocheck_demo.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | class Coordinate(typing.NamedTuple): 4 | lat: float 5 | lon: float 6 | 7 | trash = Coordinate('Ni!', None) # <1> 8 | print(trash) 9 | -------------------------------------------------------------------------------- /capitulos/code/06-obj-ref/bus.py: -------------------------------------------------------------------------------- 1 | """ 2 | >>> import copy 3 | >>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David']) 4 | >>> bus2 = copy.copy(bus1) 5 | >>> bus3 = copy.deepcopy(bus1) 6 | >>> bus1.drop('Bill') 7 | >>> bus2.passengers 8 | ['Alice', 'Claire', 'David'] 9 | >>> bus3.passengers 10 | ['Alice', 'Bill', 'Claire', 'David'] 11 | 12 | """ 13 | 14 | # tag::BUS_CLASS[] 15 | class Bus: 16 | 17 | def __init__(self, passengers=None): 18 | if passengers is None: 19 | self.passengers = [] 20 | else: 21 | self.passengers = list(passengers) 22 | 23 | def pick(self, name): 24 | self.passengers.append(name) 25 | 26 | def drop(self, name): 27 | self.passengers.remove(name) 28 | # end::BUS_CLASS[] 29 | -------------------------------------------------------------------------------- /capitulos/code/06-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 | # tag::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 | -------------------------------------------------------------------------------- /capitulos/code/07-1class-func/README.rst: -------------------------------------------------------------------------------- 1 | Sample code for Chapter 7 - "First-class functions" 2 | 3 | From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2020) 4 | http://shop.oreilly.com/product/0636920273196.do -------------------------------------------------------------------------------- /capitulos/code/07-1class-func/bingocall.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::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 | # tag::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 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/README.asciidoc: -------------------------------------------------------------------------------- 1 | == Type Hints in Function Definitions 2 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/arg_lab.py: -------------------------------------------------------------------------------- 1 | import typing 2 | from typing import Optional 3 | 4 | 5 | def f(a: str, *b: int, **c: float) -> None: 6 | if typing.TYPE_CHECKING: 7 | # reveal_type(b) 8 | reveal_type(c) 9 | print(a, b, c) 10 | 11 | 12 | def g(__a: int) -> None: 13 | print(__a) 14 | 15 | 16 | def h(a: int, /) -> None: 17 | print(a) 18 | 19 | 20 | def tag( 21 | name: str, 22 | /, 23 | *content: str, 24 | class_: Optional[str] = None, 25 | foo: Optional[str] = None, 26 | **attrs: str, 27 | ) -> str: 28 | return repr((name, content, class_, attrs)) 29 | 30 | 31 | f(a='1') 32 | f('1', 2, 3, x=4, y=5) 33 | g(__a=1) 34 | # h(a=1) 35 | print(tag('li', 'first', 'second', id='#123')) 36 | print(tag('li', 'first', 'second', class_='menu', id='#123')) 37 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/birds/birds.py: -------------------------------------------------------------------------------- 1 | class Bird: 2 | pass 3 | 4 | class Duck(Bird): # <1> 5 | def quack(self): 6 | print('Quack!') 7 | 8 | def alert(birdie): # <2> 9 | birdie.quack() 10 | 11 | def alert_duck(birdie: Duck) -> None: # <3> 12 | birdie.quack() 13 | 14 | def alert_bird(birdie: Bird) -> None: # <4> 15 | birdie.quack() 16 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/birds/daffy.py: -------------------------------------------------------------------------------- 1 | from birds import * 2 | 3 | daffy = Duck() 4 | alert(daffy) # <1> 5 | alert_duck(daffy) # <2> 6 | alert_bird(daffy) # <3> 7 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/birds/protocol/lake.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol # <1> 2 | 3 | class GooseLike(Protocol): 4 | def honk(self, times: int) -> None: ... # <2> 5 | def swim(self) -> None: ... 6 | 7 | 8 | def alert(waterfowl: GooseLike) -> None: # <3> 9 | waterfowl.honk(2) 10 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/birds/protocol/parrot.py: -------------------------------------------------------------------------------- 1 | from lake import alert 2 | 3 | class Parrot: 4 | def honk(self, times: int) -> None: # <1> 5 | print('Honk! ' * times * 2) 6 | 7 | 8 | ze_carioca = Parrot() 9 | 10 | alert(ze_carioca) # <2> 11 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/birds/protocol/swan.py: -------------------------------------------------------------------------------- 1 | from lake import alert # <1> 2 | 3 | class Swan: # <2> 4 | def honk(self, repetitions: int) -> None: # <3> 5 | print('Honk! ' * repetitions) 6 | 7 | def swim(self) -> None: # <4> 8 | pass 9 | 10 | 11 | bella = Swan() 12 | 13 | alert(bella) # <5> 14 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/birds/woody.py: -------------------------------------------------------------------------------- 1 | from birds import * 2 | 3 | woody = Bird() 4 | alert(woody) 5 | alert_duck(woody) 6 | alert_bird(woody) 7 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/bus.py: -------------------------------------------------------------------------------- 1 | """ 2 | >>> import copy 3 | >>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David']) 4 | >>> bus2 = copy.copy(bus1) 5 | >>> bus3 = copy.deepcopy(bus1) 6 | >>> bus1.drop('Bill') 7 | >>> bus2.passengers 8 | ['Alice', 'Claire', 'David'] 9 | >>> bus3.passengers 10 | ['Alice', 'Bill', 'Claire', 'David'] 11 | 12 | """ 13 | 14 | # tag::BUS_CLASS[] 15 | class Bus: 16 | 17 | def __init__(self, passengers=None): 18 | if passengers is None: 19 | self.passengers = [] 20 | else: 21 | self.passengers = list(passengers) 22 | 23 | def pick(self, name): 24 | self.passengers.append(name) 25 | 26 | def drop(self, name): 27 | self.passengers.remove(name) 28 | # end::BUS_CLASS[] 29 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/callable/variance.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Callable 2 | 3 | def update( # <1> 4 | probe: Callable[[], float], # <2> 5 | display: Callable[[float], None] # <3> 6 | ) -> None: 7 | temperature = probe() 8 | # imagine lots of control code here 9 | display(temperature) 10 | 11 | def probe_ok() -> int: # <4> 12 | return 42 13 | 14 | def display_wrong(temperature: int) -> None: # <5> 15 | print(hex(temperature)) 16 | 17 | update(probe_ok, display_wrong) # type error # <6> 18 | 19 | def display_ok(temperature: complex) -> None: # <7> 20 | print(temperature) 21 | 22 | update(probe_ok, display_ok) # OK # <8> 23 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/columnize.py: -------------------------------------------------------------------------------- 1 | # tag::COLUMNIZE[] 2 | from collections.abc import Sequence 3 | 4 | def columnize( 5 | sequence: Sequence[str], num_columns: int = 0 6 | ) -> list[tuple[str, ...]]: 7 | if num_columns == 0: 8 | num_columns = round(len(sequence) ** 0.5) 9 | num_rows, reminder = divmod(len(sequence), num_columns) 10 | num_rows += bool(reminder) 11 | return [tuple(sequence[i::num_rows]) for i in range(num_rows)] 12 | # end::COLUMNIZE[] 13 | 14 | 15 | def demo() -> None: 16 | nato = ( 17 | 'Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' 18 | ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' 19 | ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' 20 | ).split() 21 | 22 | for row in columnize(nato, 4): 23 | for word in row: 24 | print(f'{word:15}', end='') 25 | print() 26 | 27 | 28 | if __name__ == '__main__': 29 | demo() 30 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/comparable/comparable.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol, Any 2 | 3 | class SupportsLessThan(Protocol): # <1> 4 | def __lt__(self, other: Any) -> bool: ... # <2> 5 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/comparable/top.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``top(it, n)`` returns the "greatest" ``n`` elements of the iterable ``t``. 3 | Example: 4 | 5 | # tag::TOP_DOCTEST[] 6 | >>> top([4, 1, 5, 2, 6, 7, 3], 3) 7 | [7, 6, 5] 8 | >>> l = 'mango pear apple kiwi banana'.split() 9 | >>> top(l, 3) 10 | ['pear', 'mango', 'kiwi'] 11 | >>> 12 | >>> l2 = [(len(s), s) for s in l] 13 | >>> l2 14 | [(5, 'mango'), (4, 'pear'), (5, 'apple'), (4, 'kiwi'), (6, 'banana')] 15 | >>> top(l2, 3) 16 | [(6, 'banana'), (5, 'mango'), (5, 'apple')] 17 | 18 | # end::TOP_DOCTEST[] 19 | 20 | """ 21 | 22 | # tag::TOP[] 23 | from collections.abc import Iterable 24 | from typing import TypeVar 25 | 26 | from comparable import SupportsLessThan 27 | 28 | LT = TypeVar('LT', bound=SupportsLessThan) 29 | 30 | def top(series: Iterable[LT], length: int) -> list[LT]: 31 | ordered = sorted(series, reverse=True) 32 | return ordered[:length] 33 | # end::TOP[] 34 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/coordinates/coordinates.py: -------------------------------------------------------------------------------- 1 | # This example uses the geolib library: 2 | # https://pypi.org/project/geolib/ 3 | 4 | """ 5 | >>> shanghai = 31.2304, 121.4737 6 | >>> geohash(shanghai) 7 | 'wtw3sjq6q' 8 | """ 9 | 10 | # tag::GEOHASH[] 11 | from geolib import geohash as gh # type: ignore # <1> 12 | 13 | PRECISION = 9 14 | 15 | def geohash(lat_lon: tuple[float, float]) -> str: # <2> 16 | return gh.encode(*lat_lon, PRECISION) 17 | # end::GEOHASH[] 18 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/coordinates/coordinates_named_test.py: -------------------------------------------------------------------------------- 1 | from coordinates_named import geohash, Coordinate, display 2 | 3 | def test_geohash_max_precision() -> None: 4 | sao_paulo = -23.5505, -46.6339 5 | result = geohash(Coordinate(*sao_paulo)) 6 | assert '6gyf4bf0r' == result 7 | 8 | def test_display() -> None: 9 | sao_paulo = -23.5505, -46.6339 10 | assert display(sao_paulo) == '23.6°S, 46.6°W' 11 | shanghai = 31.2304, 121.4737 12 | assert display(shanghai) == '31.2°N, 121.5°E' 13 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/coordinates/coordinates_test.py: -------------------------------------------------------------------------------- 1 | from coordinates import geohash 2 | 3 | def test_geohash_max_precision() -> None: 4 | sao_paulo = -23.5505, -46.6339 5 | result = geohash(sao_paulo) 6 | assert '6gyf4bf0r' == result 7 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/coordinates/requirements.txt: -------------------------------------------------------------------------------- 1 | geolib==1.0.7 2 | future==0.18.2 3 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/ctime.py: -------------------------------------------------------------------------------- 1 | import time 2 | from typing import Optional 3 | 4 | def ctime(secs: Optional[float] = None, /) -> str: 5 | return time.ctime(secs) 6 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/double/double_object.py: -------------------------------------------------------------------------------- 1 | def double(n: object) -> object: 2 | return n * 2 3 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/double/double_protocol.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, Protocol 2 | 3 | T = TypeVar('T') # <1> 4 | 5 | class Repeatable(Protocol): 6 | def __mul__(self: T, other: int) -> T: ... # <2> 7 | 8 | RT = TypeVar('RT', bound=Repeatable) # <3> 9 | 10 | def double(n: RT) -> RT: # <4> 11 | return n * 2 12 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/double/double_sequence.py: -------------------------------------------------------------------------------- 1 | from collections import abc 2 | from typing import Any 3 | 4 | def double(n: abc.Sequence) -> Any: 5 | return n * 2 6 | 7 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/messages/hints_1/messages.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::SHOW_COUNT_DOCTEST[] 3 | >>> show_count(99, 'bird') 4 | '99 birds' 5 | >>> show_count(1, 'bird') 6 | '1 bird' 7 | >>> show_count(0, 'bird') 8 | 'no birds' 9 | 10 | # end::SHOW_COUNT_DOCTEST[] 11 | """ 12 | 13 | # tag::SHOW_COUNT[] 14 | def show_count(count: int, word: str) -> str: 15 | if count == 1: 16 | return f'1 {word}' 17 | count_str = str(count) if count else 'no' 18 | return f'{count_str} {word}s' 19 | # end::SHOW_COUNT[] 20 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/messages/hints_1/messages_test.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from messages import show_count 4 | 5 | 6 | @mark.parametrize('qty, expected', [ 7 | (1, '1 part'), 8 | (2, '2 parts'), 9 | ]) 10 | def test_show_count(qty: int, expected: str) -> None: 11 | got = show_count(qty, 'part') 12 | assert got == expected 13 | 14 | 15 | def test_show_count_zero(): 16 | got = show_count(0, 'part') 17 | assert got == 'no parts' 18 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/messages/hints_2/messages.py: -------------------------------------------------------------------------------- 1 | """ 2 | >>> show_count(99, 'bird') 3 | '99 birds' 4 | >>> show_count(1, 'bird') 5 | '1 bird' 6 | >>> show_count(0, 'bird') 7 | 'no birds' 8 | >>> show_count(3, 'virus', 'viruses') 9 | '3 viruses' 10 | >>> show_count(1, 'virus', 'viruses') 11 | '1 virus' 12 | >>> show_count(0, 'virus', 'viruses') 13 | 'no viruses' 14 | """ 15 | 16 | # tag::SHOW_COUNT[] 17 | def show_count(count: int, singular: str, plural: str = '') -> str: 18 | if count == 1: 19 | return f'1 {singular}' 20 | count_str = str(count) if count else 'no' 21 | if not plural: 22 | plural = singular + 's' 23 | return f'{count_str} {plural}' 24 | 25 | # end::SHOW_COUNT[] 26 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/messages/hints_2/messages_test.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from messages import show_count 4 | 5 | 6 | @mark.parametrize('qty, expected', [ 7 | (1, '1 part'), 8 | (2, '2 parts'), 9 | (0, 'no parts'), 10 | ]) 11 | def test_show_count(qty: int, expected: str) -> None: 12 | got = show_count(qty, 'part') 13 | assert got == expected 14 | 15 | 16 | # tag::TEST_IRREGULAR[] 17 | @mark.parametrize('qty, expected', [ 18 | (1, '1 child'), 19 | (2, '2 children'), 20 | (0, 'no children'), 21 | ]) 22 | def test_irregular(qty: int, expected: str) -> None: 23 | got = show_count(qty, 'child', 'children') 24 | assert got == expected 25 | # end::TEST_IRREGULAR[] 26 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/messages/no_hints/messages.py: -------------------------------------------------------------------------------- 1 | """ 2 | # tag::SHOW_COUNT_DOCTEST[] 3 | >>> show_count(99, 'bird') 4 | '99 birds' 5 | >>> show_count(1, 'bird') 6 | '1 bird' 7 | >>> show_count(0, 'bird') 8 | 'no birds' 9 | 10 | # end::SHOW_COUNT_DOCTEST[] 11 | """ 12 | 13 | # tag::SHOW_COUNT[] 14 | def show_count(count, word): 15 | if count == 1: 16 | return f'1 {word}' 17 | count_str = str(count) if count else 'no' 18 | return f'{count_str} {word}s' 19 | # end::SHOW_COUNT[] 20 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/messages/no_hints/messages_test.py: -------------------------------------------------------------------------------- 1 | from pytest import mark 2 | 3 | from messages import show_count 4 | 5 | @mark.parametrize('qty, expected', [ 6 | (1, '1 part'), 7 | (2, '2 parts'), 8 | ]) 9 | def test_show_count(qty, expected): 10 | got = show_count(qty, 'part') 11 | assert got == expected 12 | 13 | def test_show_count_zero(): 14 | got = show_count(0, 'part') 15 | assert got == 'no parts' 16 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/mode/mode_float.py: -------------------------------------------------------------------------------- 1 | # tag::MODE_FLOAT[] 2 | from collections import Counter 3 | from collections.abc import Iterable 4 | 5 | def mode(data: Iterable[float]) -> float: 6 | pairs = Counter(data).most_common(1) 7 | if len(pairs) == 0: 8 | raise ValueError('no mode for empty data') 9 | return pairs[0][0] 10 | # end::MODE_FLOAT[] 11 | 12 | def demo() -> None: 13 | import typing 14 | pop = [1, 1, 2, 3, 3, 3, 3, 4] 15 | m = mode(pop) 16 | if typing.TYPE_CHECKING: 17 | reveal_type(pop) 18 | reveal_type(m) 19 | print(pop) 20 | print(repr(m), type(m)) 21 | 22 | if __name__ == '__main__': 23 | demo() 24 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/mode/mode_hashable.py: -------------------------------------------------------------------------------- 1 | # tag::MODE_HASHABLE_T[] 2 | from collections import Counter 3 | from collections.abc import Iterable, Hashable 4 | from typing import TypeVar 5 | 6 | HashableT = TypeVar('HashableT', bound=Hashable) 7 | 8 | def mode(data: Iterable[HashableT]) -> HashableT: 9 | pairs = Counter(data).most_common(1) 10 | if len(pairs) == 0: 11 | raise ValueError('no mode for empty data') 12 | return pairs[0][0] 13 | # end::MODE_HASHABLE_T[] 14 | 15 | 16 | def demo() -> None: 17 | import typing 18 | 19 | pop = 'abracadabra' 20 | m = mode(pop) 21 | if typing.TYPE_CHECKING: 22 | reveal_type(pop) 23 | reveal_type(m) 24 | print(pop) 25 | print(m.upper(), type(m)) 26 | 27 | 28 | if __name__ == '__main__': 29 | demo() 30 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.9 3 | warn_unused_configs = True 4 | disallow_incomplete_defs = True 5 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/romans.py: -------------------------------------------------------------------------------- 1 | values_map = [ 2 | (1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1), 3 | ( 'M', 'CM', 'D', 'CD', 'C', 'XC', 'L', 'XL', 'X', 'IX', 'V', 'IV', 'I') 4 | ] 5 | 6 | def to_roman(arabic: int) -> str: 7 | """ Convert an integer to a Roman numeral. """ 8 | if not 0 < arabic < 4000: 9 | raise ValueError('Argument must be between 1 and 3999') 10 | 11 | result = [] 12 | for value, numeral in zip(*values_map): 13 | repeat = arabic // value 14 | result.append(numeral * repeat) 15 | arabic -= value * repeat 16 | return ''.join(result) 17 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/romans_test.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from romans import to_roman 4 | 5 | 6 | def test_to_roman_1(): 7 | assert to_roman(1) == 'I' 8 | 9 | 10 | @pytest.mark.parametrize('arabic, roman', [ 11 | (3, 'III'), 12 | (4, 'IV'), 13 | (1009, 'MIX'), 14 | (1969, 'MCMLXIX'), 15 | (3999, 'MMMCMXCIX') 16 | ]) 17 | def test_to_roman(arabic, roman): 18 | assert to_roman(arabic) == roman 19 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/sample.py: -------------------------------------------------------------------------------- 1 | # tag::SAMPLE[] 2 | from collections.abc import Sequence 3 | from random import shuffle 4 | from typing import TypeVar 5 | 6 | T = TypeVar('T') 7 | 8 | def sample(population: Sequence[T], size: int) -> list[T]: 9 | if size < 1: 10 | raise ValueError('size must be >= 1') 11 | result = list(population) 12 | shuffle(result) 13 | return result[:size] 14 | # end::SAMPLE[] 15 | 16 | def demo() -> None: 17 | import typing 18 | p1 = tuple(range(10)) 19 | s1 = sample(p1, 3) 20 | if typing.TYPE_CHECKING: 21 | reveal_type(p1) 22 | reveal_type(s1) 23 | print(p1) 24 | print(s1) 25 | p2 = 'The quick brown fox jumps over the lazy dog' 26 | s2 = sample(p2, 10) 27 | if typing.TYPE_CHECKING: 28 | reveal_type(p2) 29 | reveal_type(s2) 30 | print(p2) 31 | print(s2) 32 | 33 | 34 | if __name__ == '__main__': 35 | demo() 36 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/typevar_bounded.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, TYPE_CHECKING 2 | 3 | BT = TypeVar('BT', bound=float) 4 | 5 | def triple2(a: BT) -> BT: 6 | return a * 3 7 | 8 | res2 = triple2(2) 9 | 10 | if TYPE_CHECKING: 11 | reveal_type(res2) 12 | -------------------------------------------------------------------------------- /capitulos/code/08-def-type-hints/typevars_constrained.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, TYPE_CHECKING 2 | from decimal import Decimal 3 | 4 | # tag::TYPEVAR_RESTRICTED[] 5 | RT = TypeVar('RT', float, Decimal) 6 | 7 | def triple1(a: RT) -> RT: 8 | return a * 3 9 | 10 | res1 = triple1(2) 11 | 12 | if TYPE_CHECKING: 13 | reveal_type(res1) 14 | # end::TYPEVAR_RESTRICTED[] 15 | 16 | # tag::TYPEVAR_BOUNDED[] 17 | BT = TypeVar('BT', bound=float) 18 | 19 | def triple2(a: BT) -> BT: 20 | return a * 3 21 | 22 | res2 = triple2(2) 23 | 24 | if TYPE_CHECKING: 25 | reveal_type(res2) 26 | # tag::TYPEVAR_BOUNDED[] 27 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/README.rst: -------------------------------------------------------------------------------- 1 | Sample code for Chapter 8 - "Closures and decorators" 2 | 3 | From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2020) 4 | http://shop.oreilly.com/product/0636920273196.do -------------------------------------------------------------------------------- /capitulos/code/09-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 | -------------------------------------------------------------------------------- /capitulos/code/09-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 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/clock/clockdeco.py: -------------------------------------------------------------------------------- 1 | import time 2 | import functools 3 | 4 | 5 | def clock(func): 6 | @functools.wraps(func) 7 | def clocked(*args, **kwargs): 8 | t0 = time.perf_counter() 9 | result = func(*args, **kwargs) 10 | elapsed = time.perf_counter() - t0 11 | name = func.__name__ 12 | arg_lst = [repr(arg) for arg in args] 13 | arg_lst.extend(f'{k}={v!r}' for k, v in kwargs.items()) 14 | arg_str = ', '.join(arg_lst) 15 | print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}') 16 | return result 17 | return clocked 18 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/clock/clockdeco0.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def clock(func): 5 | def clocked(*args): # <1> 6 | t0 = time.perf_counter() 7 | result = func(*args) # <2> 8 | elapsed = time.perf_counter() - t0 9 | name = func.__name__ 10 | arg_str = ', '.join(repr(arg) for arg in args) 11 | print(f'[{elapsed:0.8f}s] {name}({arg_str}) -> {result!r}') 12 | return result 13 | return clocked # <3> 14 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/clock/clockdeco_demo.py: -------------------------------------------------------------------------------- 1 | import time 2 | from clockdeco0 import clock 3 | 4 | @clock 5 | def snooze(seconds): 6 | time.sleep(seconds) 7 | 8 | @clock 9 | def factorial(n): 10 | return 1 if n < 2 else n*factorial(n-1) 11 | 12 | if __name__ == '__main__': 13 | print('*' * 40, 'Calling snooze(.123)') 14 | snooze(.123) 15 | print('*' * 40, 'Calling factorial(6)') 16 | print('6! =', factorial(6)) 17 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/clock/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 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/clock/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 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/fibo_compare.py: -------------------------------------------------------------------------------- 1 | from clockdeco import clock 2 | import fibo_demo 3 | import fibo_demo_lru 4 | 5 | 6 | @clock 7 | def demo1(): 8 | fibo_demo.fibonacci(30) 9 | 10 | 11 | @clock 12 | def demo2(): 13 | fibo_demo_lru.fibonacci(30) 14 | 15 | 16 | demo1() 17 | demo2() 18 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/fibo_demo.py: -------------------------------------------------------------------------------- 1 | from clockdeco import clock 2 | 3 | 4 | @clock 5 | def fibonacci(n): 6 | if n < 2: 7 | return n 8 | return fibonacci(n - 2) + fibonacci(n - 1) 9 | 10 | 11 | if __name__ == '__main__': 12 | print(fibonacci(6)) 13 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/fibo_demo_cache.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | from clockdeco import clock 4 | 5 | 6 | @functools.cache # <1> 7 | @clock # <2> 8 | def fibonacci(n): 9 | if n < 2: 10 | return n 11 | return fibonacci(n - 2) + fibonacci(n - 1) 12 | 13 | 14 | if __name__ == '__main__': 15 | print(fibonacci(6)) 16 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/registration.py: -------------------------------------------------------------------------------- 1 | # tag::REGISTRATION[] 2 | 3 | registry = [] # <1> 4 | 5 | def register(func): # <2> 6 | print(f'running register({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[] -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/registration_abridged.py: -------------------------------------------------------------------------------- 1 | # tag::REGISTRATION_ABRIDGED[] 2 | registry = [] 3 | 4 | def register(func): 5 | print(f'running register({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 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/registration_param.py: -------------------------------------------------------------------------------- 1 | # tag::REGISTRATION_PARAM[] 2 | 3 | registry = set() # <1> 4 | 5 | def register(active=True): # <2> 6 | def decorate(func): # <3> 7 | print('running register' 8 | f'(active={active})->decorate({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 | -------------------------------------------------------------------------------- /capitulos/code/09-closure-deco/stacked.py: -------------------------------------------------------------------------------- 1 | def first(f): 2 | print(f'apply first({f.__name__})') 3 | 4 | def inner1st(n): 5 | result = f(n) 6 | print(f'inner1({n}): called {f.__name__}({n}) -> {result}') 7 | return result 8 | return inner1st 9 | 10 | 11 | def second(f): 12 | print(f'apply second({f.__name__})') 13 | 14 | def inner2nd(n): 15 | result = f(n) 16 | print(f'inner2({n}): called {f.__name__}({n}) -> {result}') 17 | return result 18 | return inner2nd 19 | 20 | 21 | @first 22 | @second 23 | def double(n): 24 | return n * 2 25 | 26 | 27 | print(double(3)) 28 | 29 | 30 | def double_(n): 31 | return n * 2 32 | 33 | 34 | double_ = first(second(double_)) 35 | 36 | print(double_(3)) 37 | -------------------------------------------------------------------------------- /capitulos/code/10-dp-1class-func/monkeytype/classic_strategy.pyi: -------------------------------------------------------------------------------- 1 | from typing import ( 2 | List, 3 | Optional, 4 | Union, 5 | ) 6 | 7 | 8 | class BulkItemPromo: 9 | def discount(self, order: Order) -> Union[float, int]: ... 10 | 11 | 12 | class FidelityPromo: 13 | def discount(self, order: Order) -> Union[float, int]: ... 14 | 15 | 16 | class LargeOrderPromo: 17 | def discount(self, order: Order) -> Union[float, int]: ... 18 | 19 | 20 | class LineItem: 21 | def __init__(self, product: str, quantity: int, price: float) -> None: ... 22 | def total(self) -> float: ... 23 | 24 | 25 | class Order: 26 | def __init__( 27 | self, 28 | customer: Customer, 29 | cart: List[LineItem], 30 | promotion: Optional[Union[BulkItemPromo, LargeOrderPromo, FidelityPromo]] = ... 31 | ) -> None: ... 32 | def due(self) -> float: ... 33 | def total(self) -> float: ... 34 | -------------------------------------------------------------------------------- /capitulos/code/10-dp-1class-func/monkeytype/run.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | pytest.main(['.']) 3 | -------------------------------------------------------------------------------- /capitulos/code/10-dp-1class-func/promotions.py: -------------------------------------------------------------------------------- 1 | from decimal import Decimal 2 | from strategy import Order 3 | 4 | def fidelity_promo(order: Order) -> Decimal: # <3> 5 | """5% discount for customers with 1000 or more fidelity points""" 6 | if order.customer.fidelity >= 1000: 7 | return order.total() * Decimal('0.05') 8 | return Decimal(0) 9 | 10 | 11 | def bulk_item_promo(order: Order) -> Decimal: 12 | """10% discount for each LineItem with 20 or more units""" 13 | discount = Decimal(0) 14 | for item in order.cart: 15 | if item.quantity >= 20: 16 | discount += item.total() * Decimal('0.1') 17 | return discount 18 | 19 | 20 | def large_order_promo(order: Order) -> Decimal: 21 | """7% discount for orders with 10 or more distinct items""" 22 | distinct_items = {item.product for item in order.cart} 23 | if len(distinct_items) >= 10: 24 | return order.total() * Decimal('0.07') 25 | return Decimal(0) 26 | -------------------------------------------------------------------------------- /capitulos/code/10-dp-1class-func/requirements.txt: -------------------------------------------------------------------------------- 1 | mypy==0.910 2 | pytest==6.2.4 3 | -------------------------------------------------------------------------------- /capitulos/code/10-dp-1class-func/untyped/promotions.py: -------------------------------------------------------------------------------- 1 | def fidelity_promo(order): 2 | """5% discount for customers with 1000 or more fidelity points""" 3 | return order.total() * .05 if order.customer.fidelity >= 1000 else 0 4 | 5 | 6 | def bulk_item_promo(order): 7 | """10% discount for each LineItem with 20 or more units""" 8 | discount = 0 9 | for item in order.cart: 10 | if item.quantity >= 20: 11 | discount += item.total() * .1 12 | return discount 13 | 14 | def large_order_promo(order): 15 | """7% discount for orders with 10 or more distinct items""" 16 | distinct_items = {item.product for item in order.cart} 17 | if len(distinct_items) >= 10: 18 | return order.total() * .07 19 | return 0 20 | -------------------------------------------------------------------------------- /capitulos/code/11-pythonic-obj/README.md: -------------------------------------------------------------------------------- 1 | # A Pythonic Object 2 | 3 | Sample code for Chapter 11 of _Fluent Python 2e_ by Luciano Ramalho (O'Reilly, 2020) 4 | 5 | The _memtest.py_ script takes a module name in the command line and loads it. 6 | Assuming the module defines a class named `Vector`, _memtest.py_ creates a list with 10 million instances, reporting the memory usage before and after the list is created. 7 | -------------------------------------------------------------------------------- /capitulos/code/11-pythonic-obj/mem_test.py: -------------------------------------------------------------------------------- 1 | import importlib 2 | import sys 3 | import resource 4 | 5 | NUM_VECTORS = 10**7 6 | 7 | module = None 8 | if len(sys.argv) == 2: 9 | module_name = sys.argv[1].replace('.py', '') 10 | module = importlib.import_module(module_name) 11 | else: 12 | print(f'Usage: {sys.argv[0]} ') 13 | 14 | if module is None: 15 | print('Running test with built-in `complex`') 16 | cls = complex 17 | else: 18 | fmt = 'Selected Vector2d type: {.__name__}.{.__name__}' 19 | print(fmt.format(module, module.Vector2d)) 20 | cls = module.Vector2d 21 | 22 | mem_init = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 23 | print(f'Creating {NUM_VECTORS:,} {cls.__qualname__!r} instances') 24 | 25 | vectors = [cls(3.0, 4.0) for i in range(NUM_VECTORS)] 26 | 27 | mem_final = resource.getrusage(resource.RUSAGE_SELF).ru_maxrss 28 | print(f'Initial RAM usage: {mem_init:14,}') 29 | print(f' Final RAM usage: {mem_final:14,}') 30 | -------------------------------------------------------------------------------- /capitulos/code/11-pythonic-obj/private/.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | .jython_cache/ 3 | -------------------------------------------------------------------------------- /capitulos/code/11-pythonic-obj/private/Confidential.java: -------------------------------------------------------------------------------- 1 | public class Confidential { 2 | 3 | private String secret = ""; 4 | 5 | public Confidential(String text) { 6 | this.secret = text.toUpperCase(); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /capitulos/code/11-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 setAccessible(true) 22 | System.err.println(e); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /capitulos/code/11-pythonic-obj/private/expose.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jython 2 | # NOTE: Jython is still Python 2.7 in late2020 3 | 4 | import Confidential 5 | 6 | message = Confidential('top secret text') 7 | secret_field = Confidential.getDeclaredField('secret') 8 | secret_field.setAccessible(True) # break the lock! 9 | print 'message.secret =', secret_field.get(message) 10 | -------------------------------------------------------------------------------- /capitulos/code/11-pythonic-obj/private/leakprivate.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jython 2 | # NOTE: Jython is still Python 2.7 in late2020 3 | 4 | from java.lang.reflect import Modifier 5 | import Confidential 6 | 7 | message = Confidential('top secret text') 8 | fields = Confidential.getDeclaredFields() 9 | for field in fields: 10 | # list private fields only 11 | if Modifier.isPrivate(field.getModifiers()): 12 | field.setAccessible(True) # break the lock 13 | print 'field:', field 14 | print '\t', field.getName(), '=', field.get(message) 15 | -------------------------------------------------------------------------------- /capitulos/code/11-pythonic-obj/private/no_respect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env jython 2 | # NOTE: Jython is still Python 2.7 in late2020 3 | 4 | """ 5 | In the Jython registry file there is this line: 6 | 7 | python.security.respectJavaAccessibility = true 8 | 9 | Set this to false and Jython provides access to non-public 10 | fields, methods, and constructors of Java objects. 11 | """ 12 | 13 | import Confidential 14 | 15 | message = Confidential('top secret text') 16 | for name in dir(message): 17 | attr = getattr(message, name) 18 | if not callable(attr): # non-methods only 19 | print name + '\t=', attr 20 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-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 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/bingo.py: -------------------------------------------------------------------------------- 1 | # tag::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): # <6> 26 | self.pick() 27 | 28 | # end::TOMBOLA_BINGO[] 29 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/double/double_object.py: -------------------------------------------------------------------------------- 1 | def double(x: object) -> object: 2 | return x * 2 3 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/double/double_protocol.py: -------------------------------------------------------------------------------- 1 | from typing import TypeVar, Protocol 2 | 3 | T = TypeVar('T') # <1> 4 | 5 | class Repeatable(Protocol): 6 | def __mul__(self: T, repeat_count: int) -> T: ... # <2> 7 | 8 | RT = TypeVar('RT', bound=Repeatable) # <3> 9 | 10 | def double(x: RT) -> RT: # <4> 11 | return x * 2 12 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/double/double_sequence.py: -------------------------------------------------------------------------------- 1 | from collections import abc 2 | from typing import Any 3 | 4 | def double(x: abc.Sequence) -> Any: 5 | return x * 2 6 | 7 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-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 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/frenchdeck2.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple, abc 2 | 3 | Card = namedtuple('Card', ['rank', 'suit']) 4 | 5 | class FrenchDeck2(abc.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 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/lotto.py: -------------------------------------------------------------------------------- 1 | # tag::LOTTERY_BLOWER[] 2 | 3 | import random 4 | 5 | from tombola import Tombola 6 | 7 | 8 | class LottoBlower(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 LottoBlower') 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(self._balls) 28 | 29 | 30 | # end::LOTTERY_BLOWER[] 31 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/tombola.py: -------------------------------------------------------------------------------- 1 | # tag::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 | def inspect(self): 23 | """Return a sorted tuple with the items currently inside.""" 24 | items = [] 25 | while True: # <6> 26 | try: 27 | items.append(self.pick()) 28 | except LookupError: 29 | break 30 | self.load(items) # <7> 31 | return tuple(items) 32 | 33 | 34 | # end::TOMBOLA_ABC[] 35 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-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(self) 22 | 23 | # Tombola.register(TomboList) # <7> 24 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/typing/randompick.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol, runtime_checkable, Any 2 | 3 | @runtime_checkable 4 | class RandomPicker(Protocol): 5 | def pick(self) -> Any: ... 6 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/typing/randompick_test.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing import Any, Iterable, TYPE_CHECKING 3 | 4 | from randompick import RandomPicker # <1> 5 | 6 | class SimplePicker: # <2> 7 | def __init__(self, items: Iterable) -> None: 8 | self._items = list(items) 9 | random.shuffle(self._items) 10 | 11 | def pick(self) -> Any: # <3> 12 | return self._items.pop() 13 | 14 | def test_isinstance() -> None: # <4> 15 | popper: RandomPicker = SimplePicker([1]) # <5> 16 | assert isinstance(popper, RandomPicker) # <6> 17 | 18 | def test_item_type() -> None: # <7> 19 | items = [1, 2] 20 | popper = SimplePicker(items) 21 | item = popper.pick() 22 | assert item in items 23 | if TYPE_CHECKING: 24 | reveal_type(item) # <8> 25 | assert isinstance(item, int) 26 | -------------------------------------------------------------------------------- /capitulos/code/13-protocol-abc/typing/randompickload.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol, runtime_checkable 2 | from randompick import RandomPicker 3 | 4 | @runtime_checkable # <1> 5 | class LoadableRandomPicker(RandomPicker, Protocol): # <2> 6 | def load(self, Iterable) -> None: ... # <3> 7 | -------------------------------------------------------------------------------- /capitulos/code/14-inheritance/README.rst: -------------------------------------------------------------------------------- 1 | Sample code for Chapter 14 - "Inheritance: for better or for worse" 2 | 3 | From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2021) 4 | https://learning.oreilly.com/library/view/fluent-python-2nd/9781492056348/ 5 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/cast/empty.py: -------------------------------------------------------------------------------- 1 | # Mypy 0.812 can't spot this inevitable runtime IndexError 2 | print([][0]) -------------------------------------------------------------------------------- /capitulos/code/15-more-types/cast/find.py: -------------------------------------------------------------------------------- 1 | # tag::CAST[] 2 | from typing import cast 3 | 4 | def find_first_str(a: list[object]) -> str: 5 | index = next(i for i, x in enumerate(a) if isinstance(x, str)) 6 | # We only get here if there's at least one string 7 | return cast(str, a[index]) 8 | # end::CAST[] 9 | 10 | 11 | from typing import TYPE_CHECKING 12 | 13 | l1 = [10, 20, 'thirty', 40] 14 | if TYPE_CHECKING: 15 | reveal_type(l1) 16 | 17 | print(find_first_str(l1)) 18 | 19 | l2 = [0, ()] 20 | try: 21 | find_first_str(l2) 22 | except StopIteration as e: 23 | print(repr(e)) 24 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/cast/tcp_echo_no_cast.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | from asyncio import StreamReader, StreamWriter 4 | 5 | async def handle_echo(reader: StreamReader, writer: StreamWriter) -> None: 6 | data = await reader.read(100) 7 | message = data.decode() 8 | addr = writer.get_extra_info('peername') 9 | 10 | print(f"Received {message!r} from {addr!r}") 11 | 12 | print(f"Send: {message!r}") 13 | writer.write(data) 14 | await writer.drain() 15 | 16 | print("Close the connection") 17 | writer.close() 18 | 19 | async def main() -> None: 20 | server = await asyncio.start_server( 21 | handle_echo, '127.0.0.1', 8888) 22 | 23 | addr = server.sockets[0].getsockname() 24 | print(f'Serving on {addr}') 25 | 26 | async with server: 27 | await server.serve_forever() 28 | 29 | asyncio.run(main()) 30 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/clip_annot_demo.py: -------------------------------------------------------------------------------- 1 | from clip_annot_post import clip 2 | 3 | print(clip.__annotations__) 4 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/collections_variance.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Collection, Sequence 2 | 3 | col_int: Collection[int] 4 | 5 | seq_int: Sequence[int] = (1, 2, 3) 6 | 7 | ## Incompatible types in assignment 8 | ## expression has type "Collection[int]" 9 | ## variable has type "Sequence[int]" 10 | # seq_int = col_int 11 | 12 | col_int = seq_int 13 | 14 | ## List item 0 has incompatible type "float" 15 | ## expected "int" 16 | # col_int = [1.1] 17 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/lotto/generic_lotto.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | from collections.abc import Iterable 4 | from typing import TypeVar, Generic 5 | 6 | from tombola import Tombola 7 | 8 | T = TypeVar('T') 9 | 10 | class LottoBlower(Tombola, Generic[T]): # <1> 11 | 12 | def __init__(self, items: Iterable[T]) -> None: # <2> 13 | self._balls = list[T](items) 14 | 15 | def load(self, items: Iterable[T]) -> None: # <3> 16 | self._balls.extend(items) 17 | 18 | def pick(self) -> T: # <4> 19 | try: 20 | position = random.randrange(len(self._balls)) 21 | except ValueError: 22 | raise LookupError('pick from empty LottoBlower') 23 | return self._balls.pop(position) 24 | 25 | def loaded(self) -> bool: # <5> 26 | return bool(self._balls) 27 | 28 | def inspect(self) -> tuple[T, ...]: # <6> 29 | return tuple(self._balls) 30 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/lotto/generic_lotto_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from typing import TYPE_CHECKING 4 | 5 | # tag::LOTTO_USE[] 6 | from generic_lotto import LottoBlower 7 | 8 | machine = LottoBlower[int](range(1, 11)) # <1> 9 | 10 | first = machine.pick() # <2> 11 | remain = machine.inspect() # <3> 12 | # end::LOTTO_USE[] 13 | 14 | expected = set(i for i in range(1, 11) if i != first) 15 | 16 | assert set(remain) == expected 17 | 18 | print('picked:', first) 19 | print('remain:', remain) 20 | 21 | if TYPE_CHECKING: 22 | reveal_type(first) 23 | # Revealed type is 'builtins.int*' 24 | if TYPE_CHECKING: 25 | reveal_type(remain) 26 | # Revealed type is 'builtins.tuple[builtins.int*]' 27 | 28 | 29 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/lotto/generic_lotto_errors.py: -------------------------------------------------------------------------------- 1 | from generic_lotto import LottoBlower 2 | 3 | machine = LottoBlower[int]([1, .2]) 4 | ## error: List item 1 has incompatible type "float"; # <1> 5 | ## expected "int" 6 | 7 | machine = LottoBlower[int](range(1, 11)) 8 | 9 | machine.load('ABC') 10 | ## error: Argument 1 to "load" of "LottoBlower" # <2> 11 | ## has incompatible type "str"; 12 | ## expected "Iterable[int]" 13 | ## note: Following member(s) of "str" have conflicts: 14 | ## note: Expected: 15 | ## note: def __iter__(self) -> Iterator[int] 16 | ## note: Got: 17 | ## note: def __iter__(self) -> Iterator[str] 18 | 19 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/lotto/tombola.py: -------------------------------------------------------------------------------- 1 | # tag::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 | def inspect(self): 23 | """Return a sorted tuple with the items currently inside.""" 24 | items = [] 25 | while True: # <6> 26 | try: 27 | items.append(self.pick()) 28 | except LookupError: 29 | break 30 | self.load(items) # <7> 31 | return tuple(items) 32 | 33 | 34 | # end::TOMBOLA_ABC[] 35 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/mysum.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import operator 3 | from collections.abc import Iterable 4 | from typing import overload, Union, TypeVar 5 | 6 | T = TypeVar('T') 7 | S = TypeVar('S') # <1> 8 | 9 | @overload 10 | def sum(it: Iterable[T]) -> Union[T, int]: ... # <2> 11 | @overload 12 | def sum(it: Iterable[T], /, start: S) -> Union[T, S]: ... # <3> 13 | def sum(it, /, start=0): # <4> 14 | return functools.reduce(operator.add, it, start) 15 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/protocol/abs_demo.py: -------------------------------------------------------------------------------- 1 | import math 2 | from typing import NamedTuple, SupportsAbs 3 | 4 | class Vector2d(NamedTuple): 5 | x: float 6 | y: float 7 | 8 | def __abs__(self) -> float: # <1> 9 | return math.hypot(self.x, self.y) 10 | 11 | def is_unit(v: SupportsAbs[float]) -> bool: # <2> 12 | """'True' if the magnitude of 'v' is close to 1.""" 13 | return math.isclose(abs(v), 1.0) # <3> 14 | 15 | assert issubclass(Vector2d, SupportsAbs) # <4> 16 | 17 | v0 = Vector2d(0, 1) # <5> 18 | sqrt2 = math.sqrt(2) 19 | v1 = Vector2d(sqrt2 / 2, sqrt2 / 2) 20 | v2 = Vector2d(1, 1) 21 | v3 = complex(.5, math.sqrt(3) / 2) 22 | v4 = 1 # <6> 23 | 24 | assert is_unit(v0) 25 | assert is_unit(v1) 26 | assert not is_unit(v2) 27 | assert is_unit(v3) 28 | assert is_unit(v4) 29 | 30 | print('OK') 31 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/protocol/random/erp.py: -------------------------------------------------------------------------------- 1 | import random 2 | from typing import TypeVar, Generic, List, Iterable 3 | 4 | 5 | T = TypeVar('T') 6 | 7 | 8 | class EnterpriserRandomPopper(Generic[T]): 9 | def __init__(self, items: Iterable[T]) -> None: 10 | self._items: List[T] = list(items) 11 | random.shuffle(self._items) 12 | 13 | def pop_random(self) -> T: 14 | return self._items.pop() 15 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/protocol/random/generic_randompick.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol, runtime_checkable, TypeVar 2 | 3 | T_co = TypeVar('T_co', covariant=True) # <1> 4 | 5 | @runtime_checkable 6 | class RandomPicker(Protocol[T_co]): # <2> 7 | def pick(self) -> T_co: ... # <3> 8 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/protocol/random/randompop.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol, runtime_checkable, Any 2 | 3 | 4 | @runtime_checkable 5 | class RandomPopper(Protocol): 6 | def pop_random(self) -> Any: ... 7 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/protocol/random/randompop_test.py: -------------------------------------------------------------------------------- 1 | from randompop import RandomPopper 2 | import random 3 | from typing import Any, Iterable, TYPE_CHECKING 4 | 5 | 6 | class SimplePopper: 7 | def __init__(self, items: Iterable) -> None: 8 | self._items = list(items) 9 | random.shuffle(self._items) 10 | 11 | def pop_random(self) -> Any: 12 | return self._items.pop() 13 | 14 | 15 | def test_issubclass() -> None: 16 | assert issubclass(SimplePopper, RandomPopper) 17 | 18 | 19 | def test_isinstance() -> None: 20 | popper: RandomPopper = SimplePopper([1]) 21 | if TYPE_CHECKING: 22 | reveal_type(popper) 23 | # Revealed type is 'randompop.RandomPopper' 24 | assert isinstance(popper, RandomPopper) 25 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/typeddict/books.py: -------------------------------------------------------------------------------- 1 | import json 2 | # tag::BOOKDICT[] 3 | from typing import TypedDict 4 | 5 | class BookDict(TypedDict): 6 | isbn: str 7 | title: str 8 | authors: list[str] 9 | pagecount: int 10 | # end::BOOKDICT[] 11 | 12 | # tag::TOXML[] 13 | AUTHOR_ELEMENT = '{}' 14 | 15 | def to_xml(book: BookDict) -> str: # <1> 16 | elements: list[str] = [] # <2> 17 | for key, value in book.items(): 18 | if isinstance(value, list): # <3> 19 | elements.extend( 20 | AUTHOR_ELEMENT.format(n) for n in value) # <4> 21 | else: 22 | tag = key.upper() 23 | elements.append(f'<{tag}>{value}') 24 | xml = '\n\t'.join(elements) 25 | return f'\n\t{xml}\n' 26 | # end::TOXML[] 27 | 28 | # tag::FROMJSON[] 29 | def from_json(data: str) -> BookDict: 30 | whatever: BookDict = json.loads(data) # <1> 31 | return whatever # <2> 32 | # end::FROMJSON[] -------------------------------------------------------------------------------- /capitulos/code/15-more-types/typeddict/books_any.py: -------------------------------------------------------------------------------- 1 | # tag::BOOKDICT[] 2 | from typing import TypedDict, List 3 | import json 4 | 5 | class BookDict(TypedDict): 6 | isbn: str 7 | title: str 8 | authors: List[str] 9 | pagecount: int 10 | # end::BOOKDICT[] 11 | 12 | # tag::TOXML[] 13 | AUTHOR_ELEMENT = '{}' 14 | 15 | def to_xml(book: BookDict) -> str: # <1> 16 | elements: List[str] = [] # <2> 17 | for key, value in book.items(): 18 | if isinstance(value, list): # <3> 19 | elements.extend(AUTHOR_ELEMENT.format(n) 20 | for n in value) 21 | else: 22 | tag = key.upper() 23 | elements.append(f'<{tag}>{value}') 24 | xml = '\n\t'.join(elements) 25 | return f'\n\t{xml}\n' 26 | # end::TOXML[] 27 | 28 | # tag::FROMJSON[] 29 | def from_json(data: str) -> BookDict: 30 | whatever = json.loads(data) # <1> 31 | return whatever # <2> 32 | # end::FROMJSON[] 33 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/typeddict/demo_books.py: -------------------------------------------------------------------------------- 1 | from books import BookDict 2 | from typing import TYPE_CHECKING 3 | 4 | def demo() -> None: # <1> 5 | book = BookDict( # <2> 6 | isbn='0134757599', 7 | title='Refactoring, 2e', 8 | authors=['Martin Fowler', 'Kent Beck'], 9 | pagecount=478 10 | ) 11 | authors = book['authors'] # <3> 12 | if TYPE_CHECKING: # <4> 13 | reveal_type(authors) # <5> 14 | authors = 'Bob' # <6> 15 | book['weight'] = 4.2 16 | del book['title'] 17 | 18 | 19 | if __name__ == '__main__': 20 | demo() 21 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/typeddict/demo_not_book.py: -------------------------------------------------------------------------------- 1 | from books import to_xml, from_json 2 | from typing import TYPE_CHECKING 3 | 4 | def demo() -> None: 5 | NOT_BOOK_JSON = """ 6 | {"title": "Andromeda Strain", 7 | "flavor": "pistachio", 8 | "authors": true} 9 | """ 10 | not_book = from_json(NOT_BOOK_JSON) # <1> 11 | if TYPE_CHECKING: # <2> 12 | reveal_type(not_book) 13 | reveal_type(not_book['authors']) 14 | 15 | print(not_book) # <3> 16 | print(not_book['flavor']) # <4> 17 | 18 | xml = to_xml(not_book) # <5> 19 | print(xml) # <6> 20 | 21 | 22 | if __name__ == '__main__': 23 | demo() 24 | -------------------------------------------------------------------------------- /capitulos/code/15-more-types/typeddict/test_books_check_fails.py: -------------------------------------------------------------------------------- 1 | from books import BookDict, to_xml 2 | 3 | XML_SAMPLE = """ 4 | 5 | \t0134757599 6 | \tRefactoring, 2e 7 | \tMartin Fowler 8 | \tKent Beck 9 | \t478 10 | 11 | """.strip() 12 | 13 | def test_3() -> None: 14 | xml = to_xml(BookDict(dict([ # Expected keyword arguments, {...}, or dict(...) in TypedDict constructor 15 | ('isbn', '0134757599'), 16 | ('title', 'Refactoring, 2e'), 17 | ('authors', ['Martin Fowler', 'Kent Beck']), 18 | ('pagecount', 478), 19 | ]))) 20 | assert xml == XML_SAMPLE 21 | -------------------------------------------------------------------------------- /capitulos/code/16-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 | -------------------------------------------------------------------------------- /capitulos/code/16-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 | -------------------------------------------------------------------------------- /capitulos/code/17-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 | -------------------------------------------------------------------------------- /capitulos/code/17-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 | -------------------------------------------------------------------------------- /capitulos/code/17-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 | -------------------------------------------------------------------------------- /capitulos/code/17-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 = type(self.begin + self.step) 21 | result = result_type(self.begin) 22 | forever = self.end is None 23 | while forever or result < self.end: 24 | yield result 25 | result += self.step 26 | -------------------------------------------------------------------------------- /capitulos/code/17-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'), Decimal('0.1'), Decimal('0.2')] 18 | 19 | """ 20 | 21 | 22 | # tag::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 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/aritprog_v3.py: -------------------------------------------------------------------------------- 1 | # tag::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 None: 9 | return ap_gen 10 | return itertools.takewhile(lambda n: n < end, ap_gen) 11 | # end::ARITPROG_ITERTOOLS[] 12 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/columnize_iter.py: -------------------------------------------------------------------------------- 1 | # tag::COLUMNIZE[] 2 | from collections.abc import Sequence, Iterator 3 | 4 | def columnize( 5 | sequence: Sequence[str], num_columns: int = 0 6 | ) -> Iterator[tuple[str, ...]]: # <1> 7 | if num_columns == 0: 8 | num_columns = round(len(sequence) ** 0.5) 9 | num_rows, reminder = divmod(len(sequence), num_columns) 10 | num_rows += bool(reminder) 11 | return (tuple(sequence[i::num_rows]) for i in range(num_rows)) # <2> 12 | # end::COLUMNIZE[] 13 | 14 | def demo() -> None: 15 | nato = ( 16 | 'Alfa Bravo Charlie Delta Echo Foxtrot Golf Hotel India' 17 | ' Juliett Kilo Lima Mike November Oscar Papa Quebec Romeo' 18 | ' Sierra Tango Uniform Victor Whiskey X-ray Yankee Zulu' 19 | ).split() 20 | 21 | for row in columnize(nato, 4): 22 | for word in row: 23 | print(f'{word:15}', end='') 24 | print() 25 | 26 | if __name__ == '__main__': 27 | demo() 28 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/fibo_gen.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterator 2 | 3 | def fibonacci() -> Iterator[int]: 4 | a, b = 0, 1 5 | while True: 6 | yield a 7 | a, b = b, a + b 8 | -------------------------------------------------------------------------------- /capitulos/code/17-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 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/iter_gen_type.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterator 2 | from keyword import kwlist 3 | from typing import TYPE_CHECKING 4 | 5 | short_kw = (k for k in kwlist if len(k) < 5) # <1> 6 | 7 | if TYPE_CHECKING: 8 | reveal_type(short_kw) # <2> 9 | 10 | long_kw: Iterator[str] = (k for k in kwlist if len(k) >= 4) # <3> 11 | 12 | if TYPE_CHECKING: # <4> 13 | reveal_type(long_kw) 14 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/sentence.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sentence: access words by index 3 | 4 | >>> text = 'To be, or not to be, that is the question' 5 | >>> s = Sentence(text) 6 | >>> len(s) 7 | 10 8 | >>> s[1], s[5] 9 | ('be', 'be') 10 | >>> s 11 | Sentence('To be, or no... the question') 12 | 13 | """ 14 | 15 | # tag::SENTENCE_SEQ[] 16 | import re 17 | import reprlib 18 | 19 | RE_WORD = re.compile(r'\w+') 20 | 21 | 22 | class Sentence: 23 | 24 | def __init__(self, text): 25 | self.text = text 26 | self.words = RE_WORD.findall(text) # <1> 27 | 28 | def __getitem__(self, index): 29 | return self.words[index] # <2> 30 | 31 | def __len__(self): # <3> 32 | return len(self.words) 33 | 34 | def __repr__(self): 35 | return 'Sentence(%s)' % reprlib.repr(self.text) # <4> 36 | 37 | # end::SENTENCE_SEQ[] 38 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/sentence_gen.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sentence: iterate over words using a generator function 3 | """ 4 | 5 | # tag::SENTENCE_GEN[] 6 | import re 7 | import reprlib 8 | 9 | RE_WORD = re.compile(r'\w+') 10 | 11 | 12 | class Sentence: 13 | 14 | def __init__(self, text): 15 | self.text = text 16 | self.words = RE_WORD.findall(text) 17 | 18 | def __repr__(self): 19 | return 'Sentence(%s)' % reprlib.repr(self.text) 20 | 21 | def __iter__(self): 22 | for word in self.words: # <1> 23 | yield word # <2> 24 | # <3> 25 | 26 | # done! <4> 27 | 28 | # end::SENTENCE_GEN[] 29 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/sentence_gen2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Sentence: iterate over words using a generator function 3 | """ 4 | 5 | # tag::SENTENCE_GEN2[] 6 | import re 7 | import reprlib 8 | 9 | RE_WORD = re.compile(r'\w+') 10 | 11 | 12 | class Sentence: 13 | 14 | def __init__(self, text): 15 | self.text = text # <1> 16 | 17 | def __repr__(self): 18 | return f'Sentence({reprlib.repr(self.text)})' 19 | 20 | def __iter__(self): 21 | for match in RE_WORD.finditer(self.text): # <2> 22 | yield match.group() # <3> 23 | 24 | # end::SENTENCE_GEN2[] 25 | -------------------------------------------------------------------------------- /capitulos/code/17-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(r'\w+') 12 | 13 | 14 | class Sentence: 15 | 16 | def __init__(self, text): 17 | self.text = text 18 | 19 | def __repr__(self): 20 | return f'Sentence({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 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/4steps/tree_step0.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__ 3 | 4 | 5 | if __name__ == '__main__': 6 | for cls_name in tree(BaseException): 7 | print(cls_name) 8 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/4steps/tree_step1.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__, 0 3 | for sub_cls in cls.__subclasses__(): 4 | yield sub_cls.__name__, 1 5 | 6 | 7 | if __name__ == '__main__': 8 | for cls_name, level in tree(BaseException): 9 | indent = ' ' * 4 * level 10 | print(f'{indent}{cls_name}') 11 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/4steps/tree_step2.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__, 0 3 | for sub_cls in cls.__subclasses__(): 4 | yield sub_cls.__name__, 1 5 | for sub_sub_cls in sub_cls.__subclasses__(): 6 | yield sub_sub_cls.__name__, 2 7 | 8 | 9 | if __name__ == '__main__': 10 | for cls_name, level in tree(BaseException): 11 | indent = ' ' * 4 * level 12 | print(f'{indent}{cls_name}') 13 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/4steps/tree_step3.py: -------------------------------------------------------------------------------- 1 | def tree(cls, level=0): 2 | yield cls.__name__, level 3 | for sub_cls in cls.__subclasses__(): 4 | yield from tree(sub_cls, level + 1) 5 | 6 | 7 | if __name__ == '__main__': 8 | for cls_name, level in tree(BaseException): 9 | indent = ' ' * 4 * level 10 | print(f'{indent}{cls_name}') 11 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/extra/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls, level=0, last_sibling=True): 2 | yield cls, level, last_sibling 3 | subclasses = cls.__subclasses__() 4 | if subclasses: 5 | last = subclasses[-1] 6 | for sub_cls in subclasses: 7 | yield from tree(sub_cls, level+1, sub_cls is last) 8 | 9 | 10 | def display(cls): 11 | for cls, level, _ in tree(cls): 12 | indent = ' ' * 4 * level 13 | print(f'{indent}{cls.__name__}') 14 | 15 | 16 | if __name__ == '__main__': 17 | display(BaseException) 18 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step0/test_tree.py: -------------------------------------------------------------------------------- 1 | from tree import tree 2 | 3 | 4 | def test_1_level(): 5 | class One: pass 6 | expected = ['One'] 7 | result = list(tree(One)) 8 | assert expected == result 9 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step0/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__ 3 | 4 | 5 | def display(cls): 6 | for cls_name in tree(cls): 7 | print(cls_name) 8 | 9 | 10 | if __name__ == '__main__': 11 | display(BaseException) -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step1/test_tree.py: -------------------------------------------------------------------------------- 1 | from tree import tree 2 | 3 | 4 | def test_1_level(): 5 | class One: pass 6 | expected = [('One', 0)] 7 | result = list(tree(One)) 8 | assert expected == result 9 | 10 | 11 | def test_2_levels_2_leaves(): 12 | class Branch: pass 13 | class Leaf1(Branch): pass 14 | class Leaf2(Branch): pass 15 | expected = [ 16 | ('Branch', 0), 17 | ('Leaf1', 1), 18 | ('Leaf2', 1), 19 | ] 20 | result = list(tree(Branch)) 21 | assert expected == result 22 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step1/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__, 0 # <1> 3 | for sub_cls in cls.__subclasses__(): # <2> 4 | yield sub_cls.__name__, 1 # <3> 5 | 6 | 7 | def display(cls): 8 | for cls_name, level in tree(cls): 9 | indent = ' ' * 4 * level # <4> 10 | print(f'{indent}{cls_name}') 11 | 12 | 13 | if __name__ == '__main__': 14 | display(BaseException) -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step2/test_tree.py: -------------------------------------------------------------------------------- 1 | from tree import tree 2 | 3 | 4 | def test_1_level(): 5 | class One: pass 6 | expected = [('One', 0)] 7 | result = list(tree(One)) 8 | assert expected == result 9 | 10 | 11 | def test_2_levels_2_leaves(): 12 | class Branch: pass 13 | class Leaf1(Branch): pass 14 | class Leaf2(Branch): pass 15 | expected = [ 16 | ('Branch', 0), 17 | ('Leaf1', 1), 18 | ('Leaf2', 1), 19 | ] 20 | result = list(tree(Branch)) 21 | assert expected == result 22 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step2/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__, 0 3 | yield from sub_tree(cls) # <1> 4 | 5 | 6 | def sub_tree(cls): 7 | for sub_cls in cls.__subclasses__(): 8 | yield sub_cls.__name__, 1 # <2> 9 | 10 | 11 | def display(cls): 12 | for cls_name, level in tree(cls): # <3> 13 | indent = ' ' * 4 * level 14 | print(f'{indent}{cls_name}') 15 | 16 | 17 | if __name__ == '__main__': 18 | display(BaseException) 19 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step3/test_tree.py: -------------------------------------------------------------------------------- 1 | from tree import tree 2 | 3 | 4 | def test_1_level(): 5 | class One: pass 6 | expected = [('One', 0)] 7 | result = list(tree(One)) 8 | assert expected == result 9 | 10 | 11 | def test_2_levels_2_leaves(): 12 | class Branch: pass 13 | class Leaf1(Branch): pass 14 | class Leaf2(Branch): pass 15 | expected = [ 16 | ('Branch', 0), 17 | ('Leaf1', 1), 18 | ('Leaf2', 1), 19 | ] 20 | result = list(tree(Branch)) 21 | assert expected == result 22 | 23 | 24 | def test_3_levels_1_leaf(): 25 | class X: pass 26 | class Y(X): pass 27 | class Z(Y): pass 28 | expected = [ 29 | ('X', 0), 30 | ('Y', 1), 31 | ('Z', 2), 32 | ] 33 | result = list(tree(X)) 34 | assert expected == result 35 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step3/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__, 0 3 | yield from sub_tree(cls) 4 | 5 | 6 | def sub_tree(cls): 7 | for sub_cls in cls.__subclasses__(): 8 | yield sub_cls.__name__, 1 9 | for sub_sub_cls in sub_cls.__subclasses__(): 10 | yield sub_sub_cls.__name__, 2 11 | 12 | 13 | def display(cls): 14 | for cls_name, level in tree(cls): 15 | indent = ' ' * 4 * level 16 | print(f'{indent}{cls_name}') 17 | 18 | 19 | if __name__ == '__main__': 20 | display(BaseException) 21 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step4/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__, 0 3 | yield from sub_tree(cls) 4 | 5 | 6 | # tag::SUB_TREE[] 7 | def sub_tree(cls): 8 | for sub_cls in cls.__subclasses__(): 9 | yield sub_cls.__name__, 1 10 | for sub_sub_cls in sub_cls.__subclasses__(): 11 | yield sub_sub_cls.__name__, 2 12 | for sub_sub_sub_cls in sub_sub_cls.__subclasses__(): 13 | yield sub_sub_sub_cls.__name__, 3 14 | # end::SUB_TREE[] 15 | 16 | 17 | def display(cls): 18 | for cls_name, level in tree(cls): 19 | indent = ' ' * 4 * level 20 | print(f'{indent}{cls_name}') 21 | 22 | 23 | if __name__ == '__main__': 24 | display(BaseException) 25 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step5/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls): 2 | yield cls.__name__, 0 3 | yield from sub_tree(cls, 1) 4 | 5 | 6 | def sub_tree(cls, level): 7 | for sub_cls in cls.__subclasses__(): 8 | yield sub_cls.__name__, level 9 | yield from sub_tree(sub_cls, level+1) 10 | 11 | 12 | def display(cls): 13 | for cls_name, level in tree(cls): 14 | indent = ' ' * 4 * level 15 | print(f'{indent}{cls_name}') 16 | 17 | 18 | if __name__ == '__main__': 19 | display(BaseException) 20 | -------------------------------------------------------------------------------- /capitulos/code/17-it-generator/tree/step6/tree.py: -------------------------------------------------------------------------------- 1 | def tree(cls, level=0): 2 | yield cls.__name__, level 3 | for sub_cls in cls.__subclasses__(): 4 | yield from tree(sub_cls, level+1) 5 | 6 | 7 | def display(cls): 8 | for cls_name, level in tree(cls): 9 | indent = ' ' * 4 * level 10 | print(f'{indent}{cls_name}') 11 | 12 | 13 | if __name__ == '__main__': 14 | display(BaseException) 15 | -------------------------------------------------------------------------------- /capitulos/code/17-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 | # tag::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 | -------------------------------------------------------------------------------- /capitulos/code/17-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 | # tag::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 | -------------------------------------------------------------------------------- /capitulos/code/18-with-match/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 | -------------------------------------------------------------------------------- /capitulos/code/18-with-match/lispy/original/README.md: -------------------------------------------------------------------------------- 1 | 2 | # Source of the originals 3 | 4 | * [lis.py](https://raw.githubusercontent.com/norvig/pytudes/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lis.py) 5 | 6 | * [lispy.py](https://raw.githubusercontent.com/norvig/pytudes/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispy.py) 7 | 8 | * [lispytest.py](https://raw.githubusercontent.com/norvig/pytudes/705c0a335c1811a203e79587d7d41865cf7f41c7/py/lispytest.py) 9 | -------------------------------------------------------------------------------- /capitulos/code/18-with-match/lispy/py3.10/quicksort.scm: -------------------------------------------------------------------------------- 1 | (define (quicksort lst) 2 | (if (null? lst) 3 | lst 4 | (begin 5 | (define pivot (car lst)) 6 | (define rest (cdr lst)) 7 | (append 8 | (quicksort 9 | (filter (lambda (x) (< x pivot)) rest)) 10 | (list pivot) 11 | (quicksort 12 | (filter (lambda (x) (>= x pivot)) rest))) 13 | ) 14 | ) 15 | ) 16 | (display 17 | (quicksort (list 2 1 6 3 4 0 8 9 7 5))) 18 | -------------------------------------------------------------------------------- /capitulos/code/19-concurrency/primes/run_procs.sh: -------------------------------------------------------------------------------- 1 | #/bin/bash 2 | for i in {1..20}; do echo -n $i; python3 procs.py $i | tail -1; done 3 | -------------------------------------------------------------------------------- /capitulos/code/19-concurrency/primes/sequential.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | """ 4 | sequential.py: baseline for comparing sequential, multiprocessing, 5 | and threading code for CPU-intensive work. 6 | """ 7 | 8 | from time import perf_counter 9 | from typing import NamedTuple 10 | 11 | from primes import is_prime, NUMBERS 12 | 13 | class Result(NamedTuple): # <1> 14 | prime: bool 15 | elapsed: float 16 | 17 | def check(n: int) -> Result: # <2> 18 | t0 = perf_counter() 19 | prime = is_prime(n) 20 | return Result(prime, perf_counter() - t0) 21 | 22 | def main() -> None: 23 | print(f'Checking {len(NUMBERS)} numbers sequentially:') 24 | t0 = perf_counter() 25 | for n in NUMBERS: # <3> 26 | prime, elapsed = check(n) 27 | label = 'P' if prime else ' ' 28 | print(f'{n:16} {label} {elapsed:9.6f}s') 29 | 30 | elapsed = perf_counter() - t0 # <4> 31 | print(f'Total time: {elapsed:.2f}s') 32 | 33 | if __name__ == '__main__': 34 | main() 35 | -------------------------------------------------------------------------------- /capitulos/code/20-executors/demo_executor_map.py: -------------------------------------------------------------------------------- 1 | """ 2 | Experiment with ``ThreadPoolExecutor.map`` 3 | """ 4 | # tag::EXECUTOR_MAP[] 5 | from time import sleep, strftime 6 | from concurrent import futures 7 | 8 | def display(*args): # <1> 9 | print(strftime('[%H:%M:%S]'), end=' ') 10 | print(*args) 11 | 12 | def loiter(n): # <2> 13 | msg = '{}loiter({}): doing nothing for {}s...' 14 | display(msg.format('\t'*n, n, n)) 15 | sleep(n) 16 | msg = '{}loiter({}): done.' 17 | display(msg.format('\t'*n, n)) 18 | return n * 10 # <3> 19 | 20 | def main(): 21 | display('Script starting.') 22 | executor = futures.ThreadPoolExecutor(max_workers=3) # <4> 23 | results = executor.map(loiter, range(5)) # <5> 24 | display('results:', results) # <6> 25 | display('Waiting for individual results:') 26 | for i, result in enumerate(results): # <7> 27 | display(f'result {i}: {result}') 28 | 29 | if __name__ == '__main__': 30 | main() 31 | # end::EXECUTOR_MAP[] 32 | -------------------------------------------------------------------------------- /capitulos/code/20-executors/getflags/.gitignore: -------------------------------------------------------------------------------- 1 | flags/ 2 | downloaded/ -------------------------------------------------------------------------------- /capitulos/code/20-executors/getflags/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 | -------------------------------------------------------------------------------- /capitulos/code/20-executors/getflags/flags.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/capitulos/code/20-executors/getflags/flags.zip -------------------------------------------------------------------------------- /capitulos/code/20-executors/getflags/httpx-error-tree/tree.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import httpx # make httpx classes available to .__subclasses__() 4 | 5 | 6 | def tree(cls, level=0, last_sibling=True): 7 | yield cls, level, last_sibling 8 | 9 | # get RuntimeError and exceptions defined in httpx 10 | subclasses = [sub for sub in cls.__subclasses__() 11 | if sub is RuntimeError or sub.__module__ == 'httpx'] 12 | if subclasses: 13 | last = subclasses[-1] 14 | for sub in subclasses: 15 | yield from tree(sub, level+1, sub is last) 16 | 17 | 18 | def display(cls): 19 | for cls, level, _ in tree(cls): 20 | indent = ' ' * 4 * level 21 | module = 'builtins.' if cls.__module__ == 'builtins' else '' 22 | print(f'{indent}{module}{cls.__name__}') 23 | 24 | 25 | if __name__ == '__main__': 26 | display(Exception) 27 | -------------------------------------------------------------------------------- /capitulos/code/20-executors/getflags/requirements.txt: -------------------------------------------------------------------------------- 1 | anyio==3.3.2 2 | certifi==2021.5.30 3 | charset-normalizer==2.0.6 4 | h11==0.12.0 5 | httpcore==0.13.7 6 | httpx==1.0.0b0 7 | idna==3.2 8 | rfc3986==1.5.0 9 | sniffio==1.2.0 10 | tqdm==4.62.3 11 | -------------------------------------------------------------------------------- /capitulos/code/21-async/README.rst: -------------------------------------------------------------------------------- 1 | Sample code for Chapter 22 - "Asynchronous programming" 2 | 3 | From the book "Fluent Python, Second Edition" by Luciano Ramalho (O'Reilly, 2021) 4 | https://learning.oreilly.com/library/view/fluent-python-2nd/9781492056348/ 5 | -------------------------------------------------------------------------------- /capitulos/code/21-async/domains/README.rst: -------------------------------------------------------------------------------- 1 | domainlib demonstration 2 | ======================= 3 | 4 | Run Python's async console (requires Python ≥ 3.8):: 5 | 6 | $ python3 -m asyncio 7 | 8 | I'll see ``asyncio`` imported automatically:: 9 | 10 | >>> import asyncio 11 | 12 | Now you can experiment with ``domainlib``. 13 | 14 | At the `>>>` prompt, type these commands:: 15 | 16 | >>> from domainlib import * 17 | >>> await probe('python.org') 18 | 19 | Note the result. 20 | 21 | Next:: 22 | 23 | >>> names = 'python.org rust-lang.org golang.org n05uch1an9.org'.split() 24 | >>> async for result in multi_probe(names): 25 | ... print(*result, sep='\t') 26 | 27 | Note that if you run the last two lines again, 28 | the results are likely to appear in a different order. 29 | -------------------------------------------------------------------------------- /capitulos/code/21-async/domains/asyncio/domaincheck.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import asyncio 3 | import sys 4 | from keyword import kwlist 5 | 6 | from domainlib import multi_probe 7 | 8 | 9 | async def main(tld: str) -> None: 10 | tld = tld.strip('.') 11 | names = (kw for kw in kwlist if len(kw) <= 4) # <1> 12 | domains = (f'{name}.{tld}'.lower() for name in names) # <2> 13 | print('FOUND\t\tNOT FOUND') # <3> 14 | print('=====\t\t=========') 15 | async for domain, found in multi_probe(domains): # <4> 16 | indent = '' if found else '\t\t' # <5> 17 | print(f'{indent}{domain}') 18 | 19 | 20 | if __name__ == '__main__': 21 | if len(sys.argv) == 2: 22 | asyncio.run(main(sys.argv[1])) # <6> 23 | else: 24 | print('Please provide a TLD.', f'Example: {sys.argv[0]} COM.BR') 25 | -------------------------------------------------------------------------------- /capitulos/code/21-async/domains/asyncio/domainlib.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import socket 3 | from collections.abc import Iterable, AsyncIterator 4 | from typing import NamedTuple, Optional 5 | 6 | 7 | class Result(NamedTuple): # <1> 8 | domain: str 9 | found: bool 10 | 11 | 12 | OptionalLoop = Optional[asyncio.AbstractEventLoop] # <2> 13 | 14 | 15 | async def probe(domain: str, loop: OptionalLoop = None) -> Result: # <3> 16 | if loop is None: 17 | loop = asyncio.get_running_loop() 18 | try: 19 | await loop.getaddrinfo(domain, None) 20 | except socket.gaierror: 21 | return Result(domain, False) 22 | return Result(domain, True) 23 | 24 | 25 | async def multi_probe(domains: Iterable[str]) -> AsyncIterator[Result]: # <4> 26 | loop = asyncio.get_running_loop() 27 | coros = [probe(domain, loop) for domain in domains] # <5> 28 | for coro in asyncio.as_completed(coros): # <6> 29 | result = await coro # <7> 30 | yield result # <8> 31 | -------------------------------------------------------------------------------- /capitulos/code/21-async/domains/curio/blogdom.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | from curio import run, TaskGroup 3 | import curio.socket as socket 4 | from keyword import kwlist 5 | 6 | MAX_KEYWORD_LEN = 4 7 | 8 | 9 | async def probe(domain: str) -> tuple[str, bool]: # <1> 10 | try: 11 | await socket.getaddrinfo(domain, None) # <2> 12 | except socket.gaierror: 13 | return (domain, False) 14 | return (domain, True) 15 | 16 | async def main() -> None: 17 | names = (kw for kw in kwlist if len(kw) <= MAX_KEYWORD_LEN) 18 | domains = (f'{name}.dev'.lower() for name in names) 19 | async with TaskGroup() as group: # <3> 20 | for domain in domains: 21 | await group.spawn(probe, domain) # <4> 22 | async for task in group: # <5> 23 | domain, found = task.result 24 | mark = '+' if found else ' ' 25 | print(f'{mark} {domain}') 26 | 27 | if __name__ == '__main__': 28 | run(main()) # <6> 29 | -------------------------------------------------------------------------------- /capitulos/code/21-async/domains/curio/domaincheck.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import curio 3 | import sys 4 | from keyword import kwlist 5 | 6 | from domainlib import multi_probe 7 | 8 | 9 | async def main(tld: str) -> None: 10 | tld = tld.strip('.') 11 | names = (kw for kw in kwlist if len(kw) <= 4) 12 | domains = (f'{name}.{tld}'.lower() for name in names) 13 | print('FOUND\t\tNOT FOUND') 14 | print('=====\t\t=========') 15 | async for domain, found in multi_probe(domains): 16 | indent = '' if found else '\t\t' 17 | print(f'{indent}{domain}') 18 | 19 | 20 | if __name__ == '__main__': 21 | if len(sys.argv) == 2: 22 | curio.run(main(sys.argv[1])) 23 | else: 24 | print('Please provide a TLD.', f'Example: {sys.argv[0]} COM.BR') 25 | -------------------------------------------------------------------------------- /capitulos/code/21-async/domains/curio/domainlib.py: -------------------------------------------------------------------------------- 1 | from collections.abc import Iterable, AsyncIterator 2 | from typing import NamedTuple 3 | 4 | from curio import TaskGroup 5 | import curio.socket as socket 6 | 7 | 8 | class Result(NamedTuple): 9 | domain: str 10 | found: bool 11 | 12 | 13 | async def probe(domain: str) -> Result: 14 | try: 15 | await socket.getaddrinfo(domain, None) 16 | except socket.gaierror: 17 | return Result(domain, False) 18 | return Result(domain, True) 19 | 20 | 21 | async def multi_probe(domains: Iterable[str]) -> AsyncIterator[Result]: 22 | async with TaskGroup() as group: 23 | for domain in domains: 24 | await group.spawn(probe, domain) 25 | async for task in group: 26 | yield task.result 27 | -------------------------------------------------------------------------------- /capitulos/code/21-async/domains/curio/requirements.txt: -------------------------------------------------------------------------------- 1 | curio==1.5 2 | -------------------------------------------------------------------------------- /capitulos/code/21-async/mojifinder/requirements.txt: -------------------------------------------------------------------------------- 1 | click==7.1.2 2 | fastapi==0.65.2 3 | h11==0.12.0 4 | pydantic==1.8.2 5 | starlette==0.13.6 6 | typing-extensions==3.7.4.3 7 | uvicorn==0.13.4 8 | -------------------------------------------------------------------------------- /capitulos/code/21-async/mojifinder/web_mojifinder_bottle.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import json 4 | import unicodedata 5 | 6 | from bottle import route, request, run, static_file 7 | 8 | from charindex import InvertedIndex 9 | 10 | index = {} 11 | 12 | @route('/') 13 | def form(): 14 | return static_file('form.html', root='static/') 15 | 16 | 17 | @route('/search') 18 | def search(): 19 | query = request.query['q'] 20 | chars = index.search(query) 21 | results = [] 22 | for char in chars: 23 | name = unicodedata.name(char) 24 | results.append({'char': char, 'name': name}) 25 | return json.dumps(results).encode('UTF-8') 26 | 27 | 28 | def main(port): 29 | global index 30 | index = InvertedIndex() 31 | run(host='localhost', port=port, debug=True) 32 | 33 | 34 | if __name__ == '__main__': 35 | main(8000) 36 | -------------------------------------------------------------------------------- /capitulos/code/22-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 | # tag::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 | -------------------------------------------------------------------------------- /capitulos/code/22-dyn-attr-prop/oscon/osconfeed-talk.json: -------------------------------------------------------------------------------- 1 | { "serial": 33950, 2 | "name": "There *Will* Be Bugs", 3 | "event_type": "40-minute conference session", 4 | "time_start": "2014-07-23 14:30:00", 5 | "time_stop": "2014-07-23 15:10:00", 6 | "venue_serial": 1449, 7 | "description": "If you're pushing the envelope of programming...", 8 | "website_url": "http://oscon.com/oscon2014/public/schedule/detail/33950", 9 | "speakers": [3471, 5199], 10 | "categories": ["Python"] } 11 | -------------------------------------------------------------------------------- /capitulos/code/22-dyn-attr-prop/oscon/osconfeed_explore.rst: -------------------------------------------------------------------------------- 1 | >>> import json 2 | >>> with open('data/osconfeed.json') as fp: 3 | ... feed = json.load(fp) # <1> 4 | >>> sorted(feed['Schedule'].keys()) # <2> 5 | ['conferences', 'events', 'speakers', 'venues'] 6 | >>> for key, value in sorted(feed['Schedule'].items()): 7 | ... print(f'{len(value):3} {key}') # <3> 8 | ... 9 | 1 conferences 10 | 484 events 11 | 357 speakers 12 | 53 venues 13 | >>> feed['Schedule']['speakers'][-1]['name'] # <4> 14 | 'Carina C. Zona' 15 | >>> feed['Schedule']['speakers'][-1]['serial'] # <5> 16 | 141590 17 | >>> feed['Schedule']['events'][40]['name'] 18 | 'There *Will* Be Bugs' 19 | >>> feed['Schedule']['events'][40]['speakers'] # <6> 20 | [3471, 5199] 21 | -------------------------------------------------------------------------------- /capitulos/code/22-dyn-attr-prop/oscon/runtests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | pytest --doctest-modules $2 $1 test_$1 -------------------------------------------------------------------------------- /capitulos/code/22-dyn-attr-prop/oscon/test_schedule_v1.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | import schedule_v1 as schedule 4 | 5 | 6 | @pytest.fixture 7 | def records(): 8 | yield schedule.load(schedule.JSON_PATH) 9 | 10 | 11 | def test_load(records): 12 | assert len(records) == 895 13 | 14 | 15 | def test_record_attr_access(): 16 | rec = schedule.Record(spam=99, eggs=12) 17 | assert rec.spam == 99 18 | assert rec.eggs == 12 19 | 20 | 21 | def test_venue_record(records): 22 | venue = records['venue.1469'] 23 | assert venue.serial == 1469 24 | assert venue.name == 'Exhibit Hall C' 25 | -------------------------------------------------------------------------------- /capitulos/code/22-dyn-attr-prop/pseudo_construction.py: -------------------------------------------------------------------------------- 1 | # pseudocode for object construction 2 | def make(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 = make(Foo, 'bar') 11 | -------------------------------------------------------------------------------- /capitulos/code/23-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 | -------------------------------------------------------------------------------- /capitulos/code/23-descriptor/bulkfood/model_v4c.py: -------------------------------------------------------------------------------- 1 | # BEGIN MODEL_V4 2 | class Quantity: 3 | 4 | def __set_name__(self, owner, name): # <1> 5 | self.storage_name = name # <2> 6 | 7 | def __set__(self, instance, value): # <3> 8 | if value > 0: 9 | instance.__dict__[self.storage_name] = value 10 | else: 11 | msg = f'{self.storage_name} must be > 0' 12 | raise ValueError(msg) 13 | # END MODEL_V4 14 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/autoconst/autoconst.py: -------------------------------------------------------------------------------- 1 | # tag::WilyDict[] 2 | class WilyDict(dict): 3 | def __init__(self, *args, **kwargs): 4 | super().__init__(*args, **kwargs) 5 | self.__next_value = 0 6 | 7 | def __missing__(self, key): 8 | if key.startswith('__') and key.endswith('__'): 9 | raise KeyError(key) 10 | self[key] = value = self.__next_value 11 | self.__next_value += 1 12 | return value 13 | # end::WilyDict[] 14 | 15 | # tag::AUTOCONST[] 16 | class AutoConstMeta(type): 17 | def __prepare__(name, bases, **kwargs): 18 | return WilyDict() 19 | 20 | class AutoConst(metaclass=AutoConstMeta): 21 | pass 22 | # end::AUTOCONST[] 23 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/checked/decorator/checkeddeco_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from checkeddeco import checked 4 | 5 | @checked 6 | class Movie: 7 | title: str 8 | year: int 9 | box_office: float 10 | 11 | 12 | if __name__ == '__main__': 13 | # No static type checker can understand this... 14 | movie = Movie(title='The Godfather', year=1972, box_office=137) # type: ignore 15 | print(movie.title) 16 | print(movie) 17 | try: 18 | # remove the "type: ignore" comment to see Mypy correctly spot the error 19 | movie.year = 'MCMLXXII' # type: ignore 20 | except TypeError as e: 21 | print(e) 22 | try: 23 | # Again, no static type checker can understand this... 24 | blockbuster = Movie(title='Avatar', year=2009, box_office='billions') # type: ignore 25 | except TypeError as e: 26 | print(e) 27 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/checked/initsub/checked_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from checkedlib import Checked 4 | 5 | class Movie(Checked): 6 | title: str 7 | year: int 8 | box_office: float 9 | 10 | 11 | if __name__ == '__main__': 12 | movie = Movie(title='The Godfather', year=1972, box_office=137) 13 | print(movie.title) 14 | print(movie) 15 | try: 16 | # remove the "type: ignore" comment to see Mypy error 17 | movie.year = 'MCMLXXII' # type: ignore 18 | except TypeError as e: 19 | print(e) 20 | try: 21 | blockbuster = Movie(title='Avatar', year=2009, box_office='billions') 22 | except TypeError as e: 23 | print(e) 24 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/checked/metaclass/checked_demo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # tag::MOVIE_DEMO[] 4 | from checkedlib import Checked 5 | 6 | class Movie(Checked): 7 | title: str 8 | year: int 9 | box_office: float 10 | 11 | if __name__ == '__main__': 12 | movie = Movie(title='The Godfather', year=1972, box_office=137) 13 | print(movie) 14 | print(movie.title) 15 | # end::MOVIE_DEMO[] 16 | 17 | try: 18 | # remove the "type: ignore" comment to see Mypy error 19 | movie.year = 'MCMLXXII' # type: ignore 20 | except TypeError as e: 21 | print(e) 22 | try: 23 | blockbuster = Movie(title='Avatar', year=2009, box_office='billions') 24 | except TypeError as e: 25 | print(e) 26 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/evaltime/evaldemo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from builderlib import Builder, deco, Descriptor 4 | 5 | print('# evaldemo module start') 6 | 7 | @deco # <1> 8 | class Klass(Builder): # <2> 9 | print('# Klass body') 10 | 11 | attr = Descriptor() # <3> 12 | 13 | def __init__(self): 14 | super().__init__() 15 | print(f'# Klass.__init__({self!r})') 16 | 17 | def __repr__(self): 18 | return '' 19 | 20 | 21 | def main(): # <4> 22 | obj = Klass() 23 | obj.method_a() 24 | obj.method_b() 25 | obj.attr = 999 26 | 27 | if __name__ == '__main__': 28 | main() 29 | 30 | print('# evaldemo module end') 31 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/evaltime/evaldemo_meta.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from builderlib import Builder, deco, Descriptor 4 | from metalib import MetaKlass # <1> 5 | 6 | print('# evaldemo_meta module start') 7 | 8 | @deco 9 | class Klass(Builder, metaclass=MetaKlass): # <2> 10 | print('# Klass body') 11 | 12 | attr = Descriptor() 13 | 14 | def __init__(self): 15 | super().__init__() 16 | print(f'# Klass.__init__({self!r})') 17 | 18 | def __repr__(self): 19 | return '' 20 | 21 | 22 | def main(): 23 | obj = Klass() 24 | obj.method_a() 25 | obj.method_b() 26 | obj.method_c() # <3> 27 | obj.attr = 999 28 | 29 | 30 | if __name__ == '__main__': 31 | main() 32 | 33 | print('# evaldemo_meta module end') 34 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/persistent/.gitignore: -------------------------------------------------------------------------------- 1 | *.db 2 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/qualname/fakedjango.py: -------------------------------------------------------------------------------- 1 | class models: 2 | class Model: 3 | "nothing to see here" 4 | class IntegerField: 5 | "nothing to see here" 6 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/qualname/models.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from fakedjango import models 4 | 5 | class Ox(models.Model): 6 | horn_length = models.IntegerField() 7 | 8 | class Meta: 9 | ordering = ['horn_length'] 10 | verbose_name_plural = 'oxen' 11 | 12 | print(Ox.Meta.__name__) 13 | print(Ox.Meta.__qualname__) 14 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/setattr/example_from_leo.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | class Foo: 4 | @property 5 | def bar(self): 6 | return self._bar 7 | 8 | @bar.setter 9 | def bar(self, value): 10 | self._bar = value 11 | 12 | def __setattr__(self, name, value): 13 | print(f'setting {name!r} to {value!r}') 14 | super().__setattr__(name, value) 15 | 16 | o = Foo() 17 | o.bar = 8 18 | print(o.bar) 19 | print(o._bar) 20 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/tinyenums/microenum_demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Testing ``Flavor``:: 3 | 4 | >>> Flavor.cocoa, Flavor.coconut, Flavor.vanilla 5 | (0, 1, 2) 6 | >>> Flavor[1] 7 | 'coconut' 8 | 9 | """ 10 | 11 | from microenum import MicroEnum 12 | 13 | 14 | class Flavor(MicroEnum): 15 | cocoa 16 | coconut 17 | vanilla 18 | -------------------------------------------------------------------------------- /capitulos/code/24-class-metaprog/tinyenums/nanoenum_demo.py: -------------------------------------------------------------------------------- 1 | """ 2 | Testing ``Flavor``:: 3 | 4 | >>> Flavor.coconut 5 | 'coconut' 6 | >>> Flavor.cocoa, Flavor.vanilla 7 | ('cocoa', 'vanilla') 8 | 9 | """ 10 | 11 | from nanoenum import NanoEnum 12 | 13 | 14 | class Flavor(NanoEnum): 15 | cocoa 16 | coconut 17 | vanilla 18 | -------------------------------------------------------------------------------- /como-gerar-o-livro.md: -------------------------------------------------------------------------------- 1 | # Gerando o livro apartir do fonte 2 | 3 | ## Instale as dependências necessárias. 4 | 5 | ### Ruby 6 | 7 | #### Linux 8 | 9 | ##### ubuntu 10 | 11 | ``` 12 | sudo apt-get install -y ruby 13 | ``` 14 | 15 | ##### arch 16 | 17 | ``` 18 | sudo pacman -S ruby` 19 | ``` 20 | 21 | #### Mac OS 22 | 23 | Com o [Homebrew](https://brew.sh/) instalado use o comando abaixo: 24 | 25 | ``` 26 | brew install ruby 27 | ``` 28 | 29 | #### Windows 30 | 31 | 32 | Com o [Chocolatey](https://chocolatey.org/) instalado use o comando abaixo: 33 | 34 | ``` 35 | choco install ruby 36 | ``` 37 | 38 | ### Asciidoctor-epub3 39 | 40 | ``` 41 | gem install asciidoctor-epub3 42 | ``` 43 | 44 | ### Gerando livro no formato epub. 45 | 46 | Na raiz do projeto rode o comando: 47 | 48 | ``` 49 | asciidoctor-epub3 livro.adoc -o 'Python Fluente - Luciano Ramalho, Segunda Edição (2023).epub' 50 | ``` 51 | -------------------------------------------------------------------------------- /cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/cover.jpg -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | 2 | #!/bin/bash 3 | set -e # exit when any command fails 4 | asciidoctor Livro.adoc -o index.html 5 | 6 | #scp index.html dh_kqh7yy@pdx1-shared-a1-19.dreamhost.com:/home/dh_kqh7yy/pythonfluente.com/index.html 7 | rsync -avz --delete index.html dh_kqh7yy@pythonfluente.com:~/pythonfluente.com/2/ 8 | 9 | open https://pythonfluente.com/2/ 10 | -------------------------------------------------------------------------------- /images/despacho-duplo.graffle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/despacho-duplo.graffle -------------------------------------------------------------------------------- /images/flpy_0101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0101.png -------------------------------------------------------------------------------- /images/flpy_0102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0102.png -------------------------------------------------------------------------------- /images/flpy_0201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0201.png -------------------------------------------------------------------------------- /images/flpy_0202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0202.png -------------------------------------------------------------------------------- /images/flpy_0203.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0203.png -------------------------------------------------------------------------------- /images/flpy_0204.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0204.png -------------------------------------------------------------------------------- /images/flpy_0205.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0205.png -------------------------------------------------------------------------------- /images/flpy_0301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0301.png -------------------------------------------------------------------------------- /images/flpy_0302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0302.png -------------------------------------------------------------------------------- /images/flpy_0401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0401.png -------------------------------------------------------------------------------- /images/flpy_0402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0402.png -------------------------------------------------------------------------------- /images/flpy_0403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0403.png -------------------------------------------------------------------------------- /images/flpy_0404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0404.png -------------------------------------------------------------------------------- /images/flpy_0405.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0405.png -------------------------------------------------------------------------------- /images/flpy_0406.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0406.png -------------------------------------------------------------------------------- /images/flpy_0407.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0407.png -------------------------------------------------------------------------------- /images/flpy_0408.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0408.png -------------------------------------------------------------------------------- /images/flpy_0601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0601.png -------------------------------------------------------------------------------- /images/flpy_0602.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0602.png -------------------------------------------------------------------------------- /images/flpy_0603.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0603.png -------------------------------------------------------------------------------- /images/flpy_0604.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0604.png -------------------------------------------------------------------------------- /images/flpy_0701.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0701.png -------------------------------------------------------------------------------- /images/flpy_0901.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_0901.png -------------------------------------------------------------------------------- /images/flpy_1001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1001.png -------------------------------------------------------------------------------- /images/flpy_1002.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1002.png -------------------------------------------------------------------------------- /images/flpy_1101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1101.png -------------------------------------------------------------------------------- /images/flpy_1201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1201.png -------------------------------------------------------------------------------- /images/flpy_1202.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1202.png -------------------------------------------------------------------------------- /images/flpy_1301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1301.png -------------------------------------------------------------------------------- /images/flpy_1302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1302.png -------------------------------------------------------------------------------- /images/flpy_1303.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1303.png -------------------------------------------------------------------------------- /images/flpy_1304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1304.png -------------------------------------------------------------------------------- /images/flpy_1305.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1305.png -------------------------------------------------------------------------------- /images/flpy_1306.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1306.png -------------------------------------------------------------------------------- /images/flpy_1307.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1307.png -------------------------------------------------------------------------------- /images/flpy_1308.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1308.png -------------------------------------------------------------------------------- /images/flpy_1401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1401.png -------------------------------------------------------------------------------- /images/flpy_1402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1402.png -------------------------------------------------------------------------------- /images/flpy_1403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1403.png -------------------------------------------------------------------------------- /images/flpy_1404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1404.png -------------------------------------------------------------------------------- /images/flpy_1405.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1405.png -------------------------------------------------------------------------------- /images/flpy_1601-EN.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1601-EN.png -------------------------------------------------------------------------------- /images/flpy_1601.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1601.png -------------------------------------------------------------------------------- /images/flpy_1701.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1701.png -------------------------------------------------------------------------------- /images/flpy_1801.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1801.png -------------------------------------------------------------------------------- /images/flpy_1901.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1901.png -------------------------------------------------------------------------------- /images/flpy_1902.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1902.png -------------------------------------------------------------------------------- /images/flpy_1903.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1903.png -------------------------------------------------------------------------------- /images/flpy_1904.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_1904.png -------------------------------------------------------------------------------- /images/flpy_2001.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2001.png -------------------------------------------------------------------------------- /images/flpy_2101.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2101.png -------------------------------------------------------------------------------- /images/flpy_2102.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2102.png -------------------------------------------------------------------------------- /images/flpy_2103.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2103.png -------------------------------------------------------------------------------- /images/flpy_2104.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2104.png -------------------------------------------------------------------------------- /images/flpy_2105.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2105.png -------------------------------------------------------------------------------- /images/flpy_2201.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2201.png -------------------------------------------------------------------------------- /images/flpy_2301.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2301.png -------------------------------------------------------------------------------- /images/flpy_2302.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2302.png -------------------------------------------------------------------------------- /images/flpy_2303.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2303.png -------------------------------------------------------------------------------- /images/flpy_2304.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2304.png -------------------------------------------------------------------------------- /images/flpy_2401.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2401.png -------------------------------------------------------------------------------- /images/flpy_2402.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2402.png -------------------------------------------------------------------------------- /images/flpy_2403.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2403.png -------------------------------------------------------------------------------- /images/flpy_2404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/images/flpy_2404.png -------------------------------------------------------------------------------- /links/cap01-urls.txt: -------------------------------------------------------------------------------- 1 | https://docs.python.org/pt-br/3/reference/lexical_analysis.html#reserved-classes-of-identifiers 2 | https://docs.python.org/pt-br/3/library/doctest.html 3 | https://docs.python.org/pt-br/3.10/library/string.html#format-string-syntax 4 | https://docs.python.org/pt-br/3/library/stdtypes.html#truth 5 | https://docs.python.org/pt-br/3/tutorial/controlflow.html#unpacking-argument-lists 6 | https://docs.python.org/pt-br/3/reference/datamodel.html 7 | https://docs.python.org/pt-br/3/reference/datamodel.html 8 | https://dabeaz.com/per.html 9 | https://mitpress.mit.edu/books/art-metaobject-protocol 10 | https://docs.python.org/pt-br/3/reference/datamodel.html 11 | https://plone.org.br/ 12 | -------------------------------------------------------------------------------- /links/data/README.txt: -------------------------------------------------------------------------------- 1 | data files for testing, used by pytest-datadir 2 | -------------------------------------------------------------------------------- /links/data/sample.htaccess: -------------------------------------------------------------------------------- 1 | ErrorDocument 404 /404.html 2 | 3 | # main resources 4 | RedirectTemp /book https://www.oreilly.com/.../9781492056348/ 5 | RedirectTemp /home https://www.fluentpython.com/ # extra content site 6 | 7 | # duplicate targets 8 | RedirectTemp /1-20 https://www.fluentpython.com/ 9 | RedirectTemp /ora https://www.oreilly.com/.../9781492056348/ 10 | RedirectTemp /2-10 http://example.com/ 11 | RedirectTemp /10-2 http://example.com/ 12 | -------------------------------------------------------------------------------- /links/fpy-deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | scp FPY.LI.htaccess dh_i4p2ka@fpy.li:~/fpy.li/.htaccess 3 | -------------------------------------------------------------------------------- /links/prep1_shorten_urls.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import fileinput 4 | import re 5 | 6 | URL_RE = re.compile(r"""https?://[^\s[<>"']+""") 7 | 8 | 9 | def find_urls(fpy=True, long=True): 10 | found = 0 11 | for line in (l.rstrip() for l in fileinput.input()): 12 | if match := URL_RE.search(line): 13 | url = match.group() 14 | is_fpy = '://fpy.li/' in url 15 | if (is_fpy and not fpy) or (not is_fpy and not long): 16 | continue 17 | print(url) 18 | found += 1 19 | # print('FOUND', found, 'URLs') 20 | 21 | 22 | if __name__ == '__main__': 23 | find_urls(fpy=False) 24 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | anyio==4.9.0 2 | certifi==2025.4.26 3 | h11==0.16.0 4 | httpcore==1.0.9 5 | httpx==0.28.1 6 | idna==3.10 7 | iniconfig==2.1.0 8 | packaging==25.0 9 | pluggy==1.6.0 10 | Pygments==2.19.1 11 | pytest==8.4.0 12 | pytest-datadir==1.7.2 13 | ruff==0.11.11 14 | sniffio==1.3.1 15 | -------------------------------------------------------------------------------- /ruff.toml: -------------------------------------------------------------------------------- 1 | line-length = 100 2 | [format] 3 | # Use single quotes for strings, like Python's repr() 4 | quote-style = "single" 5 | -------------------------------------------------------------------------------- /volumes/.gitignore: -------------------------------------------------------------------------------- 1 | go_check 2 | -------------------------------------------------------------------------------- /volumes/1/code/01-data-model/README.md: -------------------------------------------------------------------------------- 1 | # The Python Data Model 2 | 3 | Sample code for Chapter 1 of _Fluent Python 2e_ by Luciano Ramalho (O'Reilly, 2020) 4 | 5 | ## Running the tests 6 | 7 | ### Doctests 8 | 9 | Use Python's standard ``doctest`` module to check stand-alone doctest file: 10 | 11 | $ python3 -m doctest frenchdeck.doctest -v 12 | 13 | And to check doctests embedded in a module: 14 | 15 | $ python3 -m doctest vector2d.py -v 16 | 17 | ### Jupyter Notebook 18 | 19 | Install ``pytest`` and the ``nbval`` plugin: 20 | 21 | $ pip install pytest nbval 22 | 23 | Run: 24 | 25 | $ pytest --nbval 26 | -------------------------------------------------------------------------------- /volumes/1/code/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 | -------------------------------------------------------------------------------- /volumes/1/code/01-data-model/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 -m doctest frenchdeck.doctest 3 | python3 -m doctest vector2d.py 4 | pytest -q --nbval 5 | -------------------------------------------------------------------------------- /volumes/1/code/01-data-model/vector2d.doctest: -------------------------------------------------------------------------------- 1 | >>> from vector2d import Vector 2 | >>> v1 = Vector(2, 4) 3 | >>> v2 = Vector(2, 1) 4 | >>> v1 + v2 5 | Vector(4, 5) 6 | >>> v = Vector(3, 4) 7 | >>> abs(v) 8 | 5.0 9 | >>> v * 3 10 | Vector(9, 12) 11 | >>> abs(v * 3) 12 | 15.0 13 | -------------------------------------------------------------------------------- /volumes/1/code/02-array-seq/README.rst: -------------------------------------------------------------------------------- 1 | Sample code for Chapter 2 - "An array of sequences" 2 | 3 | From the book "Fluent Python 2e" by Luciano Ramalho (O'Reilly) 4 | http://shop.oreilly.com/product/0636920032519.do 5 | -------------------------------------------------------------------------------- /volumes/1/code/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(f'{new_item:2d} -> {my_list}') 13 | -------------------------------------------------------------------------------- /volumes/1/code/02-array-seq/lispy/py3.10/quicksort.scm: -------------------------------------------------------------------------------- 1 | (define (quicksort lst) 2 | (if (null? lst) 3 | lst 4 | (begin 5 | (define pivot (car lst)) 6 | (define rest (cdr lst)) 7 | (append 8 | (quicksort 9 | (filter (lambda (x) (< x pivot)) rest)) 10 | (list pivot) 11 | (quicksort 12 | (filter (lambda (x) (>= x pivot)) rest))) 13 | ) 14 | ) 15 | ) 16 | (display 17 | (quicksort (list 2 1 6 3 4 0 8 9 7 5))) 18 | -------------------------------------------------------------------------------- /volumes/1/code/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, *(f'{x:.3f}' 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 | -------------------------------------------------------------------------------- /volumes/1/code/02-array-seq/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 -m doctest bisect_demo.py 3 | python3 -m doctest metro_lat_long.py 4 | pytest -q --nbval 5 | -------------------------------------------------------------------------------- /volumes/1/code/03-dict-set/README.md: -------------------------------------------------------------------------------- 1 | # Dictionaries and Sets 2 | 3 | Sample code for Chapter 3 of _Fluent Python 2e_ by Luciano Ramalho (O'Reilly, 2020) 4 | 5 | ## Running the tests 6 | 7 | ### Doctests 8 | 9 | Use Python's standard ``doctest`` module, for example: 10 | 11 | $ python3 -m doctest bisect_demo.py -v 12 | 13 | ### Jupyter Notebook 14 | 15 | Install ``pytest`` and the ``nbval`` plugin: 16 | 17 | $ pip install pytest nbval 18 | 19 | Run: 20 | 21 | $ pytest --nbval -------------------------------------------------------------------------------- /volumes/1/code/03-dict-set/dialcodes.py: -------------------------------------------------------------------------------- 1 | # tag::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 | # tag::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 | -------------------------------------------------------------------------------- /volumes/1/code/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 | # tag::INDEX[] 6 | """Build an index mapping word -> list of occurrences""" 7 | 8 | import re 9 | import sys 10 | 11 | WORD_RE = re.compile(r'\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 | # display in alphabetical order 23 | for word in sorted(index, key=str.upper): 24 | print(word, index[word]) 25 | # end::INDEX[] 26 | -------------------------------------------------------------------------------- /volumes/1/code/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 | # tag::INDEX_DEFAULT[] 6 | """Build an index mapping word -> list of occurrences""" 7 | 8 | import collections 9 | import re 10 | import sys 11 | 12 | WORD_RE = re.compile(r'\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 | # display in alphabetical order 24 | for word in sorted(index, key=str.upper): 25 | print(word, index[word]) 26 | # end::INDEX_DEFAULT[] 27 | -------------------------------------------------------------------------------- /volumes/1/code/03-dict-set/support/hashdiff.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | MAX_BITS = len(format(sys.maxsize, 'b')) 4 | print(f'{MAX_BITS + 1}-bit Python build') 5 | 6 | def hash_diff(o1, o2): 7 | h1 = f'{hash(o1):>0{MAX_BITS}b}' 8 | h2 = f'{hash(o2):>0{MAX_BITS}b}' 9 | diff = ''.join('!' if b1 != b2 else ' ' for b1, b2 in zip(h1, h2)) 10 | count = f'!= {diff.count("!")}' 11 | width = max(len(repr(o1)), len(repr(o2)), 8) 12 | sep = '-' * (width * 2 + MAX_BITS) 13 | return (f'{o1!r:{width}} {h1}\n{" ":{width}} {diff} {count}\n' 14 | f'{o2!r:{width}} {h2}\n{sep}') 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 | -------------------------------------------------------------------------------- /volumes/1/code/03-dict-set/zen.txt: -------------------------------------------------------------------------------- 1 | The Zen of Python, by Tim Peters 2 | 3 | Beautiful is better than ugly. 4 | Explicit is better than implicit. 5 | Simple is better than complex. 6 | Complex is better than complicated. 7 | Flat is better than nested. 8 | Sparse is better than dense. 9 | Readability counts. 10 | Special cases aren't special enough to break the rules. 11 | Although practicality beats purity. 12 | Errors should never pass silently. 13 | Unless explicitly silenced. 14 | In the face of ambiguity, refuse the temptation to guess. 15 | There should be one-- and preferably only one --obvious way to do it. 16 | Although that way may not be obvious at first unless you're Dutch. 17 | Now is better than never. 18 | Although never is often better than *right* now. 19 | If the implementation is hard to explain, it's a bad idea. 20 | If the implementation is easy to explain, it may be a good idea. 21 | Namespaces are one honking great idea -- let's do more of those! 22 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/.gitignore: -------------------------------------------------------------------------------- 1 | dummy 2 | -------------------------------------------------------------------------------- /volumes/1/code/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 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/charfinder/cf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | import sys 3 | import unicodedata 4 | 5 | START, END = ord(' '), sys.maxunicode + 1 # <1> 6 | 7 | def find(*query_words, start=START, end=END): # <2> 8 | query = {w.upper() for w in query_words} # <3> 9 | for code in range(start, end): 10 | char = chr(code) # <4> 11 | name = unicodedata.name(char, None) # <5> 12 | if name and query.issubset(name.split()): # <6> 13 | print(f'U+{code:04X}\t{char}\t{name}') # <7> 14 | 15 | def main(words): 16 | if words: 17 | find(*words) 18 | else: 19 | print('Please provide words to find.') 20 | 21 | if __name__ == '__main__': 22 | main(sys.argv[1:]) 23 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/charfinder/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | python3 -m doctest README.rst $1 3 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/default_encodings.py: -------------------------------------------------------------------------------- 1 | import locale 2 | import sys 3 | 4 | expressions = """ 5 | locale.getpreferredencoding() 6 | type(my_file) 7 | my_file.encoding 8 | sys.stdout.isatty() 9 | sys.stdout.encoding 10 | sys.stdin.isatty() 11 | sys.stdin.encoding 12 | sys.stderr.isatty() 13 | sys.stderr.encoding 14 | sys.getdefaultencoding() 15 | sys.getfilesystemencoding() 16 | """ 17 | 18 | my_file = open('dummy', 'w') 19 | 20 | for expression in expressions.split(): 21 | value = eval(expression) 22 | print(f'{expression:>30} -> {value!r}') 23 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/encodings-win10.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/volumes/1/code/04-text-byte/encodings-win10.txt -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/locale_sort.py: -------------------------------------------------------------------------------- 1 | import locale 2 | my_locale = locale.setlocale(locale.LC_COLLATE, 'pt_BR.UTF-8') 3 | print(my_locale) 4 | fruits = ['caju', 'atemoia', 'cajá', 'açaí', 'acerola'] 5 | sorted_fruits = sorted(fruits, key=locale.strxfrm) 6 | print(sorted_fruits) 7 | -------------------------------------------------------------------------------- /volumes/1/code/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 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/numerics_demo.py: -------------------------------------------------------------------------------- 1 | # tag::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(f'U+{ord(char):04x}', # <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 | f'{unicodedata.numeric(char):5.2f}', # <6> 16 | unicodedata.name(char), # <7> 17 | sep='\t') 18 | # end::NUMERICS_DEMO[] 19 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/ola.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/volumes/1/code/04-text-byte/ola.py -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/ramanujan.py: -------------------------------------------------------------------------------- 1 | # tag::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(f'Text\n {text_str!r}') 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 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/skin.py: -------------------------------------------------------------------------------- 1 | from unicodedata import name 2 | 3 | SKIN1 = 0x1F3FB # EMOJI MODIFIER FITZPATRICK TYPE-1-2 # <1> 4 | SKINS = [chr(i) for i in range(SKIN1, SKIN1 + 5)] # <2> 5 | THUMB = '\U0001F44d' # THUMBS UP SIGN 👍 6 | 7 | examples = [THUMB] # <3> 8 | examples.extend(THUMB + skin for skin in SKINS) # <4> 9 | 10 | for example in examples: 11 | print(example, end='\t') # <5> 12 | print(' + '.join(name(char) for char in example)) # <6> 13 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/stdout_check.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from unicodedata import name 3 | 4 | print(sys.version) 5 | print() 6 | print('sys.stdout.isatty():', sys.stdout.isatty()) 7 | print('sys.stdout.encoding:', sys.stdout.encoding) 8 | print() 9 | 10 | test_chars = [ 11 | '\N{HORIZONTAL ELLIPSIS}', # exists in cp1252, not in cp437 12 | '\N{INFINITY}', # exists in cp437, not in cp1252 13 | '\N{CIRCLED NUMBER FORTY TWO}', # not in cp437 or in cp1252 14 | ] 15 | 16 | for char in test_chars: 17 | print(f'Trying to output {name(char)}:') 18 | print(char) 19 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/syntax-msg.txt: -------------------------------------------------------------------------------- 1 | SyntaxError: Non-UTF-8 code starting with '\xe1' in file ola.py on line 2 | 1, but no encoding declared; see https://python.org/dev/peps/pep-0263/ 3 | for details -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/two_flags.py: -------------------------------------------------------------------------------- 1 | # REGIONAL INDICATOR SYMBOLS 2 | RIS_A = '\U0001F1E6' # LETTER A 3 | RIS_U = '\U0001F1FA' # LETTER U 4 | print(RIS_A + RIS_U) # AU: Australia 5 | print(RIS_U + RIS_A) # UA: Ukraine 6 | print(RIS_A + RIS_A) # AA: no such country 7 | -------------------------------------------------------------------------------- /volumes/1/code/04-text-byte/zwj_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pythonfluente/pythonfluente2e/84fc7bb73ecb29f1eee9480a1d35ec162dd88ea5/volumes/1/code/04-text-byte/zwj_sample.png -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/README.asciidoc: -------------------------------------------------------------------------------- 1 | == Record-like Structures 2 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/cards.doctest: -------------------------------------------------------------------------------- 1 | >>> from cards import Card 2 | >>> helen = Card('Q', 'hearts') 3 | >>> helen 4 | Card(rank='Q', suit='hearts') 5 | 6 | >>> cards = [Card(r, s) for s in Card.suits for r in Card.ranks] 7 | >>> cards[:3] 8 | [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')] 9 | >>> sorted(cards)[:3] 10 | [Card(rank='2', suit='clubs'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='hearts')] 11 | 12 | >>> from cards_enum import Card, Suit, Rank 13 | >>> helen = Card('Q', 'hearts') 14 | >>> helen 15 | Card(rank='Q', suit='hearts') 16 | 17 | >>> cards = [Card(r, s) for s in Suit for r in Rank] 18 | >>> cards[:3] 19 | [Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')] 20 | >>> sorted(cards)[:3] 21 | [Card(rank='2', suit='clubs'), Card(rank='2', suit='diamonds'), Card(rank='2', suit='hearts')] 22 | >>> for card in cards[12::13]: print(card) 23 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/cards.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | @dataclass(order=True) 4 | class Card: 5 | rank: str 6 | suit: str 7 | 8 | ranks = [str(n) for n in range(2, 10)] + list('JQKA') 9 | suits = 'spades diamonds clubs hearts'.split() 10 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/cards_enum.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | import enum 3 | 4 | Suit = enum.IntEnum('Suit', 'spades diamonds clubs hearts') 5 | Rank = enum.Enum('Rank', [str(n) for n in range(2, 10)] + list('JQKA')) 6 | 7 | @dataclass(order=True) 8 | class Card: 9 | rank: Suit 10 | suit: Rank 11 | 12 | def __str__(self): 13 | glyphs = [chr(x) for x in range(0x2660, 0x2664)] 14 | return f'{self.rank} of {glyphs[self.suit-1]}' 15 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/class/coordinates.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: a simple class with a custom ``__str__``:: 3 | 4 | >>> moscow = Coordinate(55.756, 37.617) 5 | >>> print(moscow) # doctest:+ELLIPSIS 6 | 7 | """ 8 | 9 | # tag::COORDINATE[] 10 | class Coordinate: 11 | 12 | def __init__(self, lat, lon): 13 | self.lat = lat 14 | self.lon = lon 15 | 16 | # end::COORDINATE[] -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/dataclass/club.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | 3 | @dataclass 4 | class ClubMember: 5 | name: str 6 | guests: list = field(default_factory=list) 7 | 8 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/dataclass/club_generic.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass, field 2 | 3 | @dataclass 4 | class ClubMember: 5 | name: str 6 | guests: list[str] = field(default_factory=list) # <1> 7 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/dataclass/club_wrong.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | # tag::CLUBMEMBER[] 4 | @dataclass 5 | class ClubMember: 6 | name: str 7 | guests: list = [] 8 | # end::CLUBMEMBER[] 9 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/dataclass/coordinates.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: simple class decorated with ``dataclass`` and a custom ``__str__``:: 3 | 4 | >>> moscow = Coordinate(55.756, 37.617) 5 | >>> print(moscow) 6 | 55.8°N, 37.6°E 7 | 8 | """ 9 | 10 | # tag::COORDINATE[] 11 | 12 | from dataclasses import dataclass 13 | 14 | @dataclass(frozen=True) 15 | class Coordinate: 16 | lat: float 17 | lon: float 18 | 19 | def __str__(self): 20 | ns = 'N' if self.lat >= 0 else 'S' 21 | we = 'E' if self.lon >= 0 else 'W' 22 | return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}' 23 | # end::COORDINATE[] 24 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/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 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/meaning/demo_dc.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | @dataclass 4 | class DemoDataClass: 5 | a: int # <1> 6 | b: float = 1.1 # <2> 7 | c = 'spam' # <3> 8 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/meaning/demo_nt.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | class DemoNTClass(typing.NamedTuple): 4 | a: int # <1> 5 | b: float = 1.1 # <2> 6 | c = 'spam' # <3> 7 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/meaning/demo_plain.py: -------------------------------------------------------------------------------- 1 | class DemoPlainClass: 2 | a: int # <1> 3 | b: float = 1.1 # <2> 4 | c = 'spam' # <3> 5 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/typing_namedtuple/coordinates.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: a simple ``NamedTuple`` subclass with a custom ``__str__``:: 3 | 4 | >>> moscow = Coordinate(55.756, 37.617) 5 | >>> print(moscow) 6 | 55.8°N, 37.6°E 7 | 8 | """ 9 | 10 | # tag::COORDINATE[] 11 | from typing import NamedTuple 12 | 13 | class Coordinate(NamedTuple): 14 | lat: float 15 | lon: float 16 | 17 | def __str__(self): 18 | ns = 'N' if self.lat >= 0 else 'S' 19 | we = 'E' if self.lon >= 0 else 'W' 20 | return f'{abs(self.lat):.1f}°{ns}, {abs(self.lon):.1f}°{we}' 21 | # end::COORDINATE[] 22 | -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/typing_namedtuple/coordinates2.py: -------------------------------------------------------------------------------- 1 | """ 2 | ``Coordinate``: a simple ``NamedTuple`` subclass 3 | 4 | This version has a field with a default value:: 5 | 6 | >>> moscow = Coordinate(55.756, 37.617) 7 | >>> moscow 8 | Coordinate(lat=55.756, lon=37.617, reference='WGS84') 9 | 10 | """ 11 | 12 | # tag::COORDINATE[] 13 | from typing import NamedTuple 14 | 15 | class Coordinate(NamedTuple): 16 | lat: float # <1> 17 | lon: float 18 | reference: str = 'WGS84' # <2> 19 | # end::COORDINATE[] -------------------------------------------------------------------------------- /volumes/1/code/05-data-classes/typing_namedtuple/nocheck_demo.py: -------------------------------------------------------------------------------- 1 | import typing 2 | 3 | class Coordinate(typing.NamedTuple): 4 | lat: float 5 | lon: float 6 | 7 | trash = Coordinate('Ni!', None) # <1> 8 | print(trash) 9 | -------------------------------------------------------------------------------- /volumes/1/code/06-obj-ref/bus.py: -------------------------------------------------------------------------------- 1 | """ 2 | >>> import copy 3 | >>> bus1 = Bus(['Alice', 'Bill', 'Claire', 'David']) 4 | >>> bus2 = copy.copy(bus1) 5 | >>> bus3 = copy.deepcopy(bus1) 6 | >>> bus1.drop('Bill') 7 | >>> bus2.passengers 8 | ['Alice', 'Claire', 'David'] 9 | >>> bus3.passengers 10 | ['Alice', 'Bill', 'Claire', 'David'] 11 | 12 | """ 13 | 14 | # tag::BUS_CLASS[] 15 | class Bus: 16 | 17 | def __init__(self, passengers=None): 18 | if passengers is None: 19 | self.passengers = [] 20 | else: 21 | self.passengers = list(passengers) 22 | 23 | def pick(self, name): 24 | self.passengers.append(name) 25 | 26 | def drop(self, name): 27 | self.passengers.remove(name) 28 | # end::BUS_CLASS[] 29 | -------------------------------------------------------------------------------- /volumes/1/code/06-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 | # tag::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 | -------------------------------------------------------------------------------- /volumes/build-1.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e # exit when any command fails 3 | asciidoctor -v vol1.adoc -o vol1.html 4 | open vol1.html 5 | -------------------------------------------------------------------------------- /volumes/experimentos/notas.txt: -------------------------------------------------------------------------------- 1 | # Notas para publicar em volumes 2 | 3 | ## Remover todos os passthroughs 4 | 5 | Perigo: pass:[]!!! 6 | 7 | ## Referências para capítulos ou partes de outros volumes 8 | 9 | Converter <> para _{ch_data_model}_ 10 | 11 | ## Referências para seções, exemplos ou figuras de outros volumes 12 | 13 | Converter (note "na" -> "em"): 14 | 15 | na <> 16 | 17 | (renderizada como "Seção 12.5.1") 18 | 19 | para isso: 20 | 21 | em _Como funciona o fatiamento_ (fpy.li/2bhwsl) 22 | 23 | onde fpy.li/2bhwsl é esta URL encurtada, direto para a seção: 24 | (https://pythonfluente.com/2e/#how_slicing_works) -------------------------------------------------------------------------------- /volumes/experimentos/pyfl_refs.txt: -------------------------------------------------------------------------------- 1 | https://pythonfluente.com/2/#pattern_matching_case_study_sec 2 | https://pythonfluente.com/2/#pattern_matching_case_study_sec 3 | https://pythonfluente.com/2/#pattern_matching_case_study_sec 4 | https://pythonfluente.com/2/#how_slicing_works 5 | https://pythonfluente.com/2/#sliceable_sequence 6 | https://pythonfluente.com/2/#virtual_subclass_sec 7 | https://pythonfluente.com/2/#environment_class_ex 8 | https://pythonfluente.com/2/#subclass_builtin_woes 9 | https://pythonfluente.com/2/#slots_section 10 | https://pythonfluente.com/2/#typeddict_sec 11 | https://pythonfluente.com/2/#problems_annot_runtime_sec 12 | https://pythonfluente.com/2/#legacy_deprecated_typing_box 13 | https://pythonfluente.com/2/#positional_pattern_implement_sec 14 | -------------------------------------------------------------------------------- /volumes/experimentos/sort_check_output.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import fileinput 4 | import re 5 | 6 | TASK_RE = re.compile(r'\[\s*(\d+)\]') 7 | 8 | def task_id(line): 9 | found = TASK_RE.match(line) 10 | return int(match.group(1)) if found else 0 11 | 12 | for line in sorted(fileinput.input(), key=task_id): 13 | print(line, end='') 14 | -------------------------------------------------------------------------------- /volumes/experimentos/vol1-results-orca.txt: -------------------------------------------------------------------------------- 1 | [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1028) 2 | URL: https://dabeaz.com/per.html 3 | -1 https://dabeaz.com/per.html 4 | 302 http://gopl.io 5 | 302 https://pythonfluente.com/#iter_closer_look 6 | 301 https://mitpress.mit.edu/books/art-metaobject-protocol 7 | -------------------------------------------------------------------------------- /volumes/experimentos/vol1-url-checked.txt: -------------------------------------------------------------------------------- 1 | [ 3] 302 https://pythonfluente.com/#iter_closer_look 2 | [ 3] 200 https://pythonfluente.com/2/ 3 | [ 8] Error connecting to https://dabeaz.com/per.html: Get "https://dabeaz.com/per.html": tls: failed to verify certificate: x509: certificate signed by unknown authority 4 | [ 9] 301 https://mitpress.mit.edu/books/art-metaobject-protocol 5 | [ 9] 301 https://mitpress.mit.edu/9780262111584 6 | [ 9] 301 https://mitpress.mit.edu/9780262111584/ 7 | [ 9] 200 https://mitpress.mit.edu/9780262111584/the-art-of-the-metaobject-protocol/ 8 | [ 26] 302 http://gopl.io 9 | [ 26] 302 https://gopl.io/ 10 | [ 26] 200 http://www.gopl.io/ 11 | -------------------------------------------------------------------------------- /volumes/experimentos/vol1-urls-checked.txt: -------------------------------------------------------------------------------- 1 | [ 3] 302 https://pythonfluente.com/#iter_closer_look 2 | [ 3] 200 https://pythonfluente.com/2/#iter_closer_look 3 | [ 8] *ERROR* Get "https://dabeaz.com/per.html": tls: failed to verify certificate: x509: certificate signed by unknown authority 4 | [ 9] 301 https://mitpress.mit.edu/books/art-metaobject-protocol 5 | [ 9] 301 https://mitpress.mit.edu/9780262111584 6 | [ 9] 301 https://mitpress.mit.edu/9780262111584/ 7 | [ 9] 200 https://mitpress.mit.edu/9780262111584/the-art-of-the-metaobject-protocol/ 8 | [ 26] 302 http://gopl.io 9 | [ 26] 302 https://gopl.io/ 10 | [ 26] 200 http://www.gopl.io/ 11 | -------------------------------------------------------------------------------- /volumes/vol1.adoc: -------------------------------------------------------------------------------- 1 | = Python Fluente: Estruturas de Dados 2 | :doctype: book 3 | :author: Luciano Ramalho 4 | :lang: pt_BR 5 | :language: asciidoctor 6 | :xrefstyle: short 7 | :sectnums: 8 | :sectlinks: 9 | :data-uri: 10 | :toc: left 11 | :toclevels: 2 12 | :!chapter-signifier: 13 | :front-cover-image: image:cover.jpg[fit=cover] 14 | :media: print 15 | include::atributos-pt_BR.adoc[] 16 | include::xrefs-v1.adoc[] 17 | 18 | include::Prefacio.adoc[] 19 | 20 | [[vo_data_str]] 21 | = Volume I: Estruturas de dados 22 | 23 | include::1/cap01.adoc[] 24 | 25 | include::1/cap02.adoc[] 26 | 27 | include::1/cap03.adoc[] 28 | 29 | include::1/cap04.adoc[] 30 | 31 | include::1/cap05.adoc[] 32 | 33 | include::1/cap06.adoc[] 34 | --------------------------------------------------------------------------------