├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── bezdekIris.data ├── bezdekIris.json ├── bezdekIris.ndjson ├── bezdekIris.yaml ├── bezdekiris.data ├── bezdekiris.json ├── bezdekiris.ndjson ├── bezdekiris.yaml ├── ch_01 ├── README.md ├── concept_image.ipynb ├── distance_images.ipynb ├── docs │ ├── Concept.png │ ├── Fig_11.drawio │ ├── context.png │ ├── context.uml │ ├── development_view_1.png │ ├── development_view_1.uml │ ├── fig_1.png │ ├── fig_1.uml │ ├── fig_10.png │ ├── fig_10.uml │ ├── fig_11.png │ ├── fig_11_alt.png │ ├── fig_2.png │ ├── fig_2.uml │ ├── fig_3.png │ ├── fig_3.uml │ ├── fig_4.png │ ├── fig_4.uml │ ├── fig_5.png │ ├── fig_5.uml │ ├── fig_6.png │ ├── fig_6.uml │ ├── fig_7.png │ ├── fig_7.uml │ ├── fig_8.png │ ├── fig_8.uml │ ├── fig_9.png │ ├── fig_9.uml │ ├── logical_view_1.png │ ├── logical_view_1.uml │ ├── logical_view_2.png │ ├── logical_view_2.uml │ ├── logical_view_3.png │ ├── logical_view_3.uml │ ├── logical_view_4.png │ ├── logical_view_4.uml │ ├── physical_view_1.png │ ├── physical_view_1.uml │ ├── process_view_1.png │ └── process_view_1.uml └── pyproject.toml ├── ch_02 ├── README.md ├── __init__.py ├── docs │ ├── case_study_2.md │ ├── examples.md │ ├── fig_1.png │ ├── fig_1.uml │ ├── fig_2.png │ ├── fig_2.uml │ ├── fig_3.png │ ├── fig_3.uml │ ├── fig_4.png │ └── fig_4.uml ├── pyproject.toml ├── requirements.txt ├── src │ ├── bad_hints.py │ ├── ecommerce │ │ ├── __init__.py │ │ ├── contact │ │ │ ├── __init__.py │ │ │ └── email.py │ │ ├── database.py │ │ ├── payments │ │ │ ├── __init__.py │ │ │ ├── common.py │ │ │ ├── square.py │ │ │ └── stripe.py │ │ ├── products.py │ │ └── vendors.py │ ├── first_class.py │ ├── formatter.py │ ├── main.py │ ├── model.py │ ├── point_1.py │ ├── point_2.py │ ├── point_3.py │ └── point_4.py └── tests │ └── test_ecommerce.py ├── ch_03 ├── README.rst ├── docs │ ├── Chebyshev.png │ ├── Distances-Chebyshev.png │ ├── Distances-Euclidean.png │ ├── Distances-Manhattan.png │ ├── Distances-Sorensen.png │ ├── Distances.drawio │ ├── Euclidean.png │ ├── Manhattan.png │ ├── Sorensen.png │ ├── case_study_3.md │ ├── examples.md │ ├── examples_38.md │ ├── fig_1.png │ ├── fig_1.uml │ ├── fig_2.png │ ├── fig_2.uml │ ├── logical_view.png │ └── logical_view.uml ├── pyproject.toml ├── requirements.txt └── src │ ├── commerce.py │ ├── commerce_naive.py │ ├── media_model_1.py │ └── model.py ├── ch_04 ├── README.rst ├── data │ └── users.csv ├── docs │ ├── bottom-left-corner.png │ ├── case_study_4.md │ ├── context_view.png │ ├── context_view.uml │ ├── examples.md │ ├── fig_1.png │ ├── fig_1.uml │ ├── logical_view.png │ ├── logical_view.uml │ ├── processing_view.png │ └── processing_view.uml ├── mypy.ini ├── pyproject.toml ├── requirements.txt ├── src │ ├── all_exceptions.py │ ├── classifier.py │ ├── manufacturing.py │ ├── model.py │ └── odd_example.py └── tests │ ├── test_classifier.py │ └── test_user.py ├── ch_05 ├── README.rst ├── docs │ ├── IMG_3421.png │ ├── case_study_5.md │ ├── examples.md │ ├── fig_1.png │ ├── fig_1.uml │ ├── notes.md │ └── properties.md ├── mypy.ini ├── pyproject.toml ├── requirements.txt ├── sample.zip ├── sample.zip.old ├── src │ ├── archive_tweaker.py │ ├── classifier.py │ ├── colors.py │ ├── model.py │ ├── openapi.yaml │ ├── shapes_1.py │ └── shapes_2.py └── tests │ ├── test_archive_tweaker.py │ ├── test_classifier.py │ ├── test_readers.py │ └── test_user.py ├── ch_06 ├── README.rst ├── docs │ ├── case_study_6.md │ ├── examples.md │ ├── fig_1.png │ ├── fig_1.uml │ ├── fig_2.png │ ├── fig_2.uml │ ├── fig_3.png │ ├── fig_4.png │ └── metaclass.uml ├── mypy.ini ├── pyproject.toml ├── requirements.txt ├── src │ ├── classifier.py │ ├── debugging_help.py │ ├── dice.py │ ├── dict_ext.py │ ├── lookup_mapping.py │ ├── media_model_2.py │ ├── model.py │ └── odd_example.py └── tests │ ├── test_debugging_help.py │ ├── test_dice.py │ ├── test_lookup_mapping.py │ └── test_partition.py ├── ch_07 ├── README.rst ├── docs │ ├── case_study_7.md │ ├── examples.md │ ├── logical_view_1.png │ ├── logical_view_1.uml │ ├── logical_view_2.png │ ├── logical_view_2.uml │ ├── logical_view_3.png │ ├── logical_view_3.uml │ ├── logical_view_4.png │ ├── logical_view_4.uml │ ├── queue.png │ └── queue.uml ├── mypy.ini ├── pyproject.toml ├── requirements.txt ├── src │ ├── classifier.py │ ├── dc_stocks.py │ ├── dictionaries.py │ ├── lists.py │ ├── model.py │ ├── model_f.py │ ├── model_t.py │ └── queue_example.py └── tests │ └── test_model.py ├── ch_08 ├── README.rst ├── big_number.txt ├── docs │ ├── case_study_8.md │ ├── context_view.png │ ├── context_view.uml │ ├── examples.md │ ├── logical_view.png │ ├── logical_view.uml │ ├── process_view_1.png │ ├── process_view_1.uml │ ├── process_view_2.png │ ├── process_view_2.uml │ └── sample_data.md ├── pyproject.toml ├── requirements.txt ├── src │ ├── file_demo.py │ ├── function_demo.py │ ├── model.py │ └── parameters.py └── tests │ └── test_function_demo.py ├── ch_09 ├── README.rst ├── docs │ ├── case_study_9.md │ ├── examples.md │ ├── fig_1.png │ └── fig_1.uml ├── mypy.ini ├── pickled_list ├── pyproject.toml ├── requirements.txt ├── src │ ├── classifier.py │ ├── code_scanner.py │ ├── contacts.py │ ├── distances.py │ ├── model.py │ ├── pattern_matching.py │ └── url_poll.py └── tests │ ├── data_conversion.py │ └── test_readers.py ├── ch_10 ├── README.rst ├── benches │ └── bench_knn.py ├── docs │ ├── Generators.drawio │ ├── Generators.png │ ├── case_study_10.md │ ├── examples.md │ ├── fig_1.png │ ├── fig_1.uml │ ├── fig_2.png │ ├── fig_2.uml │ ├── fig_3.png │ ├── fig_3.uml │ ├── log_line_regex.png │ └── log_line_regex.uml ├── pyproject.toml ├── requirements.txt ├── src │ ├── iterator_protocol.py │ ├── log_analysis.py │ └── model.py └── tests │ ├── log_sample.py │ └── test_model.py ├── ch_11 ├── README.md ├── boat.png ├── bonus │ ├── zonk_patterns.png │ ├── zonk_score.py │ ├── zonk_state.png │ └── zonk_state.uml ├── docs │ ├── command.png │ ├── command.uml │ ├── decorator.png │ ├── decorator.uml │ ├── dice_regex.png │ ├── dice_regex.uml │ ├── examples.md │ ├── langston_ant.png │ ├── langston_ant.uml │ ├── nmea_message.png │ ├── nmea_message.uml │ ├── observer.png │ ├── observer.uml │ ├── singleton.png │ ├── singleton.uml │ ├── state.png │ ├── state.uml │ ├── strategy.png │ ├── strategy.uml │ ├── strategy_design.png │ └── strategy_design.uml ├── pyproject.toml ├── requirements.txt ├── src │ ├── decorations.py │ ├── dice.py │ ├── dice_server.py │ ├── image_filler.py │ ├── inventory.py │ ├── model.py │ ├── nmea_states.py │ ├── nmea_states_2.py │ ├── socket_client.py │ └── socket_server.py └── tests │ ├── test_dice.py │ ├── test_dice_server.py │ ├── test_image_filler.py │ ├── test_socket_client.py │ └── test_socket_server.py ├── ch_12 ├── README.md ├── docs │ ├── abstract_factory-classes-2a.png │ ├── abstract_factory-classes-2b.png │ ├── abstract_factory-classes-2c.png │ ├── abstract_factory-classes.png │ ├── abstract_factory.png │ ├── abstract_factory.uml │ ├── abstract_factory_example_2.uml │ ├── adapter.png │ ├── adapter.uml │ ├── composite.png │ ├── composite.uml │ ├── coposite-objects.png │ ├── facade.png │ ├── facade.uml │ ├── factory-modules.png │ ├── flyweight-circular.png │ ├── flyweight-classes.png │ ├── flyweight.png │ ├── flyweight.uml │ ├── logical_view_1.png │ ├── logical_view_1.uml │ ├── logical_view_2.png │ ├── logical_view_2.uml │ ├── template.png │ └── template.uml ├── gross_sales_20210126.csv ├── pyproject.toml ├── requirements.txt ├── sales.db ├── scrap │ ├── test.html │ └── tree.dot ├── src │ ├── age_computation.py │ ├── car_sales.py │ ├── card_games.py │ ├── file_system.py │ ├── gps_message_slots.py │ ├── gps_messages.py │ ├── images.py │ └── ordered_list.py └── tests │ ├── test_car_sales.py │ ├── test_cards.py │ └── test_images.py ├── ch_13 ├── README.md ├── bonus │ ├── test_vigenere_cipher.py │ └── vigenere_cipher.py ├── checksums.txt ├── data.tar ├── docs │ └── case_study_13.md ├── pyproject.toml ├── requirements.txt ├── src │ ├── checksum_writer.py │ ├── flight_status_redis.py │ ├── log_catcher.py │ ├── model.py │ ├── remote_logging_app.py │ └── stats.py └── tests │ ├── test_38_39.py │ ├── test_averages.py │ ├── test_check_numbers.py │ ├── test_checksum_writer.py │ ├── test_coverage.py │ ├── test_flight_status_tracker.py │ ├── test_logging_app.py │ ├── test_model.py │ ├── test_pythonista.py │ ├── test_setup_teardown.py │ ├── test_skipping.py │ ├── test_stats.py │ ├── test_stats_pytest.py │ └── test_with_pytest.py ├── ch_14 ├── README.md ├── benches │ └── time_to_write.py ├── bonus │ ├── image_compressor.py │ ├── image_compressor_alt.py │ └── test_image_compressor.py ├── docs │ ├── fig_1.png │ ├── fig_1.uml │ ├── fig_2.png │ ├── fig_2.uml │ ├── fig_2h.png │ ├── fig_2h.uml │ ├── fig_3_4.uml │ ├── logical_view_1.png │ ├── logical_view_1.uml │ ├── rle_class.png │ └── rle_state.png ├── images │ ├── README.rst │ ├── bricks.bmp │ ├── bricks.rle │ ├── bricks_1.bmp │ ├── bricks_1.png │ ├── bricks_1.rle │ ├── bricks_1.uml │ ├── bricks_2.bmp │ ├── bricks_2.png │ ├── bricks_2.rle │ ├── bricks_2.uml │ ├── compiling.bmp │ ├── compiling.rle │ ├── exploits_of_a_mom.bmp │ ├── exploits_of_a_mom.rle │ ├── large.bmp │ ├── large.rle │ ├── make_images.py │ ├── python.bmp │ ├── python.rle │ ├── row.bmp │ ├── row.rle │ ├── sandwich.bmp │ └── sandwich.rle ├── pyproject.toml ├── requirements.txt ├── src │ ├── async_1.py │ ├── code_search.py │ ├── correlation.py │ ├── delicatessen.py │ ├── demo_log_catcher.py │ ├── demo_signals.py │ ├── directory_search.py │ ├── food_truck.py │ ├── fw_model.py │ ├── log_catcher.py │ ├── model.py │ ├── philosophers.py │ ├── prime_factor.py │ ├── processes_1.py │ ├── remote_logging_app.py │ ├── threads_1.py │ ├── weather_async.py │ └── weather_threads.py └── tests │ ├── TestImage.ipynb │ ├── test_aync_1.py │ ├── test_code_search.py │ ├── test_directory_search.py │ ├── test_fw_model.py │ ├── test_log_catcher.py │ ├── test_model.py │ ├── test_philosophers.py │ ├── test_weather_async.py │ └── test_weather_threads.py ├── env.sh ├── environment.yml ├── iris.names ├── make.bat ├── python3.8.bat ├── python3.bat └── tools ├── check_requirements.py ├── defence.py └── tree.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Packt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # A few useful build options 2 | # Use `make test TOX_OPTIONS='-r'` to refresh all chapter virtual environments 3 | 4 | TOX_OPTIONS= 5 | 6 | test : 7 | cd ch_01 && tox $(TOX_OPTIONS) 8 | cd ch_02 && tox $(TOX_OPTIONS) 9 | cd ch_03 && tox $(TOX_OPTIONS) 10 | cd ch_04 && tox $(TOX_OPTIONS) 11 | cd ch_05 && tox $(TOX_OPTIONS) 12 | cd ch_06 && tox $(TOX_OPTIONS) 13 | cd ch_07 && tox $(TOX_OPTIONS) 14 | cd ch_08 && tox $(TOX_OPTIONS) 15 | cd ch_09 && tox $(TOX_OPTIONS) 16 | cd ch_10 && tox $(TOX_OPTIONS) && tox -e bench 17 | cd ch_11 && tox $(TOX_OPTIONS) 18 | cd ch_12 && tox $(TOX_OPTIONS) 19 | cd ch_13 && tox $(TOX_OPTIONS) && tox -e coverage 20 | cd ch_14 && tox $(TOX_OPTIONS) 21 | 22 | %.png : %.uml 23 | plantuml $< 24 | 25 | images : 26 | python ch_12/src/images.py 27 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # TODO 2 | 3 | 1. Redo all type hints. 4 | -------------------------------------------------------------------------------- /ch_01/README.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming, 4th ed. 2 | 3 | Chapter 1, Object-Oriented Design. 4 | 5 | Creating the 4+1 views of the problem domain. 6 | -------------------------------------------------------------------------------- /ch_01/docs/Concept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/Concept.png -------------------------------------------------------------------------------- /ch_01/docs/context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/context.png -------------------------------------------------------------------------------- /ch_01/docs/context.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'context' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | 8 | actor Botanist as b 9 | actor User as u 10 | package Classifier { 11 | usecase "Provide Training Data" as UC1 12 | usecase "Set Parameters and Test Classifier" as UC2 13 | usecase "Make Classification Request" as UC3 14 | } 15 | b --> UC1 16 | b --> UC2 17 | u --> UC3 18 | @enduml 19 | -------------------------------------------------------------------------------- /ch_01/docs/development_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/development_view_1.png -------------------------------------------------------------------------------- /ch_01/docs/development_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'development_view_1' 3 | 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | 8 | package "Classifier" { 9 | [View Functions] 10 | [Data Model] 11 | [Tests] 12 | } 13 | 14 | [View Functions] ..> [flask] 15 | [Tests] ..> [pytest] 16 | [Tests] ..> [tox] 17 | [View Functions] ..> [Data Model] 18 | [Tests] ..> [Data Model] 19 | [Tests] ..> [View Functions] 20 | @enduml 21 | -------------------------------------------------------------------------------- /ch_01/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_1.png -------------------------------------------------------------------------------- /ch_01/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_1' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | skinparam shadowing false 8 | hide members 9 | 10 | scale 2 11 | 12 | class Apple {} 13 | class Barrel {} 14 | Apple -- Barrel 15 | 16 | class Orange {} 17 | class Basket {} 18 | Orange -- Basket 19 | 20 | @enduml 21 | -------------------------------------------------------------------------------- /ch_01/docs/fig_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_10.png -------------------------------------------------------------------------------- /ch_01/docs/fig_10.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_9' 3 | 'left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | hide abstract circle 8 | skinparam shadowing false 9 | skinparam classAttributeIconSize 0 10 | 11 | class Piece { 12 | + chess_set: Chess Set 13 | + color: Color 14 | } 15 | 16 | together { 17 | class Rook { 18 | + shape 19 | + move(board: Board) 20 | } 21 | 22 | class Bishop { 23 | + shape 24 | + move(board: Board) 25 | } 26 | 27 | class King { 28 | + shape 29 | + move(board: Board) 30 | } 31 | } 32 | 33 | class Knight { 34 | + shape 35 | + move(board: Board) 36 | } 37 | 38 | class Pawn { 39 | + shape 40 | + move(board: Board) 41 | } 42 | 43 | class Queen { 44 | + shape 45 | + move(board: Board) 46 | } 47 | 48 | 49 | Piece <|-d- Rook 50 | Piece <|-d- Bishop 51 | Piece <|-d- King 52 | Piece <|-- Knight 53 | Piece <|-- Pawn 54 | Piece <|-- Queen 55 | 56 | Rook -[hidden]-> Knight 57 | Bishop -[hidden]-> Pawn 58 | King -[hidden]-> Queen 59 | 60 | @enduml 61 | -------------------------------------------------------------------------------- /ch_01/docs/fig_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_11.png -------------------------------------------------------------------------------- /ch_01/docs/fig_11_alt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_11_alt.png -------------------------------------------------------------------------------- /ch_01/docs/fig_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_2.png -------------------------------------------------------------------------------- /ch_01/docs/fig_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_2' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | skinparam shadowing false 8 | hide members 9 | 10 | scale 2 11 | 12 | class Apple {} 13 | class Barrel {} 14 | Apple "*" --- "1" Barrel : go in > 15 | 16 | class Orange {} 17 | class Basket {} 18 | Orange "*" --- "1" Basket : go in > 19 | 20 | 21 | @enduml 22 | -------------------------------------------------------------------------------- /ch_01/docs/fig_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_3.png -------------------------------------------------------------------------------- /ch_01/docs/fig_3.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_3' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | skinparam shadowing false 8 | skinparam classAttributeIconSize 0 9 | hide methods 10 | 11 | class Apple { 12 | + color 13 | + weight 14 | } 15 | class Barrel { 16 | + size 17 | } 18 | Apple "*" --- "1" Barrel : go in > 19 | 20 | class Orange { 21 | + weight 22 | + orchard 23 | + date_picked 24 | } 25 | class Basket { 26 | + location 27 | } 28 | Orange "*" --- "1" Basket : go in > 29 | 30 | 31 | @enduml 32 | -------------------------------------------------------------------------------- /ch_01/docs/fig_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_4.png -------------------------------------------------------------------------------- /ch_01/docs/fig_4.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_4' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | skinparam shadowing false 8 | skinparam classAttributeIconSize 0 9 | hide methods 10 | 11 | class Apple { 12 | + color: str 13 | + weight: float 14 | + barrel: Barrel 15 | } 16 | class Barrel { 17 | + size: int 18 | + apples: List[Apple] 19 | } 20 | Apple "*" --- "1" Barrel : go in > 21 | 22 | class Orange { 23 | + weight: float 24 | + orchard: str 25 | + date_picked: date 26 | + basket: Basket 27 | } 28 | class Basket { 29 | + location: str 30 | + oranges: List[Orange} 31 | } 32 | Orange "*" --- "1" Basket : go in > 33 | 34 | 35 | @enduml 36 | -------------------------------------------------------------------------------- /ch_01/docs/fig_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_5.png -------------------------------------------------------------------------------- /ch_01/docs/fig_5.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_5' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | skinparam shadowing false 8 | skinparam classAttributeIconSize 0 9 | 10 | class Orange { 11 | + weight: float 12 | + orchard: str 13 | + date_picked: date 14 | + basket: Basket 15 | + pick(basket: Basket) : None 16 | + squeeze() : float 17 | } 18 | class Basket { 19 | + location: str 20 | + oranges: List[Orange} 21 | + sell(customer: Customer) : None 22 | + discard() : None 23 | } 24 | Orange "*" --- "1" Basket : go in > 25 | 26 | 27 | @enduml 28 | -------------------------------------------------------------------------------- /ch_01/docs/fig_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_6.png -------------------------------------------------------------------------------- /ch_01/docs/fig_6.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_6' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | hide abstract circle 8 | skinparam shadowing false 9 | skinparam classAttributeIconSize 0 10 | 11 | class Mechanic {} 12 | 13 | class Car { 14 | + brakes: DiscBrakes 15 | + gas_pedal 16 | + engine: FuelInjected 17 | + transmission: FiveSpeed 18 | + adjust_brake() 19 | + change_oil() 20 | } 21 | 22 | Mechanic --- Car : maintains > 23 | 24 | class Driver {} 25 | 26 | abstract class AbstractCar as "Car" { 27 | + brakes 28 | + gas_pedal 29 | + steer() 30 | + change_gears() 31 | + apply_brake() 32 | } 33 | 34 | Driver --- AbstractCar : drives > 35 | 36 | @enduml 37 | -------------------------------------------------------------------------------- /ch_01/docs/fig_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_7.png -------------------------------------------------------------------------------- /ch_01/docs/fig_7.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_7' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | hide abstract circle 8 | skinparam shadowing false 9 | skinparam classAttributeIconSize 0 10 | 11 | allowmixing 12 | 13 | actor :Player 1: as p1 14 | object "Chess Set" as cs 15 | actor :Player 2: as p2 16 | 17 | p1 --> cs : make_move 18 | cs <-- p2 : make_move 19 | @enduml 20 | -------------------------------------------------------------------------------- /ch_01/docs/fig_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_8.png -------------------------------------------------------------------------------- /ch_01/docs/fig_8.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_8' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | hide abstract circle 8 | skinparam shadowing false 9 | skinparam classAttributeIconSize 0 10 | 11 | object "Player" as p 12 | object "Chess Set" as cg 13 | 14 | p "2" --> "1" cg : make move > 15 | @enduml 16 | -------------------------------------------------------------------------------- /ch_01/docs/fig_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/fig_9.png -------------------------------------------------------------------------------- /ch_01/docs/fig_9.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'fig_9' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | hide abstract circle 8 | skinparam shadowing false 9 | skinparam classAttributeIconSize 0 10 | 11 | class Player {} 12 | 13 | class Position { 14 | + chess_board: Board 15 | } 16 | 17 | class "Chess Set" as chessset { 18 | + pieces: List[Piece] 19 | + board: Board 20 | } 21 | 22 | class Piece { 23 | + chess_set: Chess Set 24 | } 25 | 26 | class Board { 27 | + chess_set: Chess Set 28 | + positions: Position 29 | } 30 | 31 | Player "2" -- "1" chessset : make move > 32 | Position "64" --* "1" Board 33 | chessset "1" o-- "1" Board 34 | Piece "32" --o chessset 35 | @enduml 36 | -------------------------------------------------------------------------------- /ch_01/docs/logical_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/logical_view_1.png -------------------------------------------------------------------------------- /ch_01/docs/logical_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'logical_view_1' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | hide class circle 7 | skinparam shadowing false 8 | 9 | class TrainingData { 10 | name: str 11 | uploaded: datetime 12 | tested: datetime 13 | k: int 14 | } 15 | class "List[Sample]" 16 | class Sample { 17 | sepal_length: float 18 | sepal_width: float 19 | petal_length: float 20 | petal_width: float 21 | } 22 | TrainingData *--> "List[Sample]" : training > 23 | TrainingData *--> "List[Sample]" : testing > 24 | "List[Sample]" o--> Sample 25 | @enduml 26 | -------------------------------------------------------------------------------- /ch_01/docs/logical_view_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/logical_view_2.png -------------------------------------------------------------------------------- /ch_01/docs/logical_view_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'logical_view_2' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | hide class circle 8 | 9 | class TrainingData { 10 | name: str 11 | uploaded: datetime 12 | tested: datetime 13 | k: int 14 | training: List[Sample] 15 | testing: List[Sample] 16 | } 17 | class Sample { 18 | sepal_length: float 19 | sepal_width: float 20 | petal_length: float 21 | petal_width: float 22 | } 23 | TrainingData *--> Sample : training > 24 | TrainingData *--> Sample : testing > 25 | @enduml 26 | -------------------------------------------------------------------------------- /ch_01/docs/logical_view_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/logical_view_3.png -------------------------------------------------------------------------------- /ch_01/docs/logical_view_3.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'logical_view_3' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class TrainingData { 9 | name: str 10 | uploaded: datetime 11 | tested: datetime 12 | } 13 | class Sample { 14 | sepal_length: float 15 | sepal_width: float 16 | petal_length: float 17 | petal_width: float 18 | } 19 | class KnownSample { 20 | species: str 21 | } 22 | class Hyperparameter { 23 | k: int 24 | quality: float 25 | } 26 | class "List[KnownSample]" 27 | class "List[Hyperparameter]" 28 | TrainingData *---> "List[KnownSample]" : training > 29 | TrainingData *---> "List[KnownSample]" : testing > 30 | TrainingData *---> "List[Hyperparameter]" : tuning > 31 | "List[KnownSample]" o--> KnownSample 32 | "List[Hyperparameter]" *--> Hyperparameter 33 | Sample <|-- KnownSample 34 | Hyperparameter ...> TrainingData : data > 35 | @enduml 36 | -------------------------------------------------------------------------------- /ch_01/docs/logical_view_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/logical_view_4.png -------------------------------------------------------------------------------- /ch_01/docs/logical_view_4.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'logical_view_4' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class Hyperparameter { 9 | k: int 10 | quality: float 11 | classify(sample: UnkownSample): str 12 | matchesI(sample: KnownSample, species: str): bool 13 | } 14 | 15 | @enduml 16 | -------------------------------------------------------------------------------- /ch_01/docs/physical_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/physical_view_1.png -------------------------------------------------------------------------------- /ch_01/docs/physical_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'physical_view_1' 3 | 4 | left to right direction 5 | skinparam monochrome true 6 | skinparam handwritten false 7 | skinparam shadowing false 8 | 9 | node client { 10 | [client app] 11 | } 12 | 13 | node server { 14 | [GUnicorn] 15 | [Classifier] 16 | } 17 | 18 | [GUnicorn] ..> [Classifier] 19 | HTTPS - [GUnicorn] 20 | [client app] --> HTTPS 21 | 22 | @enduml 23 | -------------------------------------------------------------------------------- /ch_01/docs/process_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_01/docs/process_view_1.png -------------------------------------------------------------------------------- /ch_01/docs/process_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'processa_view_1' 3 | 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | 8 | start 9 | 10 | :Validate name and 11 | Sample instances; 12 | 13 | if (valid) then (yes) 14 | :Partition Samples; 15 | :Save new TrainingData; 16 | stop 17 | else (no) 18 | :Respond with an error; 19 | stop 20 | endif 21 | 22 | @enduml 23 | -------------------------------------------------------------------------------- /ch_01/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 1" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | license = {file = "LICENSE.txt"} 8 | keywords = ["k-NN", "object-oriented design"] 9 | authors = [ 10 | {email = "slott56@gmail.com"}, 11 | {name = "Steven F. Lott"} 12 | ] 13 | 14 | [tool.tox] 15 | legacy_tox_ini = """ 16 | [tox] 17 | description = "No real tests in this chapter." 18 | minversion = 3.20.0 19 | skipsdist = True 20 | 21 | [testenv] 22 | deps = 23 | pytest==6.2.4 24 | mypy==0.812 25 | 26 | """ 27 | -------------------------------------------------------------------------------- /ch_02/README.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming, 4th ed. 2 | 3 | Chapter 2. Objects in Python. 4 | 5 | Core data model of samples and training data. 6 | -------------------------------------------------------------------------------- /ch_02/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_02/__init__.py -------------------------------------------------------------------------------- /ch_02/docs/case_study_2.md: -------------------------------------------------------------------------------- 1 | 2 | # Case Study for Chapter 2, Objects in Python 3 | 4 | ## Logical View 5 | 6 | ## Samples and Their States 7 | 8 | ## Sample State Transitions 9 | 10 | ```python 11 | >>> from model import Sample 12 | >>> s2 = Sample( 13 | ... sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species="Iris-setosa") 14 | >>> s2 15 | KnownSample(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species='Iris-setosa') 16 | >>> s2.classification = "wrong" 17 | >>> s2 18 | KnownSample(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species='Iris-setosa', classification='wrong') 19 | 20 | ``` 21 | 22 | ## The Hyperparameter Class 23 | 24 | ```python 25 | 26 | >>> class TrainingData: 27 | ... pass 28 | >>> td_1 = TrainingData() 29 | 30 | ``` 31 | 32 | ```python 33 | 34 | >>> from weakref import ref 35 | >>> b = ref(td_1) 36 | 37 | ``` 38 | 39 | ```python 40 | 41 | >>> type(b) 42 | 43 | 44 | ``` 45 | 46 | ```python 47 | 48 | >>> b() == td_1 49 | True 50 | 51 | ``` 52 | 53 | ```python 54 | 55 | >>> del td_1 56 | >>> b() is None 57 | True 58 | 59 | ``` 60 | 61 | ## Responsibilities 62 | 63 | ## The Training Data Class 64 | 65 | -------------------------------------------------------------------------------- /ch_02/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_02/docs/fig_1.png -------------------------------------------------------------------------------- /ch_02/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1a: Variables' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | frame step1 { 9 | note "a_string_variable" as l1 10 | 11 | card 140214245924432 [ 12 | "Hello, world!" 13 | ---- 14 | id = 140214245924432 15 | type = str 16 | hash = 8275030067265638305 17 | ] 18 | 19 | l1 --> 140214245924432 : "refers to" 20 | } 21 | 22 | frame step2 { 23 | note "a_string_variable" as l2 24 | 25 | card 140214350732528 [ 26 | 42 27 | ---- 28 | id = 140214245924432 29 | type = int 30 | hash = 42 31 | ] 32 | 33 | l2 --> 140214350732528 : "refers to" 34 | 35 | } 36 | 37 | @enduml 38 | -------------------------------------------------------------------------------- /ch_02/docs/fig_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_02/docs/fig_2.png -------------------------------------------------------------------------------- /ch_02/docs/fig_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: Logical View' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class TrainingData { 9 | name: str 10 | uploaded: datetime 11 | tested: datetime 12 | } 13 | class Sample { 14 | sepal_length: float 15 | sepal_width: float 16 | petal_length: float 17 | petal_width: float 18 | } 19 | class KnownSample { 20 | species: str 21 | } 22 | class Hyperparameter { 23 | k: int 24 | quality: float 25 | classify(s: Sample): str 26 | } 27 | class "List[KnownSample]" 28 | class "List[Hyperparameter]" 29 | TrainingData *---> "List[KnownSample]" : training > 30 | TrainingData *---> "List[KnownSample]" : testing > 31 | TrainingData *---> "List[Hyperparameter]" : tuning > 32 | "List[KnownSample]" o--> KnownSample 33 | "List[Hyperparameter]" *--> Hyperparameter 34 | Sample <|-- KnownSample 35 | Hyperparameter ...> TrainingData : data > 36 | @enduml 37 | -------------------------------------------------------------------------------- /ch_02/docs/fig_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_02/docs/fig_3.png -------------------------------------------------------------------------------- /ch_02/docs/fig_3.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 3: Sample State Transitions' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class TrainingData { 9 | name: str 10 | uploaded: datetime.datetime 11 | tested: datetime.datetime 12 | load(source: Iterable) 13 | } 14 | class Sample { 15 | sepal_length: float 16 | sepal_width: float 17 | petal_length: float 18 | petal_width: float 19 | species: str 20 | classification: str 21 | } 22 | class KnownSample { 23 | species is not None 24 | } 25 | class UnknownSample { 26 | species is None 27 | } 28 | class Hyperparameter { 29 | k: int 30 | quality: float 31 | classify(s: Sample): str 32 | test(t: List[TestingKnownSample]) 33 | } 34 | class "List[TestingKnownSample]" 35 | class "List[TrainingKnownSample]" 36 | class "List[Hyperparameter]" 37 | TrainingData *---> "List[TrainingKnownSample]" : training > 38 | TrainingData *---> "List[TestingKnownSample]" : testing > 39 | TrainingData *---> "List[Hyperparameter]" : tuning > 40 | "List[TrainingKnownSample]" o--> TrainingKnownSample 41 | "List[TestingKnownSample]" o--> TestingKnownSample 42 | "List[Hyperparameter]" *--> Hyperparameter 43 | Sample <|-- KnownSample 44 | KnownSample <|-- TestingKnownSample 45 | KnownSample <|-- TrainingKnownSample 46 | Sample <|-- UnknownSample 47 | Hyperparameter ...> TrainingData : data > 48 | @enduml 49 | -------------------------------------------------------------------------------- /ch_02/docs/fig_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_02/docs/fig_4.png -------------------------------------------------------------------------------- /ch_02/docs/fig_4.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 4: The Hyperparameter Class' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class TrainingData 9 | class Hyperparameter 10 | object "TrainingData instance" as td { 11 | id: 140527584537136 12 | params: List[Hyperparameter] 13 | } 14 | object list 15 | object "Hyperparameter instance" as hp_0 { 16 | id: 98765432 17 | training: TrainingData 18 | } 19 | object "Hyperparameter instance" as hp_1 { 20 | id: 87654321 21 | training: TrainingData 22 | } 23 | 24 | td ..> TrainingData 25 | hp_0 ..> Hyperparameter 26 | hp_1 ..> Hyperparameter 27 | td *--> list 28 | list --> hp_0: "[0]" 29 | list --> hp_1: "[1]" 30 | hp_0 --> td 31 | hp_1 --> td 32 | object "variable: td_1" as td_1 33 | td_1 --> td 34 | @enduml 35 | -------------------------------------------------------------------------------- /ch_02/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 2" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.tox] 18 | legacy_tox_ini = """ 19 | [tox] 20 | minversion = 3.20.0 21 | skipsdist = True 22 | [testenv] 23 | description = "Master suite of tests for all environments." 24 | deps = 25 | -rrequirements.txt 26 | pytest==6.2.4 27 | black 28 | mypy==0.812 29 | setenv = 30 | PYTHONPATH = {toxinidir}/src{:}{toxinidir}/src/ecommerce 31 | commands = 32 | black src 33 | python -m doctest --option ELLIPSIS src/point_2.py 34 | python -m doctest --option ELLIPSIS src/formatter.py 35 | python -m doctest --option ELLIPSIS src/point_3.py 36 | python -m doctest --option ELLIPSIS src/model.py 37 | python -m doctest --option ELLIPSIS src/bad_hints.py 38 | python -m doctest --option ELLIPSIS src/first_class.py 39 | python -m doctest --option ELLIPSIS src/point_4.py 40 | python -m doctest --option ELLIPSIS src/main.py 41 | python -m doctest --option ELLIPSIS src/point_1.py 42 | python -m doctest --option ELLIPSIS docs/examples.md 43 | python -m doctest --option ELLIPSIS docs/case_study_2.md 44 | python -m pytest 45 | mypy --strict --show-error-codes src 46 | """ 47 | -------------------------------------------------------------------------------- /ch_02/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | pyyaml==5.3.1 5 | pillow==8.0.1 6 | -------------------------------------------------------------------------------- /ch_02/src/bad_hints.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | 6 | NOTE. Remove the ``# type: ignore`` comments to reproduce examples in the text. 7 | """ 8 | 9 | 10 | def odd(n: int) -> bool: 11 | return n % 2 != 0 12 | 13 | 14 | def main(): # type: ignore 15 | print(odd("Hello, world!")) # type: ignore 16 | 17 | 18 | if __name__ == "__main__": 19 | main() # type: ignore 20 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | from .database import db 7 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/contact/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/contact/email.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | 8 | def send_mail(to: str, subject: str, body: str) -> None: 9 | pass 10 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/database.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | from __future__ import annotations 7 | from typing import Optional, Any 8 | 9 | 10 | class Database: 11 | """The Database Implementation""" 12 | 13 | def __init__(self, connection: Optional[str] = None) -> None: 14 | """Create a connection to a database.""" 15 | self.connection = connection 16 | 17 | def fetch(self, key: str) -> dict[str, Any]: 18 | return {"key": key} 19 | 20 | 21 | db: Optional[Database] = None 22 | 23 | 24 | def initialize_database(connection: Optional[str] = None) -> None: 25 | global db 26 | db = Database(connection) 27 | # print(f"initialized {db!r} with {connection!r}") 28 | 29 | 30 | def get_database(connection: Optional[str] = None) -> Database: 31 | global db 32 | if not db: 33 | db = Database(connection) 34 | # print(f"initialized {db!r} with {connection!r}") 35 | return db 36 | 37 | 38 | class Query: 39 | """A place-holder for a more sophistcated Query object.""" 40 | 41 | def __init__(self, database: Database, collection: str) -> None: 42 | """Create a query for a database""" 43 | pass 44 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/payments/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/payments/common.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/payments/square.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | from ..database import Database 7 | 8 | test_2 = """ 9 | >>> db = Database("path/to/data") 10 | >>> db.fetch("test_2") 11 | {'key': 'test_2'} 12 | """ 13 | 14 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 15 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/payments/stripe.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | from __future__ import annotations 7 | from typing import Any 8 | 9 | from ..database import Database 10 | from ..contact.email import send_mail 11 | 12 | 13 | def payment() -> dict[str, Any]: 14 | db = Database("path/to/data") 15 | return db.fetch("test_2") 16 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/products.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | import ecommerce.database as database 7 | 8 | from .database import Database 9 | 10 | from .database import Database as DB 11 | 12 | from .database import Database, Query 13 | 14 | from .contact.email import send_mail 15 | 16 | 17 | class Product: 18 | def __init__(self, name: str) -> None: 19 | self.name = name 20 | -------------------------------------------------------------------------------- /ch_02/src/ecommerce/vendors.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | import ecommerce.database 8 | 9 | 10 | class Vendor: 11 | def __init__(self, name: str) -> None: 12 | self.name = name 13 | -------------------------------------------------------------------------------- /ch_02/src/first_class.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | 8 | class MyFirstClass: 9 | pass 10 | -------------------------------------------------------------------------------- /ch_02/src/formatter.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | from typing import Optional 8 | 9 | 10 | class Formatter: 11 | def format(self, string: str) -> str: 12 | pass 13 | 14 | 15 | def format_string(string: str, formatter: Optional[Formatter] = None) -> str: 16 | """ 17 | Format a string using the formatter object, which 18 | is expected to have a format() method that accepts 19 | a string. 20 | """ 21 | 22 | class DefaultFormatter(Formatter): 23 | """Format a string in title case.""" 24 | 25 | def format(self, string: str) -> str: 26 | return str(string).title() 27 | 28 | if not formatter: 29 | formatter = DefaultFormatter() 30 | 31 | return formatter.format(string) 32 | 33 | 34 | test_example = """ 35 | >>> hello_string = "hello world, how are you today?" 36 | >>> print(f" input: {hello_string}") 37 | input: hello world, how are you today? 38 | >>> print(f"output: {format_string(hello_string)}") 39 | output: Hello World, How Are You Today? 40 | """ 41 | 42 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 43 | -------------------------------------------------------------------------------- /ch_02/src/main.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | import ecommerce.products 7 | 8 | # Absolute. 9 | 10 | product_1 = ecommerce.products.Product("fore") 11 | 12 | # or 13 | 14 | from ecommerce.products import Product 15 | 16 | product_2 = Product("main") 17 | 18 | # or 19 | 20 | from ecommerce import products 21 | 22 | product_3 = products.Product("mizzen") 23 | 24 | 25 | test_1 = """ 26 | >>> import ecommerce.products 27 | >>> product_1 = ecommerce.products.Product("fore") 28 | >>> product_1.name 29 | 'fore' 30 | """ 31 | 32 | test_2 = """ 33 | >>> from ecommerce.products import Product 34 | >>> product_2 = Product("main") 35 | >>> product_2.name 36 | 'main' 37 | """ 38 | 39 | test_3 = """ 40 | >>> from ecommerce import products 41 | >>> product_3 = products.Product("mizzen") 42 | >>> product_3.name 43 | 'mizzen' 44 | """ 45 | 46 | disabled_test_path = """ 47 | Change the name to test_path for a test that (a) fails 48 | and (b) exposes the `PYTHONPATH` setting in force for the 49 | test. 50 | 51 | >>> import sys 52 | >>> sys.path 53 | [] 54 | 55 | """ 56 | 57 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 58 | -------------------------------------------------------------------------------- /ch_02/src/point_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | 8 | class Point: 9 | def __init__(self, x: float = 0, y: float = 0) -> None: 10 | self.move(x, y) 11 | 12 | def move(self, x: float, y: float) -> None: 13 | self.x = x 14 | self.y = y 15 | 16 | def reset(self) -> None: 17 | self.move(0, 0) 18 | -------------------------------------------------------------------------------- /ch_02/src/point_2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | import math 8 | 9 | 10 | class Point: 11 | def move(self, x: float, y: float) -> None: 12 | self.x = x 13 | self.y = y 14 | 15 | def reset(self) -> None: 16 | self.move(0, 0) 17 | 18 | def calculate_distance(self, other: "Point") -> float: 19 | return math.hypot(self.x - other.x, self.y - other.y) 20 | 21 | 22 | test_point = """ 23 | >>> point1 = Point() 24 | >>> point2 = Point() 25 | 26 | >>> point1.reset() 27 | >>> point2.move(5, 0) 28 | >>> print(point2.calculate_distance(point1)) 29 | 5.0 30 | >>> assert point2.calculate_distance(point1) == point1.calculate_distance( 31 | ... point2 32 | ... ) 33 | >>> point1.move(3, 4) 34 | >>> print(point1.calculate_distance(point2)) 35 | 4.47213595499958 36 | >>> print(point1.calculate_distance(point1)) 37 | 0.0 38 | """ 39 | 40 | 41 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 42 | -------------------------------------------------------------------------------- /ch_02/src/point_3.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | import math 8 | 9 | 10 | class Point: 11 | def __init__(self, x: float = 0, y: float = 0) -> None: 12 | self.move(x, y) 13 | 14 | def move(self, x: float, y: float) -> None: 15 | self.x = x 16 | self.y = y 17 | 18 | def reset(self) -> None: 19 | self.move(0, 0) 20 | 21 | def calculate_distance(self, other: "Point") -> float: 22 | return math.hypot(self.x - other.x, self.y - other.y) 23 | 24 | 25 | test_point = """ 26 | >>> point1 = Point() 27 | >>> point2 = Point() 28 | 29 | >>> point1.reset() 30 | >>> point2.move(5, 0) 31 | >>> print(point2.calculate_distance(point1)) 32 | 5.0 33 | >>> assert point2.calculate_distance(point1) == point1.calculate_distance( 34 | ... point2 35 | ... ) 36 | >>> point1.move(3, 4) 37 | >>> print(point1.calculate_distance(point2)) 38 | 4.47213595499958 39 | >>> print(point1.calculate_distance(point1)) 40 | 0.0 41 | """ 42 | 43 | 44 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 45 | -------------------------------------------------------------------------------- /ch_02/tests/test_ecommerce.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 2, Objects in Python. 5 | """ 6 | 7 | from ecommerce.products import Product 8 | import ecommerce.products 9 | import ecommerce.payments.stripe 10 | 11 | def test_products_db_1(): 12 | db = ecommerce.products.database.Database("path/to/data") 13 | assert db.fetch("test_1") == {'key': 'test_1'} 14 | 15 | def test_products_db_2(): 16 | db = ecommerce.products.Database("path/to/data") 17 | assert db.fetch("test_2") == {'key': 'test_2'} 18 | 19 | def test_products_db_3(): 20 | db = ecommerce.products.DB("path/to/data") 21 | assert db.fetch("test_3") == {'key': 'test_3'} 22 | 23 | def test_products_db_4(): 24 | db = ecommerce.products.DB("path/to/data") 25 | q = ecommerce.products.Query(db, "products") 26 | 27 | def test_products_db_5(): 28 | ecommerce.products.database.initialize_database("production/data") 29 | assert ecommerce.products.database.db.connection == 'production/data' 30 | 31 | def test_products_db_6(): 32 | db = ecommerce.products.database.get_database("production/data") 33 | assert db.connection == 'production/data' 34 | 35 | def test_payments_db(): 36 | assert ecommerce.payments.stripe.payment() == {"key": "test_2"} 37 | -------------------------------------------------------------------------------- /ch_03/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 3. When Objects Are Alike. 8 | 9 | Subclasses and Extension 10 | -------------------------------------------------------------------------------- /ch_03/docs/Chebyshev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Chebyshev.png -------------------------------------------------------------------------------- /ch_03/docs/Distances-Chebyshev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Distances-Chebyshev.png -------------------------------------------------------------------------------- /ch_03/docs/Distances-Euclidean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Distances-Euclidean.png -------------------------------------------------------------------------------- /ch_03/docs/Distances-Manhattan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Distances-Manhattan.png -------------------------------------------------------------------------------- /ch_03/docs/Distances-Sorensen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Distances-Sorensen.png -------------------------------------------------------------------------------- /ch_03/docs/Euclidean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Euclidean.png -------------------------------------------------------------------------------- /ch_03/docs/Manhattan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Manhattan.png -------------------------------------------------------------------------------- /ch_03/docs/Sorensen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/Sorensen.png -------------------------------------------------------------------------------- /ch_03/docs/case_study_3.md: -------------------------------------------------------------------------------- 1 | 2 | # Case Study for Chapter 3, When Objects Are Alike 3 | 4 | ## Logical View 5 | 6 | ## The Strategy Design Pattern 7 | 8 | ```python 9 | 10 | >>> from math import isclose 11 | >>> from model import TrainingKnownSample, UnknownSample, Chebyshev 12 | 13 | >>> s1 = TrainingKnownSample( 14 | ... sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species="Iris-setosa") 15 | >>> u = UnknownSample(**{"sepal_length": 7.9, "sepal_width": 3.2, "petal_length": 4.7, "petal_width": 1.4}) 16 | 17 | >>> algorithm = Chebyshev() 18 | >>> isclose(3.3, algorithm.distance(s1, u)) 19 | True 20 | 21 | ``` 22 | 23 | ## The Minkowski Solution 24 | 25 | ```python 26 | 27 | >>> from math import isclose 28 | >>> from model import TrainingKnownSample, UnknownSample, Euclidean 29 | 30 | >>> s1 = TrainingKnownSample( 31 | ... sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species="Iris-setosa") 32 | >>> u = UnknownSample(**{"sepal_length": 7.9, "sepal_width": 3.2, "petal_length": 4.7, "petal_width": 1.4}) 33 | 34 | >>> algorithm = Euclidean() 35 | >>> isclose(4.50111097, algorithm.distance(s1, u)) 36 | True 37 | 38 | ``` 39 | 40 | ## One More Example 41 | 42 | ## Another Strategy 43 | 44 | ```python 45 | 46 | >>> from model import TrainingKnownSample, UnknownSample, Minkowski_2 47 | 48 | >>> class CD(Minkowski_2): 49 | ... m = 1 50 | ... reduction = max 51 | 52 | >>> class MD(Minkowski_2): 53 | ... m = 1 54 | ... reduction = sum 55 | 56 | >>> class ED(Minkowski_2): 57 | ... m = 2 58 | ... reduction = sum 59 | 60 | ``` 61 | 62 | ## Type Hints for Minkowski_2 63 | 64 | -------------------------------------------------------------------------------- /ch_03/docs/examples_38.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming 4th ed. 2 | 3 | Chapter 3, When Objects Are Alike 4 | 5 | ## Extending built-ins 6 | 7 | 8 | ```python 9 | Examples for Python < 3.9 10 | >>> from typing import List 11 | >>> CL_1 = List["Contact"] 12 | >>> type(CL_1) 13 | 14 | 15 | ``` 16 | -------------------------------------------------------------------------------- /ch_03/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/fig_1.png -------------------------------------------------------------------------------- /ch_03/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: inheritance diagram' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | skinparam classAttributeIconSize 0 8 | 9 | class Object { 10 | + __init__() 11 | } 12 | 13 | class Contact { 14 | + __init__() 15 | } 16 | 17 | class AddressHolder { 18 | + __init__() 19 | } 20 | 21 | class Friend { 22 | + __init__() 23 | } 24 | 25 | Object <|-- Contact 26 | Object <|-- AddressHolder 27 | Contact <|-- Friend 28 | AddressHolder <|-- Friend 29 | 30 | @enduml 31 | -------------------------------------------------------------------------------- /ch_03/docs/fig_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/fig_2.png -------------------------------------------------------------------------------- /ch_03/docs/fig_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: diamond diagram' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | skinparam classAttributeIconSize 0 8 | 9 | class BaseClass { 10 | + call_me() 11 | } 12 | 13 | class LeftSubclass { 14 | + call_me() 15 | } 16 | 17 | class RightSubclass { 18 | + call_me() 19 | } 20 | 21 | class SubClass { 22 | + call_me() 23 | } 24 | 25 | BaseClass <|-- LeftSubclass 26 | BaseClass <|-- RightSubclass 27 | LeftSubclass <|-- SubClass 28 | RightSubclass <|-- SubClass 29 | 30 | @enduml 31 | -------------------------------------------------------------------------------- /ch_03/docs/logical_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_03/docs/logical_view.png -------------------------------------------------------------------------------- /ch_03/docs/logical_view.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 3: Logical View' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | skinparam classAttributeIconSize 0 8 | 9 | class TrainingData { 10 | name: str 11 | uploaded: datetime.datetime 12 | tested: datetime.datetime 13 | load(source: Iterable) 14 | } 15 | class Sample { 16 | sepal_length: float 17 | sepal_width: float 18 | petal_length: float 19 | petal_width: float 20 | } 21 | class KnownSample { 22 | species: str 23 | } 24 | class TrainingKnownSample { 25 | } 26 | class TestingKnownSample { 27 | classification: str 28 | } 29 | class UnknownSample { 30 | classification: str 31 | } 32 | class Hyperparameter { 33 | k: int 34 | quality: float 35 | classify(s: Sample): str 36 | test(t: List[TestingKnownSample]) 37 | } 38 | class "List[TestingKnownSample]" 39 | class "List[TrainingKnownSample]" 40 | class "List[Hyperparameter]" 41 | TrainingData *---> "List[TrainingKnownSample]" : training > 42 | TrainingData *---> "List[TestingKnownSample]" : testing > 43 | TrainingData *---> "List[Hyperparameter]" : tuning > 44 | "List[TrainingKnownSample]" o--> TrainingKnownSample 45 | "List[TestingKnownSample]" o--> TestingKnownSample 46 | "List[Hyperparameter]" *--> Hyperparameter 47 | Sample <|-- KnownSample 48 | KnownSample <|-- TestingKnownSample 49 | KnownSample <|-- TrainingKnownSample 50 | Sample <|-- UnknownSample 51 | Hyperparameter ...> TrainingData : data > 52 | @enduml 53 | -------------------------------------------------------------------------------- /ch_03/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 3" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.tox] 18 | legacy_tox_ini = """ 19 | [tox] 20 | minversion = 3.4.0 21 | skipsdist = True 22 | envlist = {py38, py39} 23 | [base] 24 | deps = 25 | -rrequirements.txt 26 | black 27 | pytest==6.2.4 28 | tox==3.20.0 29 | mypy==0.812 30 | setenv = 31 | PYTHONPATH = {toxinidir}/src 32 | commands = 33 | black src 34 | python -m doctest --option ELLIPSIS src/commerce.py 35 | python -m doctest --option ELLIPSIS src/commerce_naive.py 36 | python -m doctest --option ELLIPSIS src/model.py 37 | python -m doctest --option ELLIPSIS src/media_model_1.py 38 | python -m doctest --option ELLIPSIS docs/case_study_3.md 39 | mypy --strict --show-error-codes src 40 | 41 | [testenv:py38] 42 | deps = 43 | {[base]deps} 44 | setenv = 45 | {[base]setenv} 46 | commands = 47 | python -m doctest --option ELLIPSIS docs/examples_38.md 48 | 49 | [testenv:py39] 50 | deps = 51 | {[base]deps} 52 | setenv = 53 | {[base]setenv} 54 | commands = 55 | {[base]commands} 56 | python -m doctest --option ELLIPSIS docs/examples.md 57 | """ 58 | -------------------------------------------------------------------------------- /ch_03/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | pyyaml==5.3.1 5 | pillow==8.0.1 6 | -------------------------------------------------------------------------------- /ch_04/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 4. Expecting the Unexpected. 8 | 9 | Central authentication and authorization for a web service. 10 | 11 | Running the Demo 12 | ================ 13 | 14 | MacOS X and Linux 15 | :: 16 | 17 | export PYTHONPATH=src 18 | export FLASK_APP=classifier.py 19 | export FLASK_ENV=development 20 | flask run 21 | 22 | Windows 23 | :: 24 | 25 | set PYTHONPATH=src 26 | set FLASK_APP=classifier.py 27 | set FLASK_ENV=development 28 | flask run 29 | -------------------------------------------------------------------------------- /ch_04/data/users.csv: -------------------------------------------------------------------------------- 1 | username,email,real_name,role,password 2 | noriko,noriko@example.com,Noriko K. L.,botanist,pbkdf2:sha256:150000$BN8cEAws$c67e695964f0222d3b136c1796e8d7b48e5d489dfa5f242d797ecd9362e78916 3 | emma,emma@example.com,Emma K.,researcher,pbkdf2:sha256:150000$s9Ev2Lqk$b1d90662e0784cfbe2ef1fbe4407cf2ae346e47845d0ad5c680b6dad889ae8c0 4 | -------------------------------------------------------------------------------- /ch_04/docs/bottom-left-corner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_04/docs/bottom-left-corner.png -------------------------------------------------------------------------------- /ch_04/docs/context_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_04/docs/context_view.png -------------------------------------------------------------------------------- /ch_04/docs/context_view.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: Context View' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | hide class circle 8 | 9 | actor Botanist as b 10 | actor Researcher as u 11 | 12 | package Classifier { 13 | usecase "Provide Training Data" as UC1 14 | usecase "Set Parameters And Test Classifier" as UC2 15 | usecase "Make Classification Request" as UC3 16 | ' usecase "Authenticate" as UC4 17 | } 18 | b --> UC1 19 | b --> UC2 20 | ' b --> UC4 21 | u --> UC3 22 | ' u --> UC4 23 | @enduml 24 | -------------------------------------------------------------------------------- /ch_04/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_04/docs/fig_1.png -------------------------------------------------------------------------------- /ch_04/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Exception Hierarchy' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | class BaseException {} 9 | class SystemExit {} 10 | class KeyboardInterrupt {} 11 | class Exception {} 12 | class "Most Other Exceptions" as others {} 13 | 14 | BaseException <|-- SystemExit 15 | BaseException <|-- KeyboardInterrupt 16 | BaseException <|-- Exception 17 | Exception <|-- others 18 | @enduml 19 | -------------------------------------------------------------------------------- /ch_04/docs/logical_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_04/docs/logical_view.png -------------------------------------------------------------------------------- /ch_04/docs/logical_view.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 3: Logical View' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | class Flask {} 9 | class upload <> {} 10 | hide upload attributes 11 | class test <> {} 12 | hide test attributes 13 | class classify <> {} 14 | hide classify attributes 15 | Flask *--> upload 16 | Flask *--> test 17 | Flask *--> classify 18 | 19 | class authenticate <> {} 20 | hide authenticate attributes 21 | upload ..> authenticate 22 | test ..> authenticate 23 | classify ..> authenticate 24 | 25 | class Users { 26 | get_user(name: str) : User 27 | } 28 | Flask *--> Users 29 | authenticate --> Users 30 | 31 | class User { 32 | name: str 33 | email: str 34 | } 35 | Users *--> User 36 | 37 | class Role <> 38 | User --> Role 39 | @enduml -------------------------------------------------------------------------------- /ch_04/docs/processing_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_04/docs/processing_view.png -------------------------------------------------------------------------------- /ch_04/docs/processing_view.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 3: Processing View' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | class Sample { 9 | sepal_length: float 10 | sepal_width: float 11 | petal_length: float 12 | petal_width: float 13 | } 14 | class KnownSample { 15 | species: str 16 | } 17 | 18 | class UnknownSample { 19 | classification: str 20 | } 21 | Sample <|-- KnownSample 22 | KnownSample <|-- TestingKnownSample 23 | KnownSample <|-- TrainingKnownSample 24 | Sample <|-- UnknownSample 25 | 26 | class TrainingData { 27 | load_samples(path) 28 | } 29 | 30 | class ClassifierApp { 31 | classify(sample) 32 | } 33 | 34 | ClassifierApp -- TrainingData : "uses" 35 | 36 | TrainingData::load_samples --> KnownSample : "creates" 37 | 38 | ClassifierApp::classify --> UnknownSample : "creates" 39 | @enduml 40 | -------------------------------------------------------------------------------- /ch_04/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | implicit_reexport = True 3 | show_error_codes = True 4 | python_version = 3.9 5 | strict = True 6 | -------------------------------------------------------------------------------- /ch_04/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 4" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.pytest.ini_options] 18 | filterwarnings = [ 19 | 'ignore:.*itsdangerous.json.* is deprecated .*:DeprecationWarning', 20 | ] 21 | 22 | [tool.tox] 23 | legacy_tox_ini = """ 24 | [tox] 25 | minversion = 3.4.0 26 | skipsdist = True 27 | [testenv] 28 | deps = 29 | -rrequirements.txt 30 | black 31 | pytest==6.2.4 32 | tox==3.20.0 33 | mypy==0.812 34 | setenv = 35 | PYTHONPATH = {toxinidir}/src 36 | commands = 37 | black src 38 | python -m doctest --option ELLIPSIS src/classifier.py 39 | python -m doctest --option ELLIPSIS src/model.py 40 | python -m doctest --option ELLIPSIS src/manufacturing.py 41 | python -m doctest --option ELLIPSIS src/odd_example.py 42 | python -m doctest --option ELLIPSIS src/all_exceptions.py 43 | python -m doctest --option ELLIPSIS docs/examples.md 44 | python -m doctest --option ELLIPSIS docs/case_study_4.md 45 | python -m pytest 46 | mypy src 47 | """ 48 | -------------------------------------------------------------------------------- /ch_04/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | Flask==2.0 4 | jsonschema==3.2.0 5 | pyyaml==5.3.1 6 | pillow==8.0.1 7 | -------------------------------------------------------------------------------- /ch_04/src/all_exceptions.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 4, Expecting the Unexpected 5 | """ 6 | 7 | 8 | def exception_demo() -> None: 9 | 10 | some_exceptions = [ValueError, TypeError, IndexError, None] 11 | 12 | for choice in some_exceptions: 13 | try: 14 | print(f"\nRaising {choice}") 15 | if choice: 16 | raise choice("An error") 17 | else: 18 | print("no exception raised") 19 | except ValueError: 20 | print("Caught a ValueError") 21 | except TypeError: 22 | print("Caught a TypeError") 23 | except Exception as e: 24 | print(f"Caught some other error: {e.__class__.__name__}") 25 | else: 26 | print("This code called if there is no exception") 27 | finally: 28 | print("This cleanup code is always called") 29 | 30 | 31 | test_exception_demo = """ 32 | >>> exception_demo() 33 | 34 | Raising 35 | Caught a ValueError 36 | This cleanup code is always called 37 | 38 | Raising 39 | Caught a TypeError 40 | This cleanup code is always called 41 | 42 | Raising 43 | Caught some other error: IndexError 44 | This cleanup code is always called 45 | 46 | Raising None 47 | no exception raised 48 | This code called if there is no exception 49 | This cleanup code is always called 50 | 51 | """ 52 | 53 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 54 | 55 | 56 | if __name__ == "__main__": 57 | exception_demo() 58 | -------------------------------------------------------------------------------- /ch_05/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 5. When to Use Object-Oriented Programming. 8 | 9 | Objects, properties, managers, collections. 10 | -------------------------------------------------------------------------------- /ch_05/docs/IMG_3421.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_05/docs/IMG_3421.png -------------------------------------------------------------------------------- /ch_05/docs/case_study_5.md: -------------------------------------------------------------------------------- 1 | 2 | # Case Study for Chapter 5, When to Use Object-Oriented Programming 3 | 4 | ## Input Validation 5 | 6 | ## Input Partitioning 7 | 8 | ## The Sample Class Hierarchy 9 | 10 | ## The Purpose Enumeration 11 | 12 | ```python 13 | >>> from model import KnownSample, Purpose 14 | >>> s2 = KnownSample( 15 | ... sepal_length=5.1, 16 | ... sepal_width=3.5, 17 | ... petal_length=1.4, 18 | ... petal_width=0.2, 19 | ... species="Iris-setosa", 20 | ... purpose=Purpose.Testing.value) 21 | >>> s2 22 | KnownSample(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, purpose=1, species='Iris-setosa') 23 | >>> s2.classification is None 24 | True 25 | 26 | ``` 27 | 28 | ## Property Setters 29 | 30 | ## Repeated If Statements 31 | 32 | ## Context Managers 33 | 34 | -------------------------------------------------------------------------------- /ch_05/docs/examples.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming Case Study 2 | 3 | Chapter 5, When to Use Object-Oriented Programming 4 | 5 | ## Treat objects as objects 6 | 7 | ```python 8 | 9 | >>> square = [(1,1), (1,2), (2,2), (2,1)] 10 | 11 | >>> from math import hypot 12 | >>> def distance(p_1, p_2): 13 | ... return hypot(p_1[0]-p_2[0], p_1[1]-p_2[1]) 14 | >>> def perimeter(polygon): 15 | ... pairs = zip(polygon, polygon[1:]+polygon[:1]) 16 | ... return sum( 17 | ... distance(p1, p2) for p1, p2 in pairs 18 | ... ) 19 | 20 | >>> perimeter(square) 21 | 4.0 22 | 23 | ``` 24 | 25 | ## Adding behaviors to class data with properties 26 | 27 | ## Manager objects 28 | -------------------------------------------------------------------------------- /ch_05/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_05/docs/fig_1.png -------------------------------------------------------------------------------- /ch_05/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: The Sample Class Hierarchy' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | class Sample { 9 | sepal_length: float 10 | sepal_width: float 11 | petal_length: float 12 | petal_width: float 13 | } 14 | 15 | class KnownSample { 16 | + sample_id: int 17 | + species: str 18 | - purpose: Purpose 19 | + classification: Classification 20 | classify(param: Hyperparameter) -> str 21 | } 22 | Sample <|-- KnownSample 23 | 24 | class UnknownSample { 25 | + classification: Classification 26 | classify(param: Hyperparameter) -> str 27 | } 28 | Sample <|-- UnknownSample 29 | 30 | class Classification{ 31 | species: str 32 | parameter: Hyperparameter 33 | } 34 | UnknownSample *--> Classification 35 | KnownSample *--> Classification 36 | @enduml 37 | -------------------------------------------------------------------------------- /ch_05/docs/notes.md: -------------------------------------------------------------------------------- 1 | Python class definitions leave every attribute and method as public. It's simplest 2 | to leave everything as public, and eschew the obfuscation that comes from moore 3 | complex rules. We often say "We're all adults here." The source code is readily 4 | available, and having a "private" attribute or method is so easily circumvented that 5 | we'd rather not invest the effort in it. 6 | 7 | There are four categories of attribute names. 8 | 9 | - `names`. These are the ordinary, publicly-visible names. 10 | 11 | - `_names`. These names are sometimes elided from lists of attributes. Some commands 12 | and functions will quietly skip past these names, leaving them concealed, but still usable. 13 | 14 | - `__names`. Two leading underscores (and zero or one trailing underscore) leads to "name mangling." 15 | The name is adjusted to be sure that it is unique to the class. 16 | (The class name becomes a prefix: `_{class}__names`.) 17 | 18 | - `__names__`. Two leading and two training underscores are Python special names. 19 | No application should ever create new names of this form. 20 | The Python run-time may use any name of this form, and could, at some 21 | point in the future, break your code. 22 | 23 | For the most part, public `names` and concealed `_names` form the bulk of our naming. 24 | Mangled names may be helpful for creating an attribute that cannot be used by a subclass; 25 | this doesn't seem useful. 26 | Python's special names, however, are very widely used, 27 | but defined by the language, and not by our code. 28 | -------------------------------------------------------------------------------- /ch_05/docs/properties.md: -------------------------------------------------------------------------------- 1 | 2 | ## Attributes and Properties 3 | 4 | A Python class exposes several different kinds of attributes. 5 | In other parts of this case study, we've used the following: 6 | 7 | - Instance variables. 8 | These are created in the methods of a class. 9 | They're qualified by the `self.` variable. We often create them in the `__init__()` method. 10 | 11 | - Class variables. 12 | These are variables created within the `class` statement, 13 | but outside any method definition. They belong to the class as a whole, and are shared by 14 | all instances. 15 | 16 | - Instance methods. 17 | Methods defined in the `class` statement without any decoration 18 | apply to the instances of a class. 19 | These methods receive the instance object bound to the `self` variable; the first positional parameter value. 20 | 21 | = Static and class methods. 22 | Methods decorated with `@classmethod` or `@staticmethod apply to the class 23 | as a whole. These methods belong to the class and don't apply to an instance. 24 | A `@staticmethod` has no special parameter. 25 | A `@classmethod` has the class object as the first positional parameter. 26 | 27 | It turns out, there are a more attribute-like features inside a class. The internal mechanism 28 | these are based on, the descriptor, 29 | lets us bind an instance method to a simple name. A method with only a single `self` variable 30 | as a parameter can be evaluated without the syntax of `()` after the name. 31 | 32 | We can use the expression `sample.classifier` to lead to evaluaion of a `classifier()` method 33 | on an object named `sample`. 34 | We can also make sure that `sample.classifier = x` will lead to evaluation of a `classifier(x)` 35 | method, allowing us to validated (or prevent) the assignment. 36 | -------------------------------------------------------------------------------- /ch_05/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | implicit_reexport = True 3 | show_error_codes = True 4 | python_version = 3.9 5 | strict = True 6 | -------------------------------------------------------------------------------- /ch_05/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 5" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.tox] 18 | legacy_tox_ini = """ 19 | [tox] 20 | minversion = 3.4.0 21 | skipsdist = True 22 | [testenv] 23 | deps = 24 | -rrequirements.txt 25 | black 26 | pytest==6.2.4 27 | tox==3.20.0 28 | mypy==0.812 29 | setenv = 30 | PYTHONPATH = {toxinidir}/src 31 | commands = 32 | black src 33 | python -m doctest --option ELLIPSIS src/classifier.py 34 | python -m doctest --option ELLIPSIS src/shapes_1.py 35 | python -m doctest --option ELLIPSIS src/shapes_2.py 36 | python -m doctest --option ELLIPSIS src/model.py 37 | python -m doctest --option ELLIPSIS src/archive_tweaker.py 38 | python -m doctest --option ELLIPSIS src/colors.py 39 | python -m doctest --option ELLIPSIS docs/examples.md 40 | python -m doctest --option ELLIPSIS docs/case_study_5.md 41 | python -m doctest --option ELLIPSIS docs/properties.md 42 | python -m doctest --option ELLIPSIS docs/notes.md 43 | python -m pytest -vv 44 | mypy src 45 | 46 | """ 47 | -------------------------------------------------------------------------------- /ch_05/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | Flask==2.0 4 | jsonschema==3.2.0 5 | pyyaml==5.3.1 6 | pillow==8.0.1 7 | -------------------------------------------------------------------------------- /ch_05/sample.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_05/sample.zip -------------------------------------------------------------------------------- /ch_05/sample.zip.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_05/sample.zip.old -------------------------------------------------------------------------------- /ch_05/src/shapes_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 5, When to Use Object-Oriented Programming 5 | """ 6 | from __future__ import annotations 7 | from math import hypot 8 | from typing import Tuple, List 9 | 10 | Point = Tuple[float, float] 11 | 12 | 13 | def distance(p_1: Point, p_2: Point) -> float: 14 | return hypot(p_1[0] - p_2[0], p_1[1] - p_2[1]) 15 | 16 | 17 | Polygon = List[Point] 18 | 19 | 20 | def perimeter(polygon: Polygon) -> float: 21 | pairs = zip(polygon, polygon[1:] + polygon[:1]) 22 | return sum(distance(p1, p2) for p1, p2 in pairs) 23 | 24 | 25 | test_functions = """ 26 | >>> square = [(1,1), (1,2), (2,2), (2,1)] 27 | >>> perimeter(square) 28 | 4.0 29 | 30 | """ 31 | 32 | 33 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 34 | -------------------------------------------------------------------------------- /ch_05/tests/test_readers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 5, When to Use Object-Oriented Programming 5 | """ 6 | from pytest import * 7 | import csv 8 | from model import SampleReader, BadSampleRow, Sample 9 | 10 | @fixture 11 | def good_sample_csv_file(tmp_path): 12 | test_data = tmp_path/"iris.data" 13 | with test_data.open('w', newline="") as tmpfile: 14 | tmpfile.write("5.0,3.3,1.4,0.2,Iris-setosa\r\n") 15 | tmpfile.write("7.0,3.2,4.7,1.4,Iris-versicolor\r\n") 16 | return test_data 17 | 18 | @fixture 19 | def bad_sample_csv_file(tmp_path): 20 | test_data = tmp_path/"iris.data" 21 | with test_data.open('w', newline="") as tmpfile: 22 | tmpfile.write("5.0,3.3,1.4,0.2,Iris-setosa\r\n") 23 | tmpfile.write("7.0,Nope,4.7,1.4,Iris-versicolor\r\n") 24 | tmpfile.write("7.0,3.2,4.7,1.4,Wrong\r\n") 25 | return test_data 26 | 27 | def test_good_simple_reader(good_sample_csv_file): 28 | rdr = SampleReader(good_sample_csv_file) 29 | samples = list(rdr.sample_iter()) 30 | assert samples == [ 31 | Sample(sepal_length=5.0, sepal_width=3.3, petal_length=1.4, petal_width=0.2, ), 32 | Sample(sepal_length=7.0, sepal_width=3.2, petal_length=4.7, petal_width=1.4, ), 33 | ] 34 | 35 | def test_bad_simple_reader(bad_sample_csv_file): 36 | rdr = SampleReader(bad_sample_csv_file) 37 | with raises(BadSampleRow) as ex: 38 | samples = list(rdr.sample_iter()) 39 | assert ex.value.args == ( 40 | "Invalid {'sepal_length': '7.0', 'sepal_width': 'Nope', 'petal_length': " 41 | "'4.7', 'petal_width': '1.4', 'class': 'Iris-versicolor'}", 42 | ) 43 | -------------------------------------------------------------------------------- /ch_06/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 6. Abstract Base Classes (abc’s) and Operator Overloading. 8 | 9 | Advanced topics in inheritance. 10 | -------------------------------------------------------------------------------- /ch_06/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_06/docs/fig_1.png -------------------------------------------------------------------------------- /ch_06/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: abc diagram' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | skinparam classAttributeIconSize 0 8 | 9 | class "abc.ABC" as abc {} 10 | 11 | abstract class BaseClass { 12 | + {abstract} a_method() 13 | } 14 | 15 | class ConcreteClass_1 { 16 | + a_method() 17 | } 18 | 19 | class ConcreteClass_2 { 20 | + a_method() 21 | } 22 | 23 | abc <|-- BaseClass 24 | BaseClass <|-- ConcreteClass_1 25 | BaseClass <|-- ConcreteClass_2 26 | 27 | 28 | @enduml 29 | -------------------------------------------------------------------------------- /ch_06/docs/fig_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_06/docs/fig_2.png -------------------------------------------------------------------------------- /ch_06/docs/fig_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: Napping diagram' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | skinparam classAttributeIconSize 0 8 | 9 | abstract class Container { 10 | + {abstract} __contains__(item) 11 | } 12 | 13 | abstract class Iterable { 14 | + {abstract} __iter__() 15 | } 16 | 17 | abstract class Sized { 18 | + {abstract} __len__(item) 19 | } 20 | 21 | abstract class Collection { 22 | } 23 | 24 | Sized <|-- Collection 25 | Iterable <|-- Collection 26 | Container <|-- Collection 27 | 28 | abstract class Mapping { 29 | + {abstract} __getitem__(key) 30 | + {abstract} keys() 31 | + {abstract} items() 32 | + {abstract} values() 33 | + {abstract} get(key, default) 34 | } 35 | 36 | Collection <|-- Mapping 37 | 38 | abstract class MutableMapping { 39 | + {abstract} __setitem__(key, value) 40 | + {abstract} __delitem__(key) 41 | } 42 | 43 | Mapping <|-- MutableMapping 44 | 45 | class dict {} 46 | 47 | MutableMapping <|-- dict 48 | 49 | @enduml 50 | -------------------------------------------------------------------------------- /ch_06/docs/fig_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_06/docs/fig_3.png -------------------------------------------------------------------------------- /ch_06/docs/fig_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_06/docs/fig_4.png -------------------------------------------------------------------------------- /ch_06/docs/metaclass.uml: -------------------------------------------------------------------------------- 1 | @startuml fig_3.png 2 | 'figure 3: Default Metaclass diagram' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | skinparam classAttributeIconSize 0 8 | 9 | class type { 10 | __prepare__() 11 | __new__() 12 | } 13 | 14 | object source_code { 15 | "class MyClass: ..." 16 | } 17 | 18 | object "attributes and methods" as components { 19 | "def __init__(): ..." 20 | } 21 | 22 | class MyClass { 23 | __init__() 24 | } 25 | 26 | type ..> MyClass : "creates" 27 | 28 | source_code ..> components 29 | 30 | components ..> type 31 | 32 | MyClass --> type : "metaclass" 33 | 34 | object instance_1 35 | 36 | object instance_2 37 | 38 | MyClass --> instance_1 : "creates" 39 | MyClass --> instance_2 : "creates" 40 | 41 | @enduml 42 | 43 | 44 | @startuml fig_4.png 45 | 'figure 4: Custom Metaclass diagram' 46 | skinparam monochrome true 47 | skinparam handwritten false 48 | skinparam shadowing false 49 | hide class circle 50 | skinparam classAttributeIconSize 0 51 | 52 | class type { 53 | __prepare__() 54 | __new__() 55 | } 56 | 57 | class SpecialMeta { 58 | __prepare__() 59 | __new__() 60 | } 61 | 62 | type <|-- SpecialMeta 63 | 64 | object source_code { 65 | "class MyClass: ..." 66 | } 67 | 68 | object "attributes and methods" as components { 69 | "def __init__(): ..." 70 | } 71 | 72 | class MyClass { 73 | __init__() 74 | } 75 | 76 | SpecialMeta ..> MyClass : "creates" 77 | 78 | source_code ..> components 79 | 80 | components ..> type 81 | 82 | MyClass --> SpecialMeta : "metaclass" 83 | 84 | object instance_1 85 | 86 | object instance_2 87 | 88 | MyClass --> instance_1 : "creates" 89 | MyClass --> instance_2 : "creates" 90 | 91 | @enduml 92 | -------------------------------------------------------------------------------- /ch_06/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | implicit_reexport = True 3 | show_error_codes = True 4 | python_version = 3.9 5 | strict = True 6 | -------------------------------------------------------------------------------- /ch_06/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 6" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.tox] 18 | legacy_tox_ini = """ 19 | [tox] 20 | minversion = 3.4.0 21 | skipsdist = True 22 | [testenv] 23 | deps = 24 | -rrequirements.txt 25 | black 26 | pytest==6.2.4 27 | tox==3.20.0 28 | mypy==0.812 29 | setenv = 30 | PYTHONPATH = {toxinidir}/src 31 | commands = 32 | black src 33 | python -m doctest --option ELLIPSIS src/classifier.py 34 | python -m doctest --option ELLIPSIS src/media_model_2.py 35 | python -m doctest --option ELLIPSIS src/lookup_mapping.py 36 | python -m doctest --option ELLIPSIS src/dice.py 37 | python -m doctest --option ELLIPSIS src/model.py 38 | python -m doctest --option ELLIPSIS src/debugging_help.py 39 | python -m doctest --option ELLIPSIS src/dict_ext.py 40 | python -m doctest --option ELLIPSIS src/odd_example.py 41 | python -m doctest --option ELLIPSIS docs/examples.md 42 | python -m doctest --option ELLIPSIS docs/case_study_6.md 43 | python -m pytest -vv 44 | mypy src 45 | 46 | """ 47 | -------------------------------------------------------------------------------- /ch_06/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | Flask==2.0 4 | jsonschema==3.2.0 5 | pyyaml==5.3.1 6 | pillow==8.0.1 7 | -------------------------------------------------------------------------------- /ch_06/src/media_model_2.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 4th ed. 3 | 4 | Chapter 3, When Objects Are Alike 5 | """ 6 | 7 | import abc 8 | 9 | 10 | class MediaLoader(abc.ABC): 11 | @abc.abstractmethod 12 | def play(self) -> None: 13 | ... 14 | 15 | @property 16 | @abc.abstractmethod 17 | def ext(self) -> str: 18 | ... 19 | 20 | 21 | test_abstractions = """ 22 | >>> MediaLoader.__abstractmethods__ == frozenset({'ext', 'play'}) 23 | True 24 | 25 | """ 26 | 27 | test_concrete_subclasses = """ 28 | >>> class Wav(MediaLoader): 29 | ... pass 30 | ... 31 | >>> x = Wav() 32 | Traceback (most recent call last): 33 | ... 34 | TypeError: Can't instantiate abstract class Wav with abstract methods ext, play 35 | 36 | >>> class Ogg(MediaLoader): 37 | ... ext = '.ogg' 38 | ... def play(self) -> None: 39 | ... pass 40 | ... 41 | >>> o = Ogg() 42 | 43 | """ 44 | 45 | # Exposed here so mypy can examine this, also. 46 | 47 | 48 | class Ogg(MediaLoader): 49 | ext = ".ogg" 50 | 51 | def play(self) -> None: 52 | pass 53 | 54 | 55 | o = Ogg() 56 | 57 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 58 | -------------------------------------------------------------------------------- /ch_06/src/odd_example.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 3, When Objects Are Alike 5 | """ 6 | 7 | from collections.abc import Container 8 | 9 | 10 | class OddIntegers: 11 | def __contains__(self, x: int) -> bool: 12 | return x % 2 != 0 13 | 14 | 15 | test_odd = """ 16 | >>> odd = OddIntegers() 17 | >>> 1 in odd 18 | True 19 | >>> 2 in odd 20 | False 21 | >>> 3 in odd 22 | True 23 | """ 24 | 25 | test_instance = """ 26 | >>> odd = OddIntegers() 27 | >>> isinstance(odd, Container) 28 | True 29 | >>> issubclass(OddIntegers, Container) 30 | True 31 | 32 | """ 33 | 34 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 35 | -------------------------------------------------------------------------------- /ch_06/tests/test_debugging_help.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 6, Abstract Base Classes and Operator Overloading 5 | """ 6 | from pytest import * # type: ignore[import] 7 | from debugging_help import DebuggingOnly 8 | 9 | def test_happy_path(capfd): 10 | with DebuggingOnly(): 11 | print("That worked") 12 | print("Quite well") 13 | 14 | print("Silence is golden") 15 | 16 | out, err = capfd.readouterr() 17 | assert out == "Silence is golden\n" 18 | 19 | def test_exception_path(capfd): 20 | with raises(AssertionError) as ex: 21 | with DebuggingOnly(): 22 | print("This is helpful") 23 | print("And this, too") 24 | assert False, "Because of this" 25 | 26 | out, err = capfd.readouterr() 27 | assert out == """--EX-->AssertionError('Because of this\\nassert False') 28 | This is helpful 29 | And this, too 30 | """ 31 | -------------------------------------------------------------------------------- /ch_06/tests/test_dice.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 6, Abstract Base Classes and Operator Overloading 5 | """ 6 | import logging 7 | from pytest import * 8 | import random 9 | from dice import D6L 10 | 11 | @fixture 12 | def fixed_random(): 13 | random.seed(42) 14 | 15 | def test_dice_logger(caplog, fixed_random): 16 | caplog.set_level(logging.INFO) 17 | d6l = D6L() 18 | assert d6l.face == 6 19 | assert caplog.messages == [ 20 | 'Rolled 6' 21 | ] 22 | assert D6L.roll.__doc__ == "Some documentation on D6L" 23 | -------------------------------------------------------------------------------- /ch_06/tests/test_lookup_mapping.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 6, Abstract Base Classes and Operator Overloading 5 | """ 6 | from lookup_mapping import Lookup 7 | 8 | def test_lookup_mapping(): 9 | x = Lookup( 10 | [ 11 | ["z", "Zillah"], 12 | ["a", "Amy"], 13 | ["c", "Clara"], 14 | ["b", "Basil"], 15 | ] 16 | ) 17 | 18 | assert "a" in x 19 | assert "d" not in x 20 | assert len(x) == 4 21 | assert x["a"] == "Amy" 22 | assert x["z"] == "Zillah" 23 | assert list(x) == ["a", "b", "c", "z"] 24 | -------------------------------------------------------------------------------- /ch_06/tests/test_partition.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 6, Abstract Base Classes and Operator Overloading 5 | """ 6 | from pytest import * 7 | import random 8 | from model import ( 9 | ShufflingSamplePartition, 10 | CountingDealingPartition 11 | ) 12 | 13 | @fixture 14 | def data(): 15 | samples = [ 16 | { 17 | "sepal_length": i + 0.1, 18 | "sepal_width": i + 0.2, 19 | "petal_length": i + 0.3, 20 | "petal_width": i + 0.4, 21 | "species": f"sample {i}", 22 | } 23 | for i in range(10) 24 | ] 25 | return samples 26 | 27 | 28 | def test_shuffling(data): 29 | ssp = ShufflingSamplePartition(data) 30 | random.seed(42) 31 | assert len(ssp.training) == 8 32 | assert len(ssp.testing) == 2 33 | # Other list methods work 34 | assert ssp.pop() == { 35 | 'petal_length': 1.3, 36 | 'petal_width': 1.4, 37 | 'sepal_length': 1.1, 38 | 'sepal_width': 1.2, 39 | 'species': 'sample 1' 40 | } 41 | 42 | 43 | def test_shuffling_append(data): 44 | ssp = ShufflingSamplePartition() 45 | for row in data: 46 | ssp.append(row) 47 | random.seed(42) 48 | assert len(ssp.training) == 8 49 | assert len(ssp.testing) == 2 50 | # Other list methods work 51 | assert ssp.pop() == { 52 | 'petal_length': 1.3, 53 | 'petal_width': 1.4, 54 | 'sepal_length': 1.1, 55 | 'sepal_width': 1.2, 56 | 'species': 'sample 1' 57 | } 58 | 59 | 60 | def test_dealing(data): 61 | cdp = CountingDealingPartition(data) 62 | # random.seed(42) # Not used. 63 | assert len(cdp.training) == 8 64 | assert len(cdp.testing) == 2 65 | -------------------------------------------------------------------------------- /ch_07/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 7. Python Data Structures. 8 | 9 | The @dataclass definition and generic collections 10 | -------------------------------------------------------------------------------- /ch_07/docs/logical_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_07/docs/logical_view_1.png -------------------------------------------------------------------------------- /ch_07/docs/logical_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 3: Logical Model' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | class TrainingData { 9 | name: str 10 | uploaded: datetime.datetime 11 | tested: datetime.datetime 12 | load(source: Iterable) 13 | } 14 | class Sample { 15 | sepal_length: float 16 | sepal_width: float 17 | petal_length: float 18 | petal_width: float 19 | } 20 | class KnownSample { 21 | species: str 22 | } 23 | class TrainingKnownSample { 24 | } 25 | class TestingKnownSample { 26 | classification: str 27 | } 28 | class UnknownSample { 29 | classification: str 30 | } 31 | class Hyperparameter { 32 | k: int 33 | quality: float 34 | classify(s: Sample): str 35 | test(t: List[TestingKnownSample]) 36 | } 37 | class "List[TestingKnownSample]" 38 | class "List[TrainingKnownSample]" 39 | class "List[Hyperparameter]" 40 | TrainingData *---> "List[TrainingKnownSample]" : training > 41 | TrainingData *---> "List[TestingKnownSample]" : testing > 42 | TrainingData *---> "List[Hyperparameter]" : tuning > 43 | "List[TrainingKnownSample]" o--> TrainingKnownSample 44 | "List[TestingKnownSample]" o--> TestingKnownSample 45 | "List[Hyperparameter]" *--> Hyperparameter 46 | Sample <|-- KnownSample 47 | KnownSample <|-- TestingKnownSample 48 | KnownSample <|-- TrainingKnownSample 49 | Sample <|-- UnknownSample 50 | Hyperparameter ...> TrainingData : data > 51 | @enduml 52 | -------------------------------------------------------------------------------- /ch_07/docs/logical_view_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_07/docs/logical_view_2.png -------------------------------------------------------------------------------- /ch_07/docs/logical_view_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: Logical Model' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | left to right direction 8 | 9 | class _SomeClass <> { 10 | attributes 11 | } 12 | class dataclass { 13 | __call__(cls: Type) : Type 14 | } 15 | class SomeClass <> { 16 | attributes 17 | __init__(self, attributes) 18 | __repr__(self) : str 19 | __eq__(self, other) : bool 20 | } 21 | _SomeClass ---> dataclass : input > 22 | dataclass ---> SomeClass : output > 23 | @enduml -------------------------------------------------------------------------------- /ch_07/docs/logical_view_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_07/docs/logical_view_3.png -------------------------------------------------------------------------------- /ch_07/docs/logical_view_3.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 4: Frozen Dataclasses' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | class TrainingData { 9 | name: str 10 | uploaded: datetime.datetime 11 | tested: datetime.datetime 12 | load(source: Iterable) 13 | } 14 | class Sample <> { 15 | sepal_length: float 16 | sepal_width: float 17 | petal_length: float 18 | petal_width: float 19 | } 20 | class KnownSample <> { 21 | species: str 22 | } 23 | class TrainingKnownSample { 24 | sample: KnownSample 25 | } 26 | class TestingKnownSample { 27 | sample: KnownSample 28 | classification: str 29 | } 30 | class UnknownSample { 31 | sample: Sample 32 | classification: str 33 | } 34 | class Hyperparameter { 35 | k: int 36 | quality: float 37 | classify(s: UnknownSample): str 38 | test(t: List[TestingKnownSample]): int 39 | } 40 | class "List[TestingKnownSample]" 41 | class "List[TrainingKnownSample]" 42 | class "List[Hyperparameter]" 43 | TrainingData *---> "List[TrainingKnownSample]" : training > 44 | TrainingData *---> "List[TestingKnownSample]" : testing > 45 | TrainingData *---> "List[Hyperparameter]" : tuning > 46 | "List[TrainingKnownSample]" o--> TrainingKnownSample 47 | "List[TestingKnownSample]" o--> TestingKnownSample 48 | "List[Hyperparameter]" *--> Hyperparameter 49 | Sample <|- KnownSample 50 | TestingKnownSample *--> KnownSample 51 | TrainingKnownSample *--> KnownSample 52 | UnknownSample *--> Sample 53 | Hyperparameter ...> TrainingData : data > 54 | @enduml 55 | -------------------------------------------------------------------------------- /ch_07/docs/logical_view_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_07/docs/logical_view_4.png -------------------------------------------------------------------------------- /ch_07/docs/logical_view_4.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 5: NamedTuple classes' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | package inheritance { 9 | class Sample { 10 | sepal_length: float 11 | sepal_width: float 12 | petal_length: float 13 | petal_width: float 14 | } 15 | class KnownSample { 16 | species: str 17 | } 18 | KnownSample --|> Sample 19 | } 20 | 21 | package composition { 22 | class Sample_C { 23 | sepal_length: float 24 | sepal_width: float 25 | petal_length: float 26 | petal_width: float 27 | } 28 | class KnownSample_C { 29 | sample: Sample 30 | species: str 31 | } 32 | KnownSample_C *--> Sample_C 33 | } 34 | @enduml 35 | -------------------------------------------------------------------------------- /ch_07/docs/queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_07/docs/queue.png -------------------------------------------------------------------------------- /ch_07/docs/queue.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Queue' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | 9 | object add_new 10 | object next 11 | 12 | map Queue { 13 | 3 => fourth_path 14 | 2 => third_path 15 | 1 => second_path 16 | 0 => first_path 17 | } 18 | 19 | Queue::0 --> next 20 | 21 | add_new --> Queue::3 22 | 23 | 24 | @enduml 25 | -------------------------------------------------------------------------------- /ch_07/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | implicit_reexport = True 3 | show_error_codes = True 4 | python_version = 3.9 5 | strict = True 6 | -------------------------------------------------------------------------------- /ch_07/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 7" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.tox] 18 | legacy_tox_ini = """ 19 | [tox] 20 | minversion = 3.4.0 21 | skipsdist = True 22 | [testenv] 23 | deps = 24 | -rrequirements.txt 25 | black 26 | pytest==6.2.4 27 | tox==3.20.0 28 | mypy==0.812 29 | setenv = 30 | PYTHONPATH = {toxinidir}/src 31 | PYTHONHASHSEED = 0 32 | commands = 33 | black src 34 | python -m doctest --option ELLIPSIS src/model_t.py 35 | python -m doctest --option ELLIPSIS src/lists.py 36 | python -m doctest --option ELLIPSIS src/classifier.py 37 | python -m doctest --option ELLIPSIS src/model_f.py 38 | python -m doctest --option ELLIPSIS src/model.py 39 | python -m doctest --option ELLIPSIS src/queue_example.py 40 | python -m doctest --option ELLIPSIS src/dictionaries.py 41 | python -m doctest --option ELLIPSIS src/dc_stocks.py 42 | python -m doctest --option ELLIPSIS docs/examples.md 43 | python -m doctest --option ELLIPSIS docs/case_study_7.md 44 | python -m pytest -vv 45 | mypy src 46 | """ 47 | -------------------------------------------------------------------------------- /ch_07/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | Flask==2.0 4 | jsonschema==3.2.0 5 | pyyaml==5.3.1 6 | pillow==8.0.1 7 | -------------------------------------------------------------------------------- /ch_07/tests/test_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 7. 5 | """ 6 | 7 | def test_placeholder(): 8 | pass 9 | -------------------------------------------------------------------------------- /ch_08/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 8. Functional Techniques. 8 | 9 | Using functions and immutable objects. 10 | -------------------------------------------------------------------------------- /ch_08/big_number.txt: -------------------------------------------------------------------------------- 1 | # A big number 2 | 617 3 | 32317006071311007300714876688669951960444102669715484032130345427524655138867890893197201411522913463688717960921898019494119559150490921095088152386448283120630877367300996091750197750389652106796057638384067568276792218642619756161838094338476170470581645852036305042887575891541065808607552399123930385521914333389668342420684974786564569494856176035326322058077805659331026192708460314150258592864177116725943603718461857357598351152301645904403697613233287231227125684710820209725157101726931323469678542580656697935045997268352998638215525166389437335543602135433229604645318478604952148193555853611059596230656 4 | -------------------------------------------------------------------------------- /ch_08/docs/case_study_8.md: -------------------------------------------------------------------------------- 1 | 2 | ## Processing Overview 3 | 4 | ## Splitting the Data 5 | 6 | ## Rethinking classification 7 | 8 | ## The `partition()` function 9 | 10 | ## One-Pass Partitioning 11 | -------------------------------------------------------------------------------- /ch_08/docs/context_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_08/docs/context_view.png -------------------------------------------------------------------------------- /ch_08/docs/context_view.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Processing Overivew' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | 8 | actor Botanist as b 9 | actor User as u 10 | package Classifier { 11 | usecase "Provide Training Data" as UC1 12 | usecase "Set Parameters and Test Classifier" as UC2 13 | usecase "Make Classification Request" as UC3 14 | } 15 | b --> UC1 16 | b --> UC2 17 | u --> UC3 18 | @enduml -------------------------------------------------------------------------------- /ch_08/docs/logical_view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_08/docs/logical_view.png -------------------------------------------------------------------------------- /ch_08/docs/logical_view.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 4: Rethinking classification' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class Sample { 9 | sepal_length: float 10 | sepal_width: float 11 | petal_length: float 12 | petal_width: float 13 | } 14 | class KnownSample { 15 | sample: Sample 16 | species: str 17 | } 18 | note bottom: Created at load time 19 | 20 | class UnknownSample { 21 | sample: Sample 22 | } 23 | note bottom: Created by user 24 | 25 | class TrainingKnownSample #Silver { 26 | sample: KnownSample 27 | } 28 | class TestingKnownSample #Silver { 29 | sample: KnownSample 30 | } 31 | note "Created by partition()" as partition 32 | partition .. TrainingKnownSample 33 | partition .. TestingKnownSample 34 | 35 | KnownSample *--> Sample 36 | 37 | UnknownSample *--> Sample 38 | 39 | TrainingKnownSample *--> KnownSample 40 | 41 | TestingKnownSample *--> KnownSample 42 | 43 | class ClassifiedKnownSample { 44 | sample: KnownSample 45 | classification: str 46 | } 47 | note top: Created by classifier while testing 48 | 49 | class ClassifiedUnknownSample { 50 | sample: UnknownSample 51 | classification: str 52 | } 53 | note top: Created by classifier for user 54 | 55 | ClassifiedKnownSample *--> KnownSample 56 | ClassifiedKnownSample ..> TestingKnownSample : based on 57 | ClassifiedUnknownSample *--> UnknownSample 58 | @enduml 59 | -------------------------------------------------------------------------------- /ch_08/docs/process_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_08/docs/process_view_1.png -------------------------------------------------------------------------------- /ch_08/docs/process_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: Rethinking classification' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | 7 | title UnknownSample classification 8 | start 9 | 10 | :sample : __UnknownSample__ ] 11 | 12 | :Compute classification of sample, species : __str__; 13 | 14 | :Create __ClassifiedUnknownSample__ from sample and species; 15 | 16 | :ClassifiedUnknownSample 17 | sample = sample 18 | classification = species] 19 | @enduml -------------------------------------------------------------------------------- /ch_08/docs/process_view_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_08/docs/process_view_2.png -------------------------------------------------------------------------------- /ch_08/docs/process_view_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 3: Rethinking classification' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | 7 | title TestingKnownSample classification 8 | start 9 | 10 | :sample : __TestingKnownSample__ ] 11 | 12 | :Compute classification of sample, species : __str__; 13 | 14 | :Create __ClassifiedKnownSample__ from sample and species; 15 | 16 | :ClassifiedKnownSample 17 | sample = sample 18 | classification = species] 19 | @enduml -------------------------------------------------------------------------------- /ch_08/docs/sample_data.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming 2 | 3 | Chapter 8. The Intersection of Object-Oriented and Functional Programming 4 | 5 | Some sample data to show how the `enumerate()` function works. 6 | -------------------------------------------------------------------------------- /ch_08/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 8" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | license = {file = "LICENSE.txt"} 8 | keywords = ["k-NN", "object-oriented design"] 9 | authors = [ 10 | {email = "slott56@gmail.com"}, 11 | {name = "Steven F. Lott"} 12 | ] 13 | 14 | [tool.tox] 15 | legacy_tox_ini = """ 16 | [tox] 17 | minversion = 3.4.0 18 | skipsdist = True 19 | 20 | [testenv] 21 | deps = 22 | -rrequirements.txt 23 | black 24 | pytest==6.2.4 25 | tox==3.20.0 26 | mypy==0.812 27 | setenv = 28 | PYTHONPATH = {toxinidir}/src 29 | # Required to force a specific order for sets 30 | PYTHONHASHSEED = 1502041761 31 | commands = 32 | black src 33 | python -m doctest --option ELLIPSIS docs/case_study_8.md 34 | python -m doctest --option ELLIPSIS docs/examples.md 35 | python -m doctest --option ELLIPSIS src/file_demo.py 36 | python -m doctest --option ELLIPSIS src/function_demo.py 37 | python -m doctest --option ELLIPSIS src/model.py 38 | python -m doctest --option ELLIPSIS src/parameters.py 39 | python -m doctest --option ELLIPSIS src/model.py 40 | python -m pytest -vv 41 | mypy --strict --show-error-codes --python-version 3.9 src 42 | 43 | """ 44 | -------------------------------------------------------------------------------- /ch_08/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | pyyaml==5.3.1 5 | pillow==8.0.1 6 | -------------------------------------------------------------------------------- /ch_09/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 9. Strings and Serialization. 8 | 9 | Serialization and deserialization of data. 10 | -------------------------------------------------------------------------------- /ch_09/docs/case_study_9.md: -------------------------------------------------------------------------------- 1 | 2 | # Case Study for Chapter 9, Strings and Serialization 3 | 4 | ## Serialization 5 | 6 | ``` 7 | >>> from model import TrainingKnownSample 8 | >>> s1 = TrainingKnownSample( 9 | ... sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species="Iris-setosa") 10 | >>> serialized = repr(s1) 11 | >>> serialized 12 | "TrainingKnownSample(sepal_length=5.1, sepal_width=3.5, petal_length=1.4, petal_width=0.2, species='Iris-setosa', )" 13 | >>> s2 = eval(serialized) 14 | >>> s1 == s2 15 | True 16 | >>> id(s1) == id(s2) 17 | False 18 | 19 | 20 | ``` 21 | 22 | ## Built-in Serialization 23 | 24 | ## CSV Format Designs 25 | 26 | ### CSV Dictionary Reader 27 | 28 | ``` 29 | >>> from model import CSVIrisReader 30 | >>> from pathlib import Path 31 | >>> test_data = Path.cwd().parent/"bezdekIris.data" 32 | >>> rdr = CSVIrisReader(test_data) 33 | >>> samples = list(rdr.data_iter()) 34 | >>> len(samples) 35 | 150 36 | >>> samples[0] 37 | {'sepal_length': '5.1', 'sepal_width': '3.5', 'petal_length': '1.4', 'petal_width': '0.2', 'species': 'Iris-setosa'} 38 | 39 | 40 | ``` 41 | 42 | ``` 43 | >>> from model import TrainingData 44 | >>> training_data = TrainingData("besdekIris") 45 | >>> rdr = CSVIrisReader(test_data) 46 | >>> training_data.load(rdr.data_iter()) 47 | 48 | 49 | ``` 50 | 51 | ### CSV Reader 52 | 53 | ### CSV Dialects 54 | 55 | ## JSON Serialization 56 | 57 | ### Newline Delimited JSON 58 | 59 | ### JSON Validation 60 | 61 | ## The YAML Variant 62 | 63 | -------------------------------------------------------------------------------- /ch_09/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_09/docs/fig_1.png -------------------------------------------------------------------------------- /ch_09/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Bytes and Strings' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | 8 | object str { 9 | "Flambé" 10 | } 11 | 12 | object bytes { 13 | [70, 108, 97, 109, 98, 195, 169] 14 | b'Flamb\xc3\xa9' 15 | } 16 | 17 | str --> bytes : "encode >" 18 | bytes --> str : "decode >" 19 | @enduml 20 | -------------------------------------------------------------------------------- /ch_09/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | implicit_reexport = True 3 | show_error_codes = True 4 | python_version = 3.9 5 | strict = True 6 | -------------------------------------------------------------------------------- /ch_09/pickled_list: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_09/pickled_list -------------------------------------------------------------------------------- /ch_09/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 9" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.tox] 18 | legacy_tox_ini = """ 19 | [tox] 20 | minversion = 3.4.0 21 | skipsdist = True 22 | [testenv] 23 | deps = 24 | -rrequirements.txt 25 | black 26 | pytest==6.2.4 27 | tox==3.20.0 28 | mypy==0.812 29 | setenv = 30 | PYTHONPATH = {toxinidir}/src 31 | commands = 32 | black src 33 | python -m doctest --option ELLIPSIS src/pattern_matching.py 34 | python -m doctest --option ELLIPSIS src/classifier.py 35 | python -m doctest --option ELLIPSIS src/contacts.py 36 | python -m doctest --option ELLIPSIS src/distances.py 37 | python -m doctest --option ELLIPSIS src/url_poll.py 38 | python -m doctest --option ELLIPSIS src/code_scanner.py 39 | python -m doctest --option ELLIPSIS src/model.py 40 | python -m doctest --option ELLIPSIS docs/examples.md 41 | python -m doctest --option ELLIPSIS docs/case_study_9.md 42 | python -m pytest -vv 43 | mypy src 44 | 45 | """ 46 | -------------------------------------------------------------------------------- /ch_09/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | Flask==2.0 4 | jsonschema==3.2.0 5 | pyyaml==5.3.1 6 | pillow==8.0.1 7 | -------------------------------------------------------------------------------- /ch_09/src/contacts.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 9. Strings and Serialization 5 | """ 6 | 7 | 8 | class Contact: 9 | def __init__(self, first: str, last: str) -> None: 10 | self.first = first 11 | self.last = last 12 | 13 | @property 14 | def full_name(self) -> str: 15 | return f"{self.first} {self.last}" 16 | 17 | 18 | test_contact_1 = """ 19 | >>> import json 20 | >>> c = Contact("Noriko", "Hannah") 21 | >>> json.dumps(c.__dict__) 22 | '{"first": "Noriko", "last": "Hannah"}' 23 | 24 | 25 | """ 26 | 27 | 28 | import json 29 | from typing import Any 30 | 31 | 32 | class ContactEncoder(json.JSONEncoder): 33 | def default(self, obj: Any) -> Any: 34 | if isinstance(obj, Contact): 35 | return { 36 | "__class__": "Contact", 37 | "first": obj.first, 38 | "last": obj.last, 39 | "full_name": obj.full_name, 40 | } 41 | return super().default(obj) 42 | 43 | 44 | def decode_contact(json_object: Any) -> Any: 45 | if json_object.get("__class__") == "Contact": 46 | return Contact(json_object["first"], json_object["last"]) 47 | else: 48 | return json_object 49 | 50 | 51 | test_contact_2 = """ 52 | >>> import json 53 | >>> c = Contact("Noriko", "Hannah") 54 | >>> text = json.dumps(c, cls=ContactEncoder) 55 | >>> text 56 | '{"__class__": "Contact", "first": "Noriko", "last": "Hannah", "full_name": "Noriko Hannah"}' 57 | 58 | >>> some_text = ( 59 | ... '{"__class__": "Contact", "first": "Milli", "last": "Dale", ' 60 | ... '"full_name": "Milli Dale"}' 61 | ... ) 62 | >>> c2 = json.loads(some_text, object_hook=decode_contact) 63 | >>> c2.full_name 64 | 'Milli Dale' 65 | 66 | """ 67 | 68 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 69 | -------------------------------------------------------------------------------- /ch_10/README.rst: -------------------------------------------------------------------------------- 1 | ############################################### 2 | Python 3 Object-Oriented Programming Case Study 3 | ############################################### 4 | 5 | This is the *Python 3 Object-Oriented Programming, 4th ed.* case study, 6 | 7 | Chapter 10. Iterator Design Pattern. 8 | 9 | Iterators, iterables, and more features of generic collections. 10 | -------------------------------------------------------------------------------- /ch_10/docs/Generators.drawio: -------------------------------------------------------------------------------- 1 | 7VlNc9owEP01zLQHGNsCDMdACJm0zbRJO0l66Si2sNXKkivLgPPrK9ky/oI0oYGEKSdrn1Yr7b43Qju0wDhYTjkM/U/MRaRlGe6yBU5blmWaRl9+FJJkyHBoZ4DHsaudCuAaPyANGhqNsYuiiqNgjAgcVkGHUYocUcEg52xRdZsxUt01hB5qANcOJE30BrvCz9BBzyjwc4Q9X6wS1jMBzJ01EPnQZYsSBCYtMOaMiWwULMeIqOLldcnWnW2YXR2MIyqesuCm+8W478UXF6Pph6vLu8n0/G7a1lHmkMQ64SmiiEPBeMvqwyBsgRG9j9RH2kRuNLpXM54ancXUEZhRnZ9I8qIJtFTzvgiIBEw5jARnv9CYERkXnFJGpedohgmpQZBgj0rTkUkhiY/miAss6TjREwF2XbXNaOFjga5D6Kg9F1J8EuMspi5S+RoqPKPiWh9qTbXy1OUGaFmCdPWmiAVI8ES66FkL9LIlWspWTvWiEEauC7+kCaAxqKXorSIXbMmBJuwZ5Fn/Qt5kGXIURf8PfWBgvS36QIO+nKUohLRCSf93rK6JtCbtKL0kT6SDaYRCflJ+jXRuBgNMkmx2pYGWBUBXscMxJA009Ryr7SCN2hHieFZsmKtlpiRlECxZllmlh2Mxd7JzZIeWNcjOna14bi4BcnEcNHfeGP5NivYlrhmjds10mzo1zX0KtXtAQlWYUcA3J1eXZadMu6mOj8rduXLt11auaTaqjVz5vtMm48JnHqOQTAq0VpfC5yNjoSboJxIi0Y9VGAtWpU9WkCe3en1q3Cmj08vN02V58jTRlhPzebqvCuLCyF8ZG8nJb+GNFdCMCMg9JB7x0890VZ1HqeaIQIHn1dfxOt7SpSecw6TkEDJMRVSK/FkBpd/oXAlaQcDsVd+0f/G3er2aZrITFApapbK9qHoHdB0mGMmGR/IfhwS963Q674+X3m4vPbDmWbnfS69/QPo86nJXugT9Wruz5hk53Kcs7QOS5cZ25yjS3Yr01V+MgwNS6dN7naNsdyrbvf7mf3+wva/xlW22CW9bP5xvNqbtN9XnWNs3OmiJxW1pXGqcpFWEU0ZSIbTWuWzXLXWf2C0NXqI5anYzVu0pmdt5iOz8etVjbZFdu1b7tUBZgo1AW3RIa9VovSU1HqwYwRPFaO9FjNbwhcQITLvzUnKUZvEHWuZe/A0JJn8A -------------------------------------------------------------------------------- /ch_10/docs/Generators.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_10/docs/Generators.png -------------------------------------------------------------------------------- /ch_10/docs/case_study_10.md: -------------------------------------------------------------------------------- 1 | # Case Study for Chapter Ten, The Iterator Pattern 2 | 3 | ## Multiple Partitions 4 | 5 | ```python 6 | >>> import itertools 7 | 8 | >>> p1 = range(1, 10, 2) 9 | >>> p2 = range(2, 10, 2) 10 | >>> itertools.chain(p1, p2) 11 | 12 | 13 | >>> list(itertools.chain(p1, p2)) 14 | [1, 3, 5, 7, 9, 2, 4, 6, 8] 15 | 16 | ``` 17 | 18 | ## Testing 19 | 20 | ## K-NN Altenative `bisect` 21 | 22 | ## K-NN Alternative `heapq` 23 | 24 | ## Conclusion 25 | 26 | ```python 27 | 28 | >>> import timeit 29 | 30 | >>> m = timeit.timeit( 31 | ... "manhattan(d1, d2)", 32 | ... """ 33 | ... from model import Sample, KnownSample, TrainingKnownSample, TestingKnownSample 34 | ... from model import manhattan, euclidean 35 | ... d1 = TrainingKnownSample(KnownSample(Sample(1, 2, 3, 4), "x")) 36 | ... d2 = KnownSample(Sample(2, 3, 4, 5), "y") 37 | ... """) 38 | 39 | >>> e = timeit.timeit( 40 | ... "euclidean(d1, d2)", 41 | ... """ 42 | ... from model import Sample, KnownSample, TrainingKnownSample, TestingKnownSample 43 | ... from model import manhattan, euclidean 44 | ... d1 = TrainingKnownSample(KnownSample(Sample(1, 2, 3, 4), "x")) 45 | ... d2 = KnownSample(Sample(2, 3, 4, 5), "y") 46 | ... """) 47 | 48 | >>> print(f"Manhattan: {m:.3f}") 49 | Manhattan: ... 50 | >>> print(f"Euclidean: {e:.3f}") 51 | Euclidean: ... 52 | 53 | ``` 54 | -------------------------------------------------------------------------------- /ch_10/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_10/docs/fig_1.png -------------------------------------------------------------------------------- /ch_10/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Iterator Abstraction' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | hide class circle 8 | 9 | class Collection { 10 | __contains__() 11 | __iter__() 12 | __len__() 13 | } 14 | 15 | class Sized { 16 | __len__() 17 | } 18 | 19 | class Iterable { 20 | __iter__() 21 | } 22 | 23 | class Container { 24 | __contains__() 25 | } 26 | 27 | class Sequence { 28 | index() 29 | count() 30 | } 31 | 32 | class list { 33 | append() 34 | extend() 35 | pop() 36 | remove() 37 | } 38 | 39 | class Iterator { 40 | __next__() 41 | __iter__() 42 | } 43 | 44 | Container <|-- Collection 45 | Sized <|-- Collection 46 | Iterable <|-- Collection 47 | 48 | Collection <|-- Sequence 49 | 50 | Sequence <|-- list 51 | 52 | Iterable::__iter__ ..> Iterator : "creates" 53 | Iterable <|-- Iterator 54 | @enduml 55 | -------------------------------------------------------------------------------- /ch_10/docs/fig_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_10/docs/fig_2.png -------------------------------------------------------------------------------- /ch_10/docs/fig_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Generators' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | hide class circle 8 | 9 | object function { 10 | for line in source: 11 | if "WARN" in line: 12 | yield tuple(...) 13 | } 14 | 15 | object expression { 16 | tuple(...) 17 | for line in source 18 | if "WARN" in line 19 | } 20 | 21 | function <...> expression 22 | 23 | @enduml 24 | -------------------------------------------------------------------------------- /ch_10/docs/fig_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_10/docs/fig_3.png -------------------------------------------------------------------------------- /ch_10/docs/fig_3.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Generators' 3 | left to right direction 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | skinparam componentStyle rectangle 8 | hide class circle 9 | 10 | folder "hash (mod 3) = 1" { 11 | [Sample 262] 12 | [Sample 67] 13 | [Sample 304] 14 | } 15 | 16 | folder "hash (mod 3) = 2" { 17 | [Sample 26] 18 | [Sample 11] 19 | } 20 | folder "hash (mod 3) = 0" { 21 | [Sample 231] 22 | [Sample 342] 23 | } 24 | 25 | [Sample 262] <..> [Sample 304] : Equal 26 | 27 | @enduml 28 | -------------------------------------------------------------------------------- /ch_10/docs/log_line_regex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_10/docs/log_line_regex.png -------------------------------------------------------------------------------- /ch_10/docs/log_line_regex.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 6: Log Line regex' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | state "Group
" as group_dt { 11 | state "\w\w\w \d\d, \d\d\d\d \d\d:\d\d:\d\d" as dt_1 12 | } 13 | 14 | state "\s" as space_1 { 15 | } 16 | 17 | state "Group " as group_level { 18 | state "\w" as level_letter 19 | level_letter --> level_letter 20 | 21 | } 22 | 23 | state "\s" as space_2 { 24 | } 25 | 26 | state "Group " as group_msg { 27 | state "." as message_char 28 | message_char --> message_char 29 | } 30 | 31 | [*] -right-> group_dt 32 | group_dt -right-> space_1 33 | space_1 --> space_1 34 | 35 | space_1 -right-> group_level 36 | group_level -right-> space_2 37 | space_2 ---> space_2 38 | 39 | space_2 -right-> group_msg 40 | 41 | group_msg -right-> [*] 42 | 43 | @enduml 44 | -------------------------------------------------------------------------------- /ch_10/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 10" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | license = {file = "LICENSE.txt"} 8 | keywords = ["k-NN", "object-oriented design"] 9 | authors = [ 10 | {email = "slott56@gmail.com"}, 11 | {name = "Steven F. Lott"} 12 | ] 13 | 14 | [tool.tox] 15 | legacy_tox_ini = """ 16 | [tox] 17 | minversion = 3.4.0 18 | skipsdist = True 19 | envlist = testenv 20 | 21 | [testenv] 22 | deps = 23 | -rrequirements.txt 24 | black 25 | pytest==6.2.4 26 | tox==3.20.0 27 | mypy==0.812 28 | setenv = 29 | PYTHONPATH = {toxinidir}/src 30 | MYPYPATH = {toxinidir}/src 31 | PYTHONHASHSEED = 0 32 | commands = 33 | black src 34 | python -m doctest --option ELLIPSIS docs/case_study_10.md 35 | python -m doctest --option ELLIPSIS docs/examples.md 36 | python -m doctest --option ELLIPSIS src/model.py 37 | python -m doctest --option ELLIPSIS src/iterator_protocol.py 38 | python -m doctest --option ELLIPSIS src/log_analysis.py 39 | python -m pytest -vv 40 | mypy src --strict --python-version 3.9 41 | 42 | [testenv:bench] 43 | commands = 44 | mypy benches --strict --python-version 3.9 45 | python benches/bench_knn.py 46 | 47 | """ 48 | -------------------------------------------------------------------------------- /ch_10/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | pyyaml==5.3.1 5 | pillow==8.0.1 6 | -------------------------------------------------------------------------------- /ch_10/src/iterator_protocol.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 10. The Iterator Pattern 5 | """ 6 | from typing import Iterable, Iterator 7 | 8 | 9 | class CapitalIterable(Iterable[str]): 10 | def __init__(self, string: str) -> None: 11 | self.string = string 12 | 13 | def __iter__(self) -> Iterator[str]: 14 | return CapitalIterator(self.string) 15 | 16 | 17 | class CapitalIterator(Iterator[str]): 18 | def __init__(self, string: str) -> None: 19 | self.words = [w.capitalize() for w in string.split()] 20 | self.index = 0 21 | 22 | def __next__(self) -> str: 23 | if self.index == len(self.words): 24 | raise StopIteration() 25 | 26 | word = self.words[self.index] 27 | self.index += 1 28 | return word 29 | 30 | # def __iter__(self) -> Iterator[str]: 31 | # return self 32 | 33 | 34 | test_iterable = """ 35 | >>> iterable = CapitalIterable('the quick brown fox jumps over the lazy dog') 36 | >>> iterator = iter(iterable) 37 | >>> while True: 38 | ... try: 39 | ... print(next(iterator)) 40 | ... except StopIteration: 41 | ... break 42 | ... 43 | The 44 | Quick 45 | Brown 46 | Fox 47 | Jumps 48 | Over 49 | The 50 | Lazy 51 | Dog 52 | 53 | >>> for i in iterable: 54 | ... print(i) 55 | ... 56 | The 57 | Quick 58 | Brown 59 | Fox 60 | Jumps 61 | Over 62 | The 63 | Lazy 64 | Dog 65 | 66 | >>> iterator = iter(iterable) 67 | >>> for i in iter(iterator): 68 | ... print(i) 69 | ... 70 | The 71 | Quick 72 | Brown 73 | Fox 74 | Jumps 75 | Over 76 | The 77 | Lazy 78 | Dog 79 | 80 | """ 81 | 82 | 83 | __test__ = {name: case for name, case in globals().items() if name.startswith("test_")} 84 | -------------------------------------------------------------------------------- /ch_10/tests/test_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming Case Study 3 | 4 | Chapter 10. The iterator pattern 5 | """ 6 | def test_placeholder(): 7 | pass 8 | -------------------------------------------------------------------------------- /ch_11/README.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming, 4th ed. 2 | 3 | Chapter 11. Common design patterns. 4 | 5 | - The Decorator pattern 6 | - The Observer pattern 7 | - The Strategy pattern 8 | - The Command pattern 9 | - The State pattern 10 | - The Singleton pattern 11 | -------------------------------------------------------------------------------- /ch_11/boat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/boat.png -------------------------------------------------------------------------------- /ch_11/bonus/zonk_patterns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/bonus/zonk_patterns.png -------------------------------------------------------------------------------- /ch_11/bonus/zonk_score.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 11. Common Design Patterns 5 | """ 6 | import re 7 | from dice import Dice 8 | 9 | class Pattern: 10 | regex : str 11 | reroll : int 12 | def condition(self, dice: Dice) -> bool: 13 | text = "".join(str(d) for d in dice.dice) 14 | return bool(re.match(self.regex, text)) 15 | 16 | class Three(Pattern): 17 | regex = r".*[1-6]{3}.*" 18 | reroll = 3 19 | pass 20 | 21 | class Four(Pattern): 22 | regex = r".*[1-6]{4}.*" 23 | reroll = 2 24 | pass 25 | 26 | class Five(Pattern): 27 | regex = r".*[1-6]{5}.*" 28 | reroll = 1 29 | pass 30 | 31 | class SmallStraight(Pattern): 32 | regex = r"1.?2.?3.?4.?5.?|2.?3.?4.?5.?6.?" 33 | reroll = 1 34 | pass 35 | 36 | class LargeStraight(Pattern): 37 | regex = r"123456" 38 | pass 39 | 40 | class Ace(Pattern): 41 | regex = r".*1.*" 42 | reroll = 5 43 | pass 44 | 45 | class Quint(Pattern): 46 | regex = r".*5.*" 47 | reroll = 5 48 | pass 49 | -------------------------------------------------------------------------------- /ch_11/bonus/zonk_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/bonus/zonk_state.png -------------------------------------------------------------------------------- /ch_11/bonus/zonk_state.uml: -------------------------------------------------------------------------------- 1 | @startuml zonk_state.png 2 | 'figure 6: The Zonk States' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | state "Hand of dice" as hand 11 | state "Some scoring dice" as subset 12 | state "Go On" as choice <> 13 | state "Zonked!" as score0 14 | state "Score" as scoreall 15 | 16 | score0 : score nothing 17 | scoreall : score accumulated total 18 | hand : evaluate options 19 | 20 | [*] --> hand : Roll all the dice 21 | 22 | hand --> subset : Separate scoring dice 23 | 24 | subset --> hand : Reroll non-scoring dice 25 | 26 | hand --> score0 : No scoring dice 27 | 28 | hand --> choice : All dice score 29 | 30 | choice --> hand : Roll all the dice 31 | 32 | subset --> scoreall : Stand pat 33 | choice --> scoreall : Stand pat 34 | 35 | @enduml 36 | 37 | 38 | @startuml zonk_patterns.png 39 | 'figure 7: The Zonk Patterns' 40 | skinparam monochrome true 41 | skinparam handwritten false 42 | skinparam shadowing false 43 | skinparam classAttributeIconSize 0 44 | hide class circle 45 | hide abstract circle 46 | 47 | class Hand {} 48 | 49 | class Dice {} 50 | 51 | abstract class Pattern { 52 | + condition(Dice) -> bool 53 | } 54 | 55 | class Three {} 56 | class Four {} 57 | class Five {} 58 | 59 | class SmallStraight {} 60 | 61 | class LargeStraight {} 62 | 63 | class Ace {} 64 | 65 | class Cinque {} 66 | 67 | Pattern <|-- Three 68 | Pattern <|-- Four 69 | Pattern <|-- Five 70 | Pattern <|-- SmallStraight 71 | Pattern <|-- LargeStraight 72 | Pattern <|-- Ace 73 | Pattern <|-- Cinque 74 | 75 | Hand -- "*" Dice 76 | Dice -- "*" Pattern 77 | 78 | @enduml 79 | -------------------------------------------------------------------------------- /ch_11/docs/command.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/command.png -------------------------------------------------------------------------------- /ch_11/docs/command.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 5: The Command Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | abstract class Command { 11 | + execute() 12 | } 13 | 14 | class Core { 15 | + stuff_to_do: List[Command] 16 | } 17 | 18 | class Command1 { 19 | + execute() 20 | } 21 | 22 | class Command2 { 23 | + execute() 24 | } 25 | 26 | Core::stuff_to_do *-- Command 27 | 28 | Command <|.. Command1 29 | Command <|.. Command2 30 | 31 | @enduml 32 | -------------------------------------------------------------------------------- /ch_11/docs/decorator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/decorator.png -------------------------------------------------------------------------------- /ch_11/docs/decorator.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: The Decorator Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | abstract class Interface { 11 | + someAction() 12 | } 13 | 14 | class Core { 15 | + someAction() 16 | } 17 | 18 | class Decorator1 { 19 | + someAction() 20 | } 21 | 22 | class Decorator2 { 23 | + someAction() 24 | } 25 | 26 | Decorator2 *-- Decorator1 27 | Decorator1 *-- Core 28 | 29 | Interface <|.. Core 30 | Interface <|.. Decorator1 31 | Interface <|.. Decorator2 32 | 33 | @enduml 34 | -------------------------------------------------------------------------------- /ch_11/docs/dice_regex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/dice_regex.png -------------------------------------------------------------------------------- /ch_11/docs/dice_regex.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 6: Dice_Pattern regex' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | state "Group " as group_n { 11 | state "\d" as n_digit 12 | n_digit --> n_digit 13 | } 14 | state "Group " as group_d { 15 | state "\d" as d_digit 16 | d_digit --> d_digit 17 | 18 | } 19 | state "Group " as group_a { 20 | state "[dk+-]" as a_option 21 | state "\d" as a_digit 22 | a_option --> a_digit 23 | a_digit --> a_digit 24 | } 25 | 26 | [*] -right-> group_n 27 | group_n -right-> d 28 | [*] -right-> d 29 | 30 | d -right-> group_d 31 | 32 | group_d -right-> [*] 33 | group_d -right-> group_a 34 | 35 | group_a -left-> group_a 36 | group_a -right-> [*] 37 | 38 | @enduml 39 | -------------------------------------------------------------------------------- /ch_11/docs/examples.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming 2 | 3 | Chapter 11. Common Design Patterns 4 | 5 | # Decorator 6 | 7 | ```python 8 | >>> from math import factorial 9 | >>> def binom(n: int, k: int) -> int: 10 | ... return factorial(n) // (factorial(k) * factorial(n-k)) 11 | 12 | >>> f"6-card deals: {binom(52, 6):,d}" 13 | '6-card deals: 20,358,520' 14 | 15 | >>> from math import factorial 16 | >>> from functools import lru_cache 17 | 18 | >>> @lru_cache(64) 19 | ... def binom(n: int, k: int) -> int: 20 | ... return factorial(n) // (factorial(k) * factorial(n-k)) 21 | 22 | >>> f"6-card deals: {binom(52, 6):,d}" 23 | '6-card deals: 20,358,520' 24 | 25 | ``` 26 | 27 | # Observer 28 | 29 | # Strategy 30 | 31 | # Command 32 | 33 | # State 34 | 35 | # Singleton 36 | 37 | ```python 38 | 39 | >>> class OneOnly: 40 | ... _singleton = None 41 | ... def __new__(cls, *args, **kwargs): 42 | ... if not cls._singleton: 43 | ... cls._singleton = super().__new__(cls, *args, **kwargs) 44 | ... return cls._singleton 45 | 46 | >>> o1 = OneOnly() 47 | >>> o2 = OneOnly() 48 | >>> o1 == o2 49 | True 50 | >>> id(o1) == id(o2) 51 | True 52 | 53 | ``` 54 | -------------------------------------------------------------------------------- /ch_11/docs/langston_ant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/langston_ant.png -------------------------------------------------------------------------------- /ch_11/docs/langston_ant.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 6: The Langston Ant States' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | 11 | state MoveUp 12 | state MoveRight 13 | state MoveLeft 14 | state MoveDown 15 | 16 | 17 | [*] --> MoveUp : Start 18 | 19 | MoveUp --> MoveRight : on white 20 | MoveUp --> MoveLeft : on black 21 | 22 | MoveRight --> MoveDown : on white 23 | MoveRight --> MoveUp : on black 24 | 25 | MoveLeft --> MoveUp : on white 26 | MoveLeft --> MoveDown : on black 27 | 28 | MoveDown --> MoveLeft : on white 29 | MoveDown --> MoveRight : on black 30 | 31 | @enduml 32 | 33 | -------------------------------------------------------------------------------- /ch_11/docs/nmea_message.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/nmea_message.png -------------------------------------------------------------------------------- /ch_11/docs/nmea_message.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 8: NMEA Parser States' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | 11 | state "waiting for $" as start 12 | start : zero checksum 13 | 14 | state "header" 15 | header : update checksum 16 | 17 | state "body" 18 | body: update checksum 19 | 20 | state checksum 21 | 22 | state "cr and nl" as end 23 | end : validate 24 | 25 | 26 | [*] --> start : starting 27 | 28 | start --> header : on "$" 29 | 30 | header --> body : after 5 chars 31 | header --> start : on "$" 32 | 33 | body --> checksum : on "*" 34 | body --> start : on "$" 35 | 36 | checksum --> end : after 2 chars 37 | checksum --> start : on "$" 38 | 39 | end --> header : on "$" 40 | @enduml 41 | 42 | -------------------------------------------------------------------------------- /ch_11/docs/observer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/observer.png -------------------------------------------------------------------------------- /ch_11/docs/observer.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 3: The Observer Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | abstract class Observer { 11 | + observable: Observable 12 | + __call__() 13 | } 14 | 15 | abstract class Observable { 16 | + _observers: List[Observer] 17 | + attach(Observer) 18 | } 19 | 20 | class Core { 21 | + _observers: List[Observer] 22 | + attach(Observer) 23 | + someAction() 24 | } 25 | 26 | class Observer1 { 27 | + __call__() 28 | } 29 | 30 | class Observer2 { 31 | + __call__() 32 | } 33 | 34 | Observable <|-- Core 35 | 36 | 'alternative: Core::_observers *-- "*" Observer::observable' 37 | Observer::observable "*" --* Core::_observers 38 | 39 | Observer <|.. Observer1 40 | Observer <|.. Observer2 41 | 42 | @enduml 43 | -------------------------------------------------------------------------------- /ch_11/docs/singleton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/singleton.png -------------------------------------------------------------------------------- /ch_11/docs/singleton.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 9: The Singleton Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | class Singleton { 11 | - static instance: "Singleton" 12 | + get_instance(): "Singleton" 13 | } 14 | 15 | Singleton <--- Singleton 16 | 17 | @enduml 18 | -------------------------------------------------------------------------------- /ch_11/docs/state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/state.png -------------------------------------------------------------------------------- /ch_11/docs/state.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 7: The State Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | abstract class State { 11 | + process(core: Core) 12 | } 13 | 14 | class Core { 15 | + current_state: State 16 | + process(core: "Core") 17 | } 18 | 19 | class State1 { 20 | + process(core: Core) 21 | } 22 | 23 | class State2 { 24 | + process(core: Core) 25 | } 26 | 27 | class State3 { 28 | + process(core: Core) 29 | } 30 | 31 | 32 | Core::current_state *-- State 33 | 34 | State <|.. State1 35 | State <|.. State2 36 | State <|.. State3 37 | 38 | @enduml 39 | -------------------------------------------------------------------------------- /ch_11/docs/strategy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/strategy.png -------------------------------------------------------------------------------- /ch_11/docs/strategy.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 4: The Strategy Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | abstract class Strategy { 11 | + someAction() 12 | } 13 | 14 | class Core { 15 | + strategy: Strategy 16 | + someAction() 17 | } 18 | 19 | class Implementation1 { 20 | + someAction() 21 | } 22 | 23 | class Implementation2 { 24 | + someAction() 25 | } 26 | 27 | Core::strategy *-- Strategy 28 | 29 | Strategy <|.. Implementation1 30 | Strategy <|.. Implementation2 31 | 32 | @enduml 33 | -------------------------------------------------------------------------------- /ch_11/docs/strategy_design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_11/docs/strategy_design.png -------------------------------------------------------------------------------- /ch_11/docs/strategy_design.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 10: Hyperparameter and Distance classes' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | skinparam classAttributeIconSize 0 8 | 9 | class TrainingData { 10 | name : str 11 | uploaded : datetime 12 | tested : datetime 13 | load(source: Iterable) 14 | } 15 | class Hyperparameter { 16 | k: int 17 | quality: float 18 | distance : Distance 19 | classifier : Classifier 20 | test(): float 21 | classify(sample: Sample) : str 22 | } 23 | class Sample { 24 | sepal_length: float 25 | sepal_width: float 26 | petal_length: float 27 | petal_width: float 28 | } 29 | class Distance { 30 | distance(s1: Sample, s2: Sample) : float 31 | } 32 | class Chebyshev {} 33 | class Manhattan {} 34 | class Euclidean {} 35 | class Classifier { 36 | classify(k: int, d: Distance, train: Sample, unk: Sample) : float 37 | } 38 | class K_NN_Bisect {} 39 | class K_NN_HeapQ {} 40 | 41 | TrainingData *--> Hyperparameter : Tuning > 42 | TrainingData *--> Sample : Testing > 43 | TrainingData *--> Sample : Training > 44 | Hyperparameter ..> TrainingData 45 | Hyperparameter --> Distance : distance 46 | Hyperparameter --> Classifier : classifier 47 | Distance <|-- Chebyshev 48 | Distance <|-- Manhattan 49 | Distance <|-- Euclidean 50 | Classifier <|-- K_NN_Bisect 51 | Classifier <|-- K_NN_HeapQ 52 | Classifier --> Distance 53 | @enduml 54 | -------------------------------------------------------------------------------- /ch_11/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 11" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | license = {file = "LICENSE.txt"} 8 | keywords = ["k-NN", "object-oriented design"] 9 | authors = [ 10 | {email = "slott56@gmail.com"}, 11 | {name = "Steven F. Lott"} 12 | ] 13 | 14 | [tool.tox] 15 | legacy_tox_ini = """ 16 | [tox] 17 | minversion = 3.4.0 18 | skipsdist = True 19 | 20 | [testenv] 21 | deps = 22 | -rrequirements.txt 23 | black 24 | pytest==6.2.4 25 | tox==3.20.0 26 | mypy==0.812 27 | setenv = 28 | PYTHONPATH = {toxinidir}/src 29 | PYTHONHASHSEED = 0 30 | commands = 31 | black src 32 | python -m doctest --option ELLIPSIS docs/examples.md 33 | python -m doctest --option ELLIPSIS src/dice.py 34 | python -m doctest --option ELLIPSIS src/socket_client.py 35 | python -m doctest --option ELLIPSIS src/socket_server.py 36 | python -m doctest --option ELLIPSIS src/dice_server.py 37 | python -m doctest --option ELLIPSIS src/decorations.py 38 | python -m doctest --option ELLIPSIS src/inventory.py 39 | python -m doctest --option ELLIPSIS src/nmea_states.py 40 | python -m doctest --option ELLIPSIS src/nmea_states_2.py 41 | python -m doctest --option ELLIPSIS src/model.py 42 | python -m doctest --option ELLIPSIS bonus/zonk_score.py 43 | python -m pytest 44 | mypy --strict --show-error-codes src 45 | 46 | """ 47 | -------------------------------------------------------------------------------- /ch_11/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | pyyaml==5.3.1 5 | pillow==8.0.1 6 | -------------------------------------------------------------------------------- /ch_11/src/socket_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 11. Common Design Patterns 5 | """ 6 | import gzip 7 | import io 8 | import socket 9 | 10 | 11 | def main_zip() -> None: 12 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 13 | server.connect(("localhost", 2401)) 14 | count = input("How many rolls: ") or "1" 15 | pattern = input("Dice pattern nd6[dk+-]a: ") or "d6" 16 | command = f"Dice {count} {pattern}" 17 | server.send(command.encode("utf8")) 18 | zipped_response = io.BytesIO(server.recv(1024)) 19 | with gzip.GzipFile(fileobj=zipped_response) as zipfile: 20 | response = zipfile.read() 21 | print(response.decode("utf-8")) 22 | server.close() 23 | 24 | 25 | def main() -> None: 26 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 27 | server.connect(("localhost", 2401)) 28 | count = input("How many rolls: ") or "1" 29 | pattern = input("Dice pattern nd6[dk+-]a: ") or "d6" 30 | command = f"Dice {count} {pattern}" 31 | server.send(command.encode("utf8")) 32 | response = server.recv(1024) 33 | print(response.decode("utf-8")) 34 | server.close() 35 | 36 | 37 | if __name__ == "__main__": 38 | main_zip() 39 | -------------------------------------------------------------------------------- /ch_11/tests/test_dice.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 11. Common Design Patterns 5 | """ 6 | import random 7 | import dice 8 | from unittest.mock import Mock, call, sentinel 9 | from pytest import * 10 | 11 | @fixture 12 | def fixed_seed(): 13 | random.seed(42) 14 | 15 | def test_dice(fixed_seed): 16 | d_1 = dice.Dice.from_text("3d6+1") 17 | assert d_1.adjustments[0].n == 3 18 | assert d_1.adjustments[0].d == 6 19 | assert d_1.roll() == 9 20 | assert d_1.dice == [1, 1, 6] 21 | d_2 = dice.Dice.from_text("4d6k3") 22 | assert d_2.adjustments[0].n == 4 23 | assert d_2.adjustments[0].d == 6 24 | assert d_2.roll() == 7 25 | assert d_2.dice == [2, 2, 3] 26 | d_3 = dice.Dice.from_text("4d6d1") 27 | assert d_3.adjustments[0].n == 4 28 | assert d_3.adjustments[0].d == 6 29 | assert d_3.roll() == 14 30 | assert d_3.dice == [2, 6, 6] 31 | 32 | d_4 = dice.Dice(4, dice.D6, dice.Keep(3)) 33 | assert d_4.roll() == 11 34 | assert d_4.dice == [1, 5, 5] 35 | 36 | 37 | def test_dice_2(fixed_seed): 38 | d = dice.Dice2.from_text("3d6") 39 | assert d.n == 2 40 | assert d.d == 6 41 | assert d.roll() == 7 42 | 43 | 44 | def test_dice_roller(fixed_seed): 45 | response_1 = dice.dice_roller(b"Dice2 2 2d6") 46 | assert response_1 == b"Dice2 2 2d6 = [7, 7]" 47 | response_2 = dice.dice_roller(b"Dice 2 4d6d1") 48 | assert response_2 == b"Dice 2 4d6d1 = [7, 18]" 49 | with raises(ValueError): 50 | dice.dice_roller(b"nothing recognizable") 51 | with raises(KeyError): 52 | dice.dice_roller(b"bad 2 2d6") 53 | -------------------------------------------------------------------------------- /ch_11/tests/test_socket_client.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 11. Common Design Patterns 5 | """ 6 | import socket_client 7 | from unittest.mock import Mock, call, sentinel 8 | from pytest import * 9 | 10 | @fixture 11 | def mock_socket(monkeypatch): 12 | mock_instance = Mock( 13 | connect=Mock(), 14 | send=Mock(), 15 | recv=Mock(return_value=b'server reply'), 16 | close=Mock() 17 | ) 18 | socket_module = Mock( 19 | socket=Mock(return_value=mock_instance), 20 | AF_INET=sentinel.AF_INET, 21 | SOCK_STREAM=sentinel.SOCK_STREAM 22 | ) 23 | monkeypatch.setattr(socket_client, 'socket', socket_module) 24 | return socket_module 25 | 26 | @fixture 27 | def mock_input(monkeypatch): 28 | input_function = Mock( 29 | side_effect=["42", "4d6d1"] 30 | ) 31 | monkeypatch.setitem( 32 | socket_client.__builtins__, 'input', 33 | input_function 34 | ) 35 | 36 | def test_client(mock_socket, mock_input, capsys): 37 | socket_client.main() 38 | out, err = capsys.readouterr() 39 | assert out == "server reply\n" 40 | assert mock_socket.socket.mock_calls == [ 41 | call(sentinel.AF_INET, sentinel.SOCK_STREAM) 42 | ] 43 | instance = mock_socket.socket.return_value 44 | assert instance.connect.mock_calls == [ 45 | call(('localhost', 2401)) 46 | ] 47 | assert instance.send.mock_calls == [ 48 | call(b'Dice 42 4d6d1') 49 | ] 50 | assert instance.recv.mock_calls == [ 51 | call(1024) 52 | ] 53 | assert instance.close.mock_calls == [ 54 | call() 55 | ] 56 | -------------------------------------------------------------------------------- /ch_12/README.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming, 4th ed. 2 | 3 | Chapter 12. Advanced Python design patterns. 4 | 5 | - The Adapter pattern 6 | - The Facade pattern 7 | - Lazy initialization and the Flyweight pattern 8 | - The Abstract Factory pattern 9 | - The Composition pattern 10 | - The Template pattern 11 | -------------------------------------------------------------------------------- /ch_12/docs/abstract_factory-classes-2a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/abstract_factory-classes-2a.png -------------------------------------------------------------------------------- /ch_12/docs/abstract_factory-classes-2b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/abstract_factory-classes-2b.png -------------------------------------------------------------------------------- /ch_12/docs/abstract_factory-classes-2c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/abstract_factory-classes-2c.png -------------------------------------------------------------------------------- /ch_12/docs/abstract_factory-classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/abstract_factory-classes.png -------------------------------------------------------------------------------- /ch_12/docs/abstract_factory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/abstract_factory.png -------------------------------------------------------------------------------- /ch_12/docs/adapter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/adapter.png -------------------------------------------------------------------------------- /ch_12/docs/adapter.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: The Adapter Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | class Client { 11 | } 12 | 13 | class Adapter { 14 | + load_data(path) 15 | } 16 | 17 | class Implementation { 18 | + read_raw_data(path) 19 | + parse_raw_data(row) 20 | + create_useful_object(mapping) 21 | } 22 | 23 | Client --> Adapter 24 | 25 | Adapter --> Implementation 26 | 27 | @enduml 28 | -------------------------------------------------------------------------------- /ch_12/docs/composite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/composite.png -------------------------------------------------------------------------------- /ch_12/docs/composite.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 12: The Composite Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | abstract class Component { 11 | + some_action() 12 | } 13 | 14 | class Leaf { 15 | + some_action() 16 | } 17 | 18 | class Composite { 19 | + children: List[Component] 20 | + some_action() 21 | } 22 | 23 | Component <|-- Leaf 24 | Component <|-- Composite 25 | Composite::children *-- "*" Component : "Contains" 26 | 27 | @enduml 28 | 29 | @startuml coposite-objects.png 30 | 'figure 12: The Composite Pattern' 31 | skinparam monochrome true 32 | skinparam handwritten false 33 | skinparam shadowing false 34 | skinparam classAttributeIconSize 0 35 | hide class circle 36 | hide abstract circle 37 | 38 | object Root 39 | object "Leaf" as l1 40 | object "Composite" as c2 41 | object "Composite" as c3 42 | 43 | Root *-- l1 44 | Root *-- c2 45 | Root *-- c3 46 | 47 | object "Leaf" as c2_l4 48 | object "Composite" as c2_c4 49 | object "Leaf" as c2_l5 50 | 51 | c2 *-- c2_l4 52 | c2 *-- c2_c4 53 | c2 *-- c2_l5 54 | 55 | object "Leaf" as c2_c4_l6 56 | object "Leaf" as c2_c4_l7 57 | object "Leaf" as c2_c4_l8 58 | object "Leaf" as c2_c4_l9 59 | 60 | c2_c4 *-- c2_c4_l6 61 | c2_c4 *-- c2_c4_l7 62 | c2_c4 *-- c2_c4_l8 63 | c2_c4 *-- c2_c4_l9 64 | 65 | object "Leaf" as c3_l10 66 | object "Composite" as c3_c5 67 | 68 | c3 *-- c3_l10 69 | c3 *-- c3_c5 70 | 71 | object "Leaf" as c3_c5_l11 72 | object "Leaf" as c3_c5_l12 73 | 74 | c3_c5 *-- c3_c5_l11 75 | c3_c5 *-- c3_c5_l12 76 | 77 | @enduml 78 | -------------------------------------------------------------------------------- /ch_12/docs/coposite-objects.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/coposite-objects.png -------------------------------------------------------------------------------- /ch_12/docs/facade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/facade.png -------------------------------------------------------------------------------- /ch_12/docs/facade.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 12: The Facade Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | class Client {} 11 | 12 | class Facade { 13 | + simple_task() 14 | + other_simple_task() 15 | } 16 | 17 | package "Big System" { 18 | 19 | class ComplexComponent { 20 | + some_part() 21 | } 22 | class AnotherComponent { 23 | + another_part() 24 | } 25 | 26 | } 27 | 28 | Client -- Facade 29 | 30 | Facade -- ComplexComponent 31 | Facade -- AnotherComponent 32 | 33 | @enduml 34 | -------------------------------------------------------------------------------- /ch_12/docs/factory-modules.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/factory-modules.png -------------------------------------------------------------------------------- /ch_12/docs/flyweight-circular.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/flyweight-circular.png -------------------------------------------------------------------------------- /ch_12/docs/flyweight-classes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/flyweight-classes.png -------------------------------------------------------------------------------- /ch_12/docs/flyweight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/flyweight.png -------------------------------------------------------------------------------- /ch_12/docs/logical_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/logical_view_1.png -------------------------------------------------------------------------------- /ch_12/docs/logical_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 4: Rethinking classification' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class Sample { 9 | sepal_length: float 10 | sepal_width: float 11 | petal_length: float 12 | petal_width: float 13 | } 14 | class KnownSample { 15 | sample: Sample 16 | species: str 17 | } 18 | note bottom: Created at load time 19 | 20 | class UnknownSample { 21 | sample: Sample 22 | } 23 | note bottom: Created by user 24 | 25 | class TrainingKnownSample #Silver { 26 | sample: KnownSample 27 | } 28 | class TestingKnownSample #Silver { 29 | sample: KnownSample 30 | } 31 | note "Created by partition()" as partition 32 | partition .. TrainingKnownSample 33 | partition .. TestingKnownSample 34 | 35 | KnownSample *--> Sample 36 | 37 | UnknownSample *--> Sample 38 | 39 | TrainingKnownSample *--> KnownSample 40 | 41 | TestingKnownSample *--> KnownSample 42 | 43 | class ClassifiedKnownSample { 44 | sample: KnownSample 45 | classification: str 46 | } 47 | note top: Created by classifier while testing 48 | 49 | class ClassifiedUnknownSample { 50 | sample: UnknownSample 51 | classification: str 52 | } 53 | note top: Created by classifier for user 54 | 55 | ClassifiedKnownSample *--> KnownSample 56 | ClassifiedKnownSample ..> TestingKnownSample : based on 57 | ClassifiedUnknownSample *--> UnknownSample 58 | @enduml 59 | -------------------------------------------------------------------------------- /ch_12/docs/logical_view_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/logical_view_2.png -------------------------------------------------------------------------------- /ch_12/docs/logical_view_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 4: Frozen Dataclasses' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | hide class circle 7 | 8 | class TrainingData { 9 | name: str 10 | uploaded: datetime.datetime 11 | tested: datetime.datetime 12 | load(source: Iterable) 13 | } 14 | class Sample <> { 15 | sepal_length: float 16 | sepal_width: float 17 | petal_length: float 18 | petal_width: float 19 | } 20 | class KnownSample <> { 21 | species: str 22 | } 23 | class TrainingKnownSample { 24 | sample: KnownSample 25 | } 26 | class TestingKnownSample { 27 | sample: KnownSample 28 | classification: str 29 | } 30 | class UnknownSample { 31 | sample: Sample 32 | classification: str 33 | } 34 | class Hyperparameter { 35 | k: int 36 | quality: float 37 | classify(s: UnknownSample): str 38 | test(t: List[TestingKnownSample]): int 39 | } 40 | class "List[TestingKnownSample]" 41 | class "List[TrainingKnownSample]" 42 | class "List[Hyperparameter]" 43 | TrainingData *---> "List[TrainingKnownSample]" : training > 44 | TrainingData *---> "List[TestingKnownSample]" : testing > 45 | TrainingData *---> "List[Hyperparameter]" : tuning > 46 | "List[TrainingKnownSample]" o--> TrainingKnownSample 47 | "List[TestingKnownSample]" o--> TestingKnownSample 48 | "List[Hyperparameter]" *--> Hyperparameter 49 | Sample <|- KnownSample 50 | TestingKnownSample *--> KnownSample 51 | TrainingKnownSample *--> KnownSample 52 | UnknownSample *--> Sample 53 | Hyperparameter ...> TrainingData : data > 54 | @enduml 55 | -------------------------------------------------------------------------------- /ch_12/docs/template.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/docs/template.png -------------------------------------------------------------------------------- /ch_12/docs/template.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: The Template Pattern' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | class Client { 11 | + something_interesting() 12 | } 13 | 14 | class TemplateCommand { 15 | + do_process() 16 | + step_1() 17 | + {abstract} step_2() 18 | + step_3() 19 | + step_4() 20 | } 21 | 22 | class Implementation_Choice { 23 | + step_2() 24 | } 25 | 26 | class Another_Choice { 27 | + step_2() 28 | } 29 | 30 | 31 | Client -- TemplateCommand 32 | 33 | TemplateCommand <|-- Implementation_Choice 34 | TemplateCommand <|-- Another_Choice 35 | 36 | @enduml 37 | -------------------------------------------------------------------------------- /ch_12/gross_sales_20210126.csv: -------------------------------------------------------------------------------- 1 | salesperson,total sales 2 | Hannah,86000 3 | Jason,20000 4 | Tim,25000 5 | -------------------------------------------------------------------------------- /ch_12/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 12" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | license = {file = "LICENSE.txt"} 8 | keywords = ["k-NN", "object-oriented design"] 9 | authors = [ 10 | {email = "slott56@gmail.com"}, 11 | {name = "Steven F. Lott"} 12 | ] 13 | 14 | [tool.tox] 15 | legacy_tox_ini = """ 16 | [tox] 17 | minversion = 3.4.0 18 | skipsdist = True 19 | 20 | [testenv] 21 | deps = 22 | -rrequirements.txt 23 | black 24 | pytest==6.2.4 25 | tox==3.20.0 26 | mypy==0.812 27 | 28 | setenv = 29 | PYTHONPATH = {toxinidir}/src 30 | commands = 31 | black src 32 | # python -m doctest --option ELLIPSIS docs/examples.md 33 | python -m doctest --option ELLIPSIS src/age_computation.py 34 | python -m doctest --option ELLIPSIS src/images.py 35 | python -m doctest --option ELLIPSIS src/gps_messages.py 36 | python -m doctest --option ELLIPSIS src/gps_message_slots.py 37 | python -m doctest --option ELLIPSIS src/card_games.py 38 | python -m doctest --option ELLIPSIS src/ordered_list.py 39 | python -m doctest --option ELLIPSIS src/file_system.py 40 | python -m pytest 41 | mypy --strict --show-error-codes src 42 | 43 | """ 44 | -------------------------------------------------------------------------------- /ch_12/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | pyyaml==5.3.1 5 | pillow==8.0.1 6 | -------------------------------------------------------------------------------- /ch_12/sales.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_12/sales.db -------------------------------------------------------------------------------- /ch_12/scrap/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Example 4 | 5 | 6 |

Some Listy things

7 |
    8 |
      9 |
        10 |
          11 |
        1. The only item
        2. 12 |
        13 |
      1. In the ALPHA list
      2. 14 |
      15 |
    1. In the numbered list
    2. 16 |
    17 |
  1. In the default list
  2. 18 |
19 | 20 | 21 | -------------------------------------------------------------------------------- /ch_12/scrap/tree.dot: -------------------------------------------------------------------------------- 1 | digraph tree { 2 | rankdir=LR; 3 | ratio=auto; 4 | nodesep=.125; 5 | n140569483558624 -> n140569483557280; 6 | n140569483557280 -> n140569483556992; 7 | n140569483556992 [shape=box,label="ex1.py"]; 8 | n140569483557280 [label = "src"]; 9 | n140569483558624 -> n140569483558672; 10 | n140569483558672 -> n140569483556848; 11 | n140569483556848 [shape=box,label="test1.py"]; 12 | n140569483558672 [label = "tests"]; 13 | n140569483558624 [label = "Tree"]; 14 | } 15 | -------------------------------------------------------------------------------- /ch_13/README.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming, 4th ed. 2 | 3 | Chapter 13. Testing Object-Oriented Programs. 4 | 5 | - Why test? 6 | - Unit testing 7 | - Testing with pytest 8 | - Imitating expensive objects 9 | - How much testing is enough? 10 | - Multi-environment testing with Tox 11 | - Case study 12 | -------------------------------------------------------------------------------- /ch_13/checksums.txt: -------------------------------------------------------------------------------- 1 | bezdekIris.data 0fed2a99db77ec533a62dc66894d3ec6df3b58b6a8f3cf4a6b47e4086b7f97dc 2 | bezdekIris.yaml 9df91c3a411a15a004d1690dba5c2d101566134845534e264eb6096f00a68763 3 | bezdekIris.ndjson ed8ac1eeb637bddcc5a67ca54cfc0ee27cb6023a32647e720d739929e3ac355b 4 | bezdekIris.json 5a16f1a7e9d918db79f25b19adef5ea8a1811b3a16669bee3283d82a7ddba025 5 | -------------------------------------------------------------------------------- /ch_13/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "classifier" 3 | version = "2021.4.0" 4 | description = "Python 3 Object-Oriented Programming, 4th ed., Chapter 13" 5 | readme = "README.md" 6 | requires-python = ">=3.9" 7 | keywords = [ "k-NN", "object-oriented design",] 8 | [[project.authors]] 9 | email = "slott56@gmail.com" 10 | 11 | [[project.authors]] 12 | name = "Steven F. Lott" 13 | 14 | [project.license] 15 | file = "LICENSE.txt" 16 | 17 | [tool.tox] 18 | legacy_tox_ini = """ 19 | [tox] 20 | minversion = 3.4.0 21 | skipsdist = True 22 | envlist = testenv 23 | 24 | [testenv] 25 | deps = 26 | -rrequirements.txt 27 | black 28 | pytest==6.2.4 29 | tox==3.20.0 30 | mypy==0.812 31 | setenv = 32 | PYTHONPATH = {toxinidir}/src 33 | commands = 34 | black src 35 | black tests 36 | python -m doctest --option ELLIPSIS src/remote_logging_app.py 37 | python -m doctest --option ELLIPSIS src/checksum_writer.py 38 | python -m doctest --option ELLIPSIS src/log_catcher.py 39 | python -m doctest --option ELLIPSIS src/model.py 40 | python -m doctest --option ELLIPSIS src/flight_status_redis.py 41 | python -m doctest --option ELLIPSIS src/stats.py 42 | python -m doctest --option ELLIPSIS docs/case_study_13.md 43 | python -m doctest --option ELLIPSIS bonus/vigenere_cipher.py 44 | python -m pytest --log-cli-level=INFO 45 | mypy --strict --show-error-codes --python-version 3.9 tests src 46 | 47 | [testenv:coverage] 48 | deps = 49 | -rrequirements.txt 50 | pytest==6.2.4 51 | coverage==5.4 52 | commands = 53 | coverage run --omit='.tox/*' -m pytest tests/test_coverage.py 54 | coverage report 55 | coverage report -m 56 | 57 | """ 58 | -------------------------------------------------------------------------------- /ch_13/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | pyyaml==5.3.1 5 | pillow==8.0.1 6 | redis==3.5.3 7 | sympy==1.7.1 8 | -------------------------------------------------------------------------------- /ch_13/src/checksum_writer.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import tarfile 7 | from pathlib import Path 8 | import hashlib 9 | 10 | 11 | def checksum(source: Path, checksum_path: Path) -> None: 12 | if checksum_path.exists(): 13 | backup = checksum_path.with_stem(f"(old) {checksum_path.stem}") 14 | backup.write_text(checksum_path.read_text()) 15 | checksum = hashlib.sha256(source.read_bytes()) 16 | checksum_path.write_text(f"{source.name} {checksum.hexdigest()}\n") 17 | 18 | 19 | class FileChecksum: 20 | def __init__(self, source: Path) -> None: 21 | self.source = source 22 | self.checksum = hashlib.sha256(source.read_bytes()) 23 | -------------------------------------------------------------------------------- /ch_13/src/log_catcher.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import json 7 | from pathlib import Path 8 | import socketserver 9 | from typing import TextIO 10 | import pickle 11 | import struct 12 | import sys 13 | 14 | 15 | class LogDataCatcher(socketserver.BaseRequestHandler): 16 | log_file: TextIO 17 | count: int = 0 18 | size_format = ">L" 19 | size_bytes = struct.calcsize(size_format) 20 | 21 | def handle(self) -> None: 22 | size_header_bytes = self.request.recv(LogDataCatcher.size_bytes) 23 | while size_header_bytes: 24 | payload_size = struct.unpack(LogDataCatcher.size_format, size_header_bytes) 25 | print(f"{size_header_bytes=} {payload_size=}", file=sys.stderr) 26 | payload_bytes = self.request.recv(payload_size[0]) 27 | print(f"{len(payload_bytes)=}", file=sys.stderr) 28 | payload = pickle.loads(payload_bytes) 29 | LogDataCatcher.count += 1 30 | print(f"{self.client_address[0]} {LogDataCatcher.count} {payload!r}") 31 | self.log_file.write(json.dumps(payload) + "\n") 32 | try: 33 | size_header_bytes = self.request.recv(LogDataCatcher.size_bytes) 34 | except (ConnectionResetError, BrokenPipeError): 35 | break 36 | 37 | 38 | def main(host: str, port: int, target: Path) -> None: 39 | with target.open("w") as unified_log: 40 | LogDataCatcher.log_file = unified_log 41 | with socketserver.TCPServer((host, port), LogDataCatcher) as server: 42 | server.serve_forever() 43 | 44 | 45 | if __name__ == "__main__": 46 | HOST, PORT = "localhost", 18842 47 | main(HOST, PORT, Path("one.log")) 48 | -------------------------------------------------------------------------------- /ch_13/src/remote_logging_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import logging 7 | import logging.handlers 8 | import time 9 | import sys 10 | from math import factorial 11 | 12 | logger = logging.getLogger("app") 13 | 14 | 15 | def work(i: int) -> int: 16 | logger.info("Factorial %d", i) 17 | f = factorial(i) 18 | logger.info("Factorial(%d) = %d", i, f) 19 | return f 20 | 21 | 22 | if __name__ == "__main__": 23 | HOST, PORT = "localhost", 18842 24 | socket_handler = logging.handlers.SocketHandler(HOST, PORT) 25 | stream_handler = logging.StreamHandler(sys.stderr) 26 | logging.basicConfig(handlers=[socket_handler, stream_handler], level=logging.INFO) 27 | 28 | for i in range(10): 29 | work(i) 30 | 31 | logging.shutdown() 32 | -------------------------------------------------------------------------------- /ch_13/src/stats.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | from __future__ import annotations 7 | import collections 8 | from typing import DefaultDict, List, Optional 9 | 10 | 11 | class StatsList(List[Optional[float]]): 12 | """Stats with None objects rejected""" 13 | 14 | def mean(self) -> float: 15 | clean = list(filter(None, self)) 16 | return sum(clean) / len(clean) 17 | 18 | def median(self) -> float: 19 | clean = list(filter(None, self)) 20 | if len(clean) % 2: 21 | return clean[len(clean) // 2] 22 | else: 23 | idx = len(clean) // 2 24 | return (clean[idx] + clean[idx - 1]) / 2 25 | 26 | def mode(self) -> list[float]: 27 | freqs: DefaultDict[float, int] = collections.defaultdict(int) 28 | for item in filter(None, self): 29 | freqs[item] += 1 30 | mode_freq = max(freqs.values()) 31 | modes = [item for item, value in freqs.items() if value == mode_freq] 32 | return modes 33 | -------------------------------------------------------------------------------- /ch_13/tests/test_38_39.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import pytest 7 | import sys 8 | 9 | 10 | @pytest.mark.skipif( 11 | sys.version_info < (3, 9), reason="requires 3.9, Path.removeprefix()" 12 | ) 13 | def test_feature_python39() -> None: 14 | file_name = "(old) myfile.dat" 15 | assert file_name.removeprefix("(old) ") == "myfile.dat" 16 | -------------------------------------------------------------------------------- /ch_13/tests/test_averages.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | from __future__ import annotations 7 | import unittest 8 | from typing import Optional 9 | 10 | 11 | def average(seq: list[Optional[float]]) -> float: 12 | """Average of non-None values""" 13 | total, count = 0.0, 0 14 | for v in filter(None, seq): 15 | total += v 16 | count += 1 17 | return total / count 18 | 19 | 20 | class TestAverage(unittest.TestCase): 21 | def test_zero(self) -> None: 22 | self.assertRaises(ZeroDivisionError, average, []) 23 | 24 | def test_with_zero(self) -> None: 25 | with self.assertRaises(ZeroDivisionError): 26 | average([]) 27 | 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /ch_13/tests/test_check_numbers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import unittest 7 | 8 | 9 | class CheckNumbers(unittest.TestCase): 10 | def test_int_float(self) -> None: 11 | self.assertEqual(1, 1.0) 12 | 13 | @unittest.expectedFailure 14 | def test_str_float(self) -> None: 15 | """Remove the @unittest.expectedFailure decorator to see a failing test.""" 16 | self.assertEqual(1, "1") 17 | 18 | 19 | if __name__ == "__main__": 20 | unittest.main() 21 | -------------------------------------------------------------------------------- /ch_13/tests/test_coverage.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import pytest 7 | from stats import StatsList 8 | 9 | 10 | @pytest.fixture 11 | def valid_stats() -> StatsList: 12 | return StatsList([1, 2, 2, 3, 3, 4]) 13 | 14 | 15 | def test_mean(valid_stats: StatsList) -> None: 16 | assert valid_stats.mean() == 2.5 17 | -------------------------------------------------------------------------------- /ch_13/tests/test_logging_app.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | from __future__ import annotations 7 | import logging 8 | from pathlib import Path 9 | import pytest 10 | import subprocess 11 | import signal 12 | import sys 13 | import time 14 | import remote_logging_app 15 | from typing import Iterator, Any 16 | 17 | # This is an integration test, and works by starting a log_catcher 18 | # We can call it the "Catcher in the Sky". 19 | 20 | 21 | @pytest.fixture(scope="session") 22 | def log_catcher() -> Iterator[None]: 23 | server_path = Path("src") / "log_catcher.py" 24 | print(f"Starting server {server_path}") 25 | p = subprocess.Popen( 26 | [sys.executable, str(server_path)], 27 | stdout=subprocess.PIPE, 28 | stderr=subprocess.STDOUT, 29 | text=True, 30 | ) 31 | time.sleep(0.25) 32 | yield 33 | p.terminate() 34 | p.wait() 35 | if p.stdout: 36 | print(p.stdout.read()) 37 | assert ( 38 | p.returncode == 1 if sys.platform == "win32" else -signal.SIGTERM.value 39 | ), f"Error in watcher, returncode={p.returncode}" 40 | 41 | 42 | @pytest.fixture 43 | def logging_config() -> Iterator[None]: 44 | HOST, PORT = "localhost", 18842 45 | socket_handler = logging.handlers.SocketHandler(HOST, PORT) 46 | remote_logging_app.logger.addHandler(socket_handler) 47 | yield 48 | socket_handler.close() 49 | remote_logging_app.logger.removeHandler(socket_handler) 50 | 51 | 52 | def test_1(log_catcher: None, logging_config: None) -> None: 53 | for i in range(10): 54 | r = remote_logging_app.work(i) 55 | 56 | 57 | def test_2(log_catcher: None, logging_config: None) -> None: 58 | for i in range(1, 10): 59 | r = remote_logging_app.work(52 * i) 60 | -------------------------------------------------------------------------------- /ch_13/tests/test_pythonista.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import sys 7 | import pytest 8 | 9 | 10 | def test_simple_skip() -> None: 11 | if sys.platform != "ios": 12 | pytest.skip("Test works only on Pythonista for ios") 13 | 14 | import location # type: ignore [import] 15 | 16 | img = location.render_map_snapshot(36.8508, -76.2859) 17 | assert img is not None 18 | -------------------------------------------------------------------------------- /ch_13/tests/test_setup_teardown.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | from __future__ import annotations 7 | from typing import Any, Callable 8 | 9 | 10 | def setup_module(module: Any) -> None: 11 | print(f"setting up MODULE {module.__name__}") 12 | 13 | 14 | def teardown_module(module: Any) -> None: 15 | print(f"tearing down MODULE {module.__name__}") 16 | 17 | 18 | def test_a_function() -> None: 19 | print("RUNNING TEST FUNCTION") 20 | 21 | 22 | class BaseTest: 23 | @classmethod 24 | def setup_class(cls: type["BaseTest"]) -> None: 25 | print(f"setting up CLASS {cls.__name__}") 26 | 27 | @classmethod 28 | def teardown_class(cls: type["BaseTest"]) -> None: 29 | print(f"tearing down CLASS {cls.__name__}\n") 30 | 31 | def setup_method(self, method: Callable[[], None]) -> None: 32 | print(f"setting up METHOD {method.__name__}") 33 | 34 | def teardown_method(self, method: Callable[[], None]) -> None: 35 | print(f"tearing down METHOD {method.__name__}") 36 | 37 | 38 | class TestClass1(BaseTest): 39 | def test_method_1(self) -> None: 40 | print("RUNNING METHOD 1-1") 41 | 42 | def test_method_2(self) -> None: 43 | print("RUNNING METHOD 1-2") 44 | 45 | 46 | class TestClass2(BaseTest): 47 | def test_method_1(self) -> None: 48 | print("RUNNING METHOD 2-1") 49 | 50 | def test_method_2(self) -> None: 51 | print("RUNNING METHOD 2-2") 52 | -------------------------------------------------------------------------------- /ch_13/tests/test_skipping.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import unittest 7 | import sys 8 | 9 | 10 | class SkipTests(unittest.TestCase): 11 | @unittest.expectedFailure 12 | def test_fails(self) -> None: 13 | self.assertEqual(False, True) 14 | 15 | @unittest.skip("Test is useless") 16 | def test_skip(self) -> None: 17 | self.assertEqual(False, True) 18 | 19 | @unittest.expectedFailure # Remove this to see the effect of version number tests. 20 | @unittest.skipIf(sys.version_info.minor == 8, "broken on 3.8") 21 | def test_skipif(self) -> None: 22 | self.assertEqual(False, True) 23 | 24 | @unittest.skipUnless(sys.platform.startswith("linux"), "broken unless on linux") 25 | def test_skipunless(self) -> None: 26 | self.assertEqual(False, True) 27 | 28 | 29 | if __name__ == "__main__": 30 | unittest.main() 31 | -------------------------------------------------------------------------------- /ch_13/tests/test_stats.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | from stats import StatsList 7 | import unittest 8 | 9 | 10 | class TestValidInputs(unittest.TestCase): 11 | def setUp(self) -> None: 12 | self.stats = StatsList([1, 2, 2, 3, 3, 4]) 13 | 14 | def test_mean(self) -> None: 15 | self.assertEqual(self.stats.mean(), 2.5) 16 | 17 | def test_median(self) -> None: 18 | self.assertEqual(self.stats.median(), 2.5) 19 | self.stats.append(4) 20 | self.assertEqual(self.stats.median(), 3) 21 | 22 | def test_mode(self) -> None: 23 | self.assertEqual(self.stats.mode(), [2, 3]) 24 | self.stats.remove(2) 25 | self.assertEqual(self.stats.mode(), [3]) 26 | 27 | 28 | if __name__ == "__main__": 29 | unittest.main() 30 | -------------------------------------------------------------------------------- /ch_13/tests/test_stats_pytest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import pytest 7 | from stats import StatsList 8 | 9 | 10 | @pytest.fixture 11 | def valid_stats() -> StatsList: 12 | return StatsList([1, 2, 2, 3, 3, 4]) 13 | 14 | 15 | def test_mean(valid_stats: StatsList) -> None: 16 | assert valid_stats.mean() == 2.5 17 | 18 | 19 | def test_median(valid_stats: StatsList) -> None: 20 | assert valid_stats.median() == 2.5 21 | valid_stats.append(4) 22 | assert valid_stats.median() == 3 23 | 24 | 25 | def test_mode(valid_stats: StatsList) -> None: 26 | assert valid_stats.mode() == [2, 3] 27 | valid_stats.remove(2) 28 | assert valid_stats.mode() == [3] 29 | -------------------------------------------------------------------------------- /ch_13/tests/test_with_pytest.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 13. Testing Object-Oriented Programs. 5 | """ 6 | import pytest 7 | 8 | 9 | class TestNumbers: 10 | def test_int_float(self) -> None: 11 | assert 1 == 1.0 12 | 13 | @pytest.mark.skip("expected to fail") 14 | def test_int_str(self) -> None: 15 | assert 1 == "1" # type: ignore [comparison-overlap] 16 | -------------------------------------------------------------------------------- /ch_14/README.md: -------------------------------------------------------------------------------- 1 | # Python 3 Object-Oriented Programming, 4th ed. 2 | 3 | Chapter 14. Concurrency 4 | 5 | 6 | 1. Threads 7 | 2. Multiprocessing 8 | 3. Futures 9 | 4. AsyncIO 10 | -------------------------------------------------------------------------------- /ch_14/benches/time_to_write.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | 6 | How long does a "simple" write take? 7 | """ 8 | import timeit 9 | from textwrap import dedent 10 | import random 11 | import string 12 | from pathlib import Path 13 | 14 | repeat_count = 5_000_000 15 | for message_size in (128, 256, 512, 1024): 16 | log_message = ''.join(random.choice(string.printable) for _ in range(message_size)) 17 | t = timeit.timeit( 18 | stmt="some_file.write(log_message)", 19 | setup=dedent( 20 | """ 21 | some_path = Path("temp_file.log") 22 | some_file = some_path.open('w') 23 | """ 24 | ), 25 | number=repeat_count, 26 | globals=globals() 27 | ) 28 | print(f"Time to write {message_size} character line {t/repeat_count*1_000_000:.6f}μs") 29 | -------------------------------------------------------------------------------- /ch_14/docs/fig_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/docs/fig_1.png -------------------------------------------------------------------------------- /ch_14/docs/fig_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 1: Log Catcher' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | node "App Server" { 11 | [application 1] 12 | [application 2] 13 | } 14 | 15 | cloud "Elsewhere" { 16 | node "Logging Server" { 17 | (socket) 18 | (socket) - [Log Catcher] : < reads 19 | database "LogFile" 20 | } 21 | } 22 | 23 | [application 1] ..> (socket) : "SocketHandler" 24 | [application 2] ..> (socket) : "SocketHandler" 25 | 26 | [Log Catcher] -> LogFile : writes > 27 | 28 | @enduml 29 | -------------------------------------------------------------------------------- /ch_14/docs/fig_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/docs/fig_2.png -------------------------------------------------------------------------------- /ch_14/docs/fig_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: Dining Philosophers - Circular' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | 7 | [Philosopher 0] 8 | (Fork 0) 9 | 10 | [Philosopher 1] 11 | (Fork 1) 12 | 13 | [Philosopher 2] 14 | (Fork 2) 15 | 16 | [Philosopher 3] 17 | (Fork 3) 18 | 19 | [Philosopher 4] 20 | (Fork 4) 21 | 22 | (Fork 0) <.. [Philosopher 0] 23 | [Philosopher 0] .d.> (Fork 1) 24 | [Philosopher 0] -l-> (Spaghetti) 25 | 26 | (Fork 1) <.d. [Philosopher 1] 27 | [Philosopher 1] .d.> (Fork 2) 28 | [Philosopher 1] -l-> (Spaghetti) 29 | 30 | (Fork 2) <.l. [Philosopher 2] 31 | [Philosopher 2] .u.> (Fork 3) 32 | [Philosopher 2] -u-> (Spaghetti) 33 | 34 | (Fork 3) <.u. [Philosopher 3] 35 | [Philosopher 3] .u.> (Fork 4) 36 | [Philosopher 3] -r-> (Spaghetti) 37 | 38 | (Fork 4) <.u. [Philosopher 4] 39 | [Philosopher 4] .d.> (Fork 0) 40 | [Philosopher 4] -r-> (Spaghetti) 41 | 42 | @enduml 43 | -------------------------------------------------------------------------------- /ch_14/docs/fig_2h.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/docs/fig_2h.png -------------------------------------------------------------------------------- /ch_14/docs/fig_2h.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 2: Dining Philosophers - Horizontal' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | skinparam shadowing false 6 | 'skinparam classAttributeIconSize 0 7 | 'hide class circle 8 | 'hide abstract circle 9 | 10 | (Fork 0) 11 | (Fork 1) 12 | (Fork 2) 13 | (Fork 3) 14 | (Fork 4) 15 | 16 | [Philosopher 0] 17 | [Philosopher 1] 18 | [Philosopher 2] 19 | [Philosopher 3] 20 | [Philosopher 4] 21 | 22 | 23 | (Fork 0) <.. [Philosopher 0] 24 | (Fork 1) <.. [Philosopher 0] 25 | [Philosopher 0] -- (Spaghetti) 26 | 27 | (Fork 0) <.. [Philosopher 1] 28 | (Fork 2) <.. [Philosopher 1] 29 | [Philosopher 1] -- (Spaghetti) 30 | 31 | (Fork 1) <.. [Philosopher 2] 32 | (Fork 3) <.. [Philosopher 2] 33 | [Philosopher 2] -- (Spaghetti) 34 | 35 | (Fork 2) <.. [Philosopher 3] 36 | (Fork 4) <.. [Philosopher 3] 37 | [Philosopher 3] -- (Spaghetti) 38 | 39 | (Fork 3) <.. [Philosopher 4] 40 | (Fork 4) <.. [Philosopher 4] 41 | [Philosopher 4] -- (Spaghetti) 42 | 43 | 44 | @enduml 45 | -------------------------------------------------------------------------------- /ch_14/docs/fig_3_4.uml: -------------------------------------------------------------------------------- 1 | 2 | @startuml rle_class.png 3 | 'figure 4: Classes' 4 | skinparam monochrome true 5 | skinparam shadowing false 6 | skinparam classAttributeIconSize 0 7 | hide class circle 8 | hide abstract circle 9 | 10 | class RLERun { 11 | byte_state(byte) 12 | emit() 13 | } 14 | class Replicate { 15 | count: int # -127 <= count < 0 16 | value: byte 17 | } 18 | class Literal { 19 | count: int # 0 <= count < 128 20 | value: list[byte] 21 | } 22 | 23 | RLERun <|-- Replicate 24 | RLERun <|-- Literal 25 | 26 | @enduml 27 | 28 | @startuml rle_state.png 29 | 'figure 3: States' 30 | skinparam monochrome true 31 | skinparam shadowing false 32 | skinparam classAttributeIconSize 0 33 | 34 | [*] --> Literal : first byte 35 | Literal -> Literal : [last != next byte] 36 | Literal : [len == 128] emit Literal 37 | Literal : [exit] emit Literal 38 | 39 | Literal --> Replicate : [last == next byte]\nremove last\nseed Replicate 40 | Literal --> [*] 41 | Replicate --> Literal : [last != next byte] 42 | Replicate --> [*] 43 | 44 | Replicate -> Replicate : [last == next byte] 45 | Replicate : [len == 128] emit Replicate 46 | Replicate : [exit] emit Replicate 47 | @enduml 48 | -------------------------------------------------------------------------------- /ch_14/docs/logical_view_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/docs/logical_view_1.png -------------------------------------------------------------------------------- /ch_14/docs/logical_view_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'figure 4: Rethinking classification' 3 | skinparam monochrome true 4 | skinparam handwritten false 5 | hide class circle 6 | skinparam shadowing false 7 | 8 | class Sample { 9 | sepal_length: float 10 | sepal_width: float 11 | petal_length: float 12 | petal_width: float 13 | } 14 | class KnownSample { 15 | sample: Sample 16 | species: str 17 | } 18 | 19 | class TrainingKnownSample { 20 | sample: KnownSample 21 | } 22 | class TestingKnownSample { 23 | sample: KnownSample 24 | } 25 | 26 | KnownSample *--> Sample 27 | 28 | TrainingKnownSample *--> KnownSample 29 | 30 | TestingKnownSample *--> KnownSample 31 | 32 | class TrainingData { 33 | test: List 34 | training: List 35 | load() 36 | } 37 | 38 | TrainingData::test *--> TestingKnownSample 39 | TrainingData::training *--> TrainingKnownSample 40 | 41 | 42 | class ClassifiedKnownSample { 43 | sample: KnownSample 44 | classification: str 45 | } 46 | 47 | ClassifiedKnownSample *--> KnownSample 48 | ClassifiedKnownSample ...> TestingKnownSample : based on 49 | 50 | class Hyperparameter { 51 | training_data: TrainingData 52 | k: int 53 | algorithm: Distance 54 | classify(Sample): str 55 | test(): float 56 | } 57 | 58 | Hyperparameter::training_data --> TrainingData 59 | Hyperparameter::classify --> ClassifiedKnownSample : creates 60 | 61 | 62 | @enduml 63 | -------------------------------------------------------------------------------- /ch_14/docs/rle_class.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/docs/rle_class.png -------------------------------------------------------------------------------- /ch_14/docs/rle_state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/docs/rle_state.png -------------------------------------------------------------------------------- /ch_14/images/README.rst: -------------------------------------------------------------------------------- 1 | ###### 2 | Images 3 | ###### 4 | 5 | For the antigravity image, run 6 | 7 | :: 8 | 9 | import antigravity 10 | 11 | The image link is https://imgs.xkcd.com/comics/python.png 12 | 13 | Other images that seem relevant to this chapter: 14 | 15 | https://imgs.xkcd.com/comics/exploits_of_a_mom.png 16 | 17 | https://imgs.xkcd.com/comics/compiling.png 18 | 19 | https://imgs.xkcd.com/comics/sandwich.png 20 | 21 | The ``row`` and ``bricks`` images were produced 22 | by functions in the ``make_images.py`` application 23 | 24 | The ``bricks_1`` and ``bricks_2`` images can 25 | be reproduced with PlantUML and Graphviz. This is 26 | a bit more complex to set up. 27 | 28 | The ``make_images.py`` application will download or 29 | create all the images in this directory. 30 | 31 | If you want to experiment with images 32 | created by PlantUML and the Graphviz ``dot`` language, 33 | the following setup is required: 34 | 35 | 1. Download Java Runtime (JRE) for your platform. 36 | https://www.java.com/en/download/manual.jsp 37 | 38 | 2. Download the ``plantuml.jar`` and put into your conda environment ``share`` directory. 39 | https://plantuml.com/download 40 | 41 | 3. Use ``conda install graphiz`` to create the ``dot`` application in your conda environment. 42 | 43 | 4. If necessary, update the ``make_images.py`` script with environment name and locations 44 | -------------------------------------------------------------------------------- /ch_14/images/bricks.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks.bmp -------------------------------------------------------------------------------- /ch_14/images/bricks.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks.rle -------------------------------------------------------------------------------- /ch_14/images/bricks_1.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks_1.bmp -------------------------------------------------------------------------------- /ch_14/images/bricks_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks_1.png -------------------------------------------------------------------------------- /ch_14/images/bricks_1.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks_1.rle -------------------------------------------------------------------------------- /ch_14/images/bricks_1.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'Bricks 1' 3 | 4 | skinparam monochrome true 5 | skinparam handwritten false 6 | skinparam shadowing false 7 | hide empty members 8 | 9 | object one #white;line:black 10 | object two #black 11 | object three #black 12 | object four #white;line:black 13 | 14 | one -r[hidden]- two 15 | three -r[hidden]- four 16 | one -d[hidden]- three 17 | @enduml 18 | -------------------------------------------------------------------------------- /ch_14/images/bricks_2.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks_2.bmp -------------------------------------------------------------------------------- /ch_14/images/bricks_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks_2.png -------------------------------------------------------------------------------- /ch_14/images/bricks_2.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/bricks_2.rle -------------------------------------------------------------------------------- /ch_14/images/bricks_2.uml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 'Bricks 2' 3 | digraph G { 4 | bricks [ 5 | shape=plaintext 6 | label=< 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
  
  
17 | > 18 | ] 19 | } 20 | @enduml 21 | -------------------------------------------------------------------------------- /ch_14/images/compiling.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/compiling.bmp -------------------------------------------------------------------------------- /ch_14/images/compiling.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/compiling.rle -------------------------------------------------------------------------------- /ch_14/images/exploits_of_a_mom.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/exploits_of_a_mom.bmp -------------------------------------------------------------------------------- /ch_14/images/exploits_of_a_mom.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/exploits_of_a_mom.rle -------------------------------------------------------------------------------- /ch_14/images/large.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/large.bmp -------------------------------------------------------------------------------- /ch_14/images/large.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/large.rle -------------------------------------------------------------------------------- /ch_14/images/python.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/python.bmp -------------------------------------------------------------------------------- /ch_14/images/python.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/python.rle -------------------------------------------------------------------------------- /ch_14/images/row.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/row.bmp -------------------------------------------------------------------------------- /ch_14/images/row.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/row.rle -------------------------------------------------------------------------------- /ch_14/images/sandwich.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/sandwich.bmp -------------------------------------------------------------------------------- /ch_14/images/sandwich.rle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Python-Object-Oriented-Programming---4th-edition/b2676ba220d444174335dd24f73e5d53e1f4f1e9/ch_14/images/sandwich.rle -------------------------------------------------------------------------------- /ch_14/requirements.txt: -------------------------------------------------------------------------------- 1 | 2 | beautifulsoup4==4.9.1 3 | jsonschema==3.2.0 4 | httpx==0.18.1 5 | pytest-httpx==0.12.0 6 | pyyaml==5.3.1 7 | pillow==8.0.1 8 | -------------------------------------------------------------------------------- /ch_14/src/async_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | import asyncio 7 | import random 8 | 9 | 10 | async def random_sleep(counter: float) -> None: 11 | delay = random.random() * 5 12 | print(f"{counter} sleeps for {delay:.2f} seconds") 13 | await asyncio.sleep(delay) 14 | print(f"{counter} awakens, refreshed") 15 | 16 | 17 | async def sleepers(how_many: int = 5) -> None: 18 | print(f"Creating {how_many} tasks") 19 | tasks = [asyncio.create_task(random_sleep(i)) for i in range(how_many)] 20 | print(f"Waiting for {how_many} tasks") 21 | await asyncio.gather(*tasks) 22 | 23 | 24 | if __name__ == "__main__": 25 | asyncio.run(sleepers(5)) 26 | print("Done with the sleepers") 27 | -------------------------------------------------------------------------------- /ch_14/src/demo_log_catcher.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from __future__ import annotations 7 | import argparse 8 | import signal 9 | import subprocess 10 | import time 11 | import sys 12 | 13 | 14 | def main(clients: int = 10) -> None: 15 | if sys.platform == "win32": 16 | platform_flags = subprocess.CREATE_NEW_PROCESS_GROUP 17 | else: 18 | platform_flags = 0 19 | server = subprocess.Popen( 20 | ["python", "src/log_catcher.py"], creationflags=platform_flags 21 | ) 22 | # Give the server 100 ms to get started. 23 | time.sleep(0.100) 24 | # Make sure it didn't crash because of an earlier test 25 | assert server.poll() is None, f"Server didn't start." 26 | workers = [ 27 | subprocess.Popen(["python", "src/remote_logging_app.py"]) 28 | for i in range(clients) 29 | ] 30 | print(f"***{clients} WORKERS STARTED***") 31 | for w in workers: 32 | w.wait() 33 | print(f"worker {w.pid} finished {w.returncode}") 34 | print(f"***{clients} WORKERS FINISHED***") 35 | # Give the server 100 ms to finish processing 36 | time.sleep(0.100) 37 | 38 | if sys.platform == "win32": 39 | server.send_signal(signal.CTRL_BREAK_EVENT) 40 | else: 41 | server.terminate() 42 | server.wait() 43 | print(f"server finished {server.returncode}") 44 | 45 | 46 | def get_options(argv: list[str] = sys.argv[1:]) -> argparse.Namespace: 47 | parser = argparse.ArgumentParser() 48 | parser.add_argument("workers", nargs="?", type=int, default=1) 49 | options = parser.parse_args() 50 | return options 51 | 52 | 53 | if __name__ == "__main__": 54 | options = get_options() 55 | main(options.workers) 56 | -------------------------------------------------------------------------------- /ch_14/src/demo_signals.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from time import sleep 7 | from subprocess import Popen 8 | from os import kill 9 | from signal import SIGTERM, SIGINT 10 | from sys import argv 11 | 12 | 13 | def child() -> None: 14 | print("Child Started") 15 | try: 16 | sleep(600) 17 | except Exception as ex: 18 | print(f"Child {ex}") 19 | raise 20 | 21 | 22 | def parent() -> None: 23 | child_process = Popen( 24 | ["python", "src/demo_signals.py", "child"], 25 | shell=False, 26 | ) 27 | print(f"Child: {child_process.poll()}") 28 | print(f"Child: {child_process.pid}") 29 | sleep(2) 30 | print(f"Signaling Child...") 31 | kill(child_process.pid, SIGINT) 32 | 33 | 34 | if __name__ == "__main__": 35 | if len(argv) > 1: 36 | option = argv[1] 37 | else: 38 | option = "parent" 39 | print(option) 40 | function = {"parent": parent, "child": child}[option] 41 | function() 42 | -------------------------------------------------------------------------------- /ch_14/src/food_truck.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | import math 7 | import random 8 | from threading import Thread, Lock 9 | import time 10 | 11 | THE_ORDERS = [ 12 | "Reuben", 13 | "Ham and Cheese", 14 | "Monte Cristo", 15 | "Tuna Melt", 16 | "Cuban", 17 | "Grilled Cheese", 18 | "French Dip", 19 | "BLT", 20 | ] 21 | 22 | 23 | class Chef(Thread): 24 | def __init__(self, name: str) -> None: 25 | super().__init__(name=name) 26 | self.total = 0 27 | 28 | def get_order(self) -> None: 29 | self.order = THE_ORDERS.pop(0) 30 | 31 | def prepare(self) -> None: 32 | """Simulate doing a lot of work with a BIG computation""" 33 | start = time.monotonic() 34 | target = start + 1 + random.random() 35 | for i in range(1_000_000_000): 36 | self.total += math.factorial(i) 37 | if time.monotonic() >= target: 38 | break 39 | print(f"{time.monotonic():.3f} {self.name} made {self.order}") 40 | 41 | def run(self) -> None: 42 | while True: 43 | try: 44 | self.get_order() 45 | self.prepare() 46 | except IndexError: 47 | break # No more orders 48 | 49 | 50 | Mo = Chef("Michael") 51 | Constantine = Chef("Constantine") 52 | 53 | if __name__ == "__main__": 54 | random.seed(42) 55 | Mo.start() 56 | Constantine.start() 57 | -------------------------------------------------------------------------------- /ch_14/src/philosophers.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from __future__ import annotations 7 | import asyncio 8 | import collections 9 | import random 10 | from typing import List, DefaultDict, Iterator 11 | 12 | FORKS: List[asyncio.Lock] 13 | 14 | 15 | async def philosopher(id: int, footman: asyncio.Semaphore) -> tuple[int, float, float]: 16 | async with footman: 17 | async with FORKS[id], FORKS[(id + 1) % len(FORKS)]: 18 | eat_time = 1 + random.random() 19 | print(f"{id} eating") 20 | await asyncio.sleep(eat_time) 21 | think_time = 1 + random.random() 22 | print(f"{id} philosophizing") 23 | await asyncio.sleep(think_time) 24 | return id, eat_time, think_time 25 | 26 | 27 | async def main(faculty: int = 5, servings: int = 5) -> None: 28 | global FORKS 29 | FORKS = [asyncio.Lock() for i in range(faculty)] 30 | footman = asyncio.BoundedSemaphore(faculty - 1) 31 | for serving in range(servings): 32 | department = (philosopher(p, footman) for p in range(faculty)) 33 | results = await asyncio.gather(*department) 34 | print(results) 35 | 36 | 37 | if __name__ == "__main__": 38 | asyncio.run(main()) 39 | -------------------------------------------------------------------------------- /ch_14/src/prime_factor.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from __future__ import annotations 7 | from math import sqrt, ceil 8 | import random 9 | from multiprocessing.pool import Pool 10 | 11 | 12 | def prime_factors(value: int) -> list[int]: 13 | """ 14 | >>> set(prime_factors(42)) 15 | {2, 3, 7} 16 | >>> set(prime_factors(97)) 17 | {97} 18 | """ 19 | if value in {2, 3}: 20 | return [value] 21 | factors: list[int] = [] 22 | for divisor in range(2, ceil(sqrt(value)) + 1): 23 | quotient, remainder = divmod(value, divisor) 24 | if not remainder: 25 | factors.extend(prime_factors(divisor)) 26 | factors.extend(prime_factors(quotient)) 27 | break 28 | else: 29 | factors = [value] 30 | return factors 31 | 32 | 33 | if __name__ == "__main__": 34 | to_factor = [random.randint(100_000_000, 1_000_000_000) for i in range(40_960)] 35 | with Pool() as pool: 36 | results = pool.map(prime_factors, to_factor) 37 | primes = [ 38 | value for value, factor_list in zip(to_factor, results) if len(factor_list) == 1 39 | ] 40 | print(f"9-digit primes {primes}") 41 | -------------------------------------------------------------------------------- /ch_14/src/processes_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from multiprocessing import Process, cpu_count 7 | from threading import Thread 8 | import time 9 | import os 10 | 11 | 12 | class MuchCPU(Process): 13 | def run(self) -> None: 14 | print(f"OS PID {os.getpid()}") 15 | 16 | s = sum(2 * i + 1 for i in range(100_000_000)) 17 | 18 | 19 | class MoreCPU(Thread): 20 | def run(self) -> None: 21 | print(f"OS PID {os.getpid()}") 22 | 23 | s = sum(2 * i + 1 for i in range(100_000_000)) 24 | 25 | 26 | if __name__ == "__main__": 27 | # workers = [MuchCPU() for f in range(cpu_count())] 28 | workers = [MoreCPU() for f in range(cpu_count())] 29 | 30 | t = time.perf_counter() 31 | for p in workers: 32 | p.start() 33 | for p in workers: 34 | p.join() 35 | print(f"work took {time.perf_counter() - t:.3f} seconds") 36 | -------------------------------------------------------------------------------- /ch_14/src/threads_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from threading import Thread 7 | 8 | 9 | class InputReader(Thread): 10 | def run(self) -> None: 11 | self.line_of_text = input() 12 | 13 | 14 | if __name__ == "__main__": 15 | print("Enter some text and press enter: ") 16 | thread = InputReader() 17 | # thread.start() # Concurrent 18 | thread.run() # Sequential 19 | 20 | count = result = 1 21 | while thread.is_alive(): 22 | result = count * count 23 | count += 1 24 | 25 | print(f"calculated squares up to {count} * {count} = {result}") 26 | print(f"while you typed {thread.line_of_text!r}") 27 | -------------------------------------------------------------------------------- /ch_14/tests/test_aync_1.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from pytest import * 7 | from unittest.mock import AsyncMock, Mock, call 8 | import async_1 9 | import asyncio 10 | 11 | 12 | @fixture 13 | def mock_random(monkeypatch): 14 | random = Mock( 15 | random=Mock(return_value=0.5) 16 | ) 17 | monkeypatch.setattr(async_1, 'random', random) 18 | return random 19 | 20 | @fixture 21 | def mock_sleep(monkeypatch): 22 | sleep = AsyncMock() 23 | monkeypatch.setattr(asyncio, 'sleep', sleep) 24 | return sleep 25 | 26 | def test_random_sleep(mock_random, mock_sleep, capsys): 27 | asyncio.run(async_1.random_sleep(42)) 28 | assert mock_random.random.mock_calls == [call()] 29 | mock_sleep.assert_awaited() 30 | mock_sleep.assert_called_once_with(2.5) 31 | out, err = capsys.readouterr() 32 | assert out.splitlines() == [ 33 | '42 sleeps for 2.50 seconds', 34 | '42 awakens, refreshed' 35 | ] 36 | 37 | @fixture 38 | def mock_random_sleep(monkeypatch): 39 | random_sleep = AsyncMock() 40 | monkeypatch.setattr(async_1, 'random_sleep', random_sleep) 41 | return random_sleep 42 | 43 | def test_sleepers(mock_random_sleep, capsys): 44 | asyncio.run(async_1.sleepers(2)) 45 | mock_random_sleep.mock_calls == [ 46 | call(0), 47 | call(1) 48 | ] 49 | out, err = capsys.readouterr() 50 | assert out.splitlines() == [ 51 | 'Creating 2 tasks', 52 | 'Waiting for 2 tasks' 53 | ] 54 | 55 | -------------------------------------------------------------------------------- /ch_14/tests/test_log_catcher.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | import asyncio 7 | import pickle 8 | from pytest import * 9 | import struct 10 | from unittest.mock import AsyncMock, Mock, call 11 | import log_catcher 12 | 13 | @fixture 14 | def mock_target(monkeypatch): 15 | open_file = Mock() 16 | log_catcher.TARGET = open_file 17 | return open_file 18 | 19 | def test_log_writer(mock_target, capsys): 20 | payload = pickle.dumps("message") 21 | asyncio.run(log_catcher.log_writer(payload)) 22 | assert mock_target.write.mock_calls == [ 23 | call('"message"'), 24 | call('\n') 25 | ] 26 | 27 | 28 | @fixture 29 | def mock_log_writer(monkeypatch): 30 | log_writer = AsyncMock() 31 | monkeypatch.setattr(log_catcher, 'log_writer', log_writer) 32 | return log_writer 33 | 34 | @fixture 35 | def mock_stream(): 36 | mock_socket = Mock( 37 | getpeername=Mock(return_value=['127.0.0.1', 12342]) 38 | ) 39 | payload = pickle.dumps("message") 40 | size = struct.pack(">L", len(payload)) 41 | stream = Mock( 42 | read=AsyncMock(side_effect=[size, payload, None]), 43 | get_extra_info=Mock(return_value=mock_socket) 44 | ) 45 | return payload, stream 46 | 47 | 48 | def test_log_catcher(mock_log_writer, mock_stream): 49 | payload, stream = mock_stream 50 | asyncio.run(log_catcher.log_catcher(stream, stream)) 51 | # Depends on len(payload) 52 | assert stream.read.mock_calls == [call(4), call(22), call(4)] 53 | mock_log_writer.assert_awaited_with(payload) 54 | -------------------------------------------------------------------------------- /ch_14/tests/test_weather_async.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | import asyncio 7 | from httpx import URL 8 | from pytest import * 9 | from unittest.mock import Mock, AsyncMock, mock_open, call 10 | import weather_async 11 | 12 | def test_zone(): 13 | eb = weather_async.Zone("Eastern Bay", "ANZ540", "073540") 14 | assert eb.forecast_url == "https://tgftp.nws.noaa.gov/data/forecasts/marine/coastal/an/anz540.txt" 15 | 16 | @fixture 17 | def marine_wx(): 18 | z = weather_async.Zone("Eastern Bay", "ANZ540", "073540") 19 | return weather_async.MarineWX(z) 20 | 21 | @fixture 22 | def mock_urlopen(monkeypatch): 23 | urlopen = mock_open( 24 | read_data="""Heading\n...Advisory...\n.DAY...details.\n""".encode("utf-8") 25 | ) 26 | monkeypatch.setattr(weather_async, 'urlopen', urlopen) 27 | return urlopen 28 | 29 | @fixture 30 | def mock_httpx_client(httpx_mock): 31 | httpx_mock.add_response( 32 | method="GET", 33 | data="""Heading\n...Advisory...\n.DAY...details.\n""" 34 | ) 35 | return httpx_mock 36 | 37 | def test_marine_wx(marine_wx, mock_httpx_client, httpx_mock): 38 | async def when(): 39 | return await marine_wx.run() 40 | result_0 = asyncio.run(when()) 41 | assert marine_wx.advisory == "Advisory" 42 | # mock_urlopen.assert_called_once_with( 43 | # marine_wx.zone.forecast_url 44 | # ) 45 | assert ( 46 | httpx_mock.get_request().url == URL('https://tgftp.nws.noaa.gov/data/forecasts/marine/coastal/an/anz540.txt') 47 | ) 48 | -------------------------------------------------------------------------------- /ch_14/tests/test_weather_threads.py: -------------------------------------------------------------------------------- 1 | """ 2 | Python 3 Object-Oriented Programming 3 | 4 | Chapter 14. Concurrency 5 | """ 6 | from pytest import * 7 | from unittest.mock import Mock, mock_open, call 8 | import weather_threads 9 | 10 | def test_station(): 11 | halifax = weather_threads.Station("NS", "s0000318") 12 | assert halifax.path == "/NS/s0000318_e.xml" 13 | assert halifax.url == "https://dd.weather.gc.ca/citypage_weather/xml/NS/s0000318_e.xml" 14 | 15 | @fixture 16 | def temp_getter(): 17 | return weather_threads.TempGetter("Halifax") 18 | 19 | @fixture 20 | def mock_urlopen(monkeypatch): 21 | urlopen = mock_open( 22 | read_data="""42""" 23 | ) 24 | monkeypatch.setattr(weather_threads, 'urlopen', urlopen) 25 | return urlopen 26 | 27 | def test_temp_getter(temp_getter, mock_urlopen): 28 | temp_getter.run() 29 | assert temp_getter.temperature == "42" 30 | mock_urlopen.assert_called_once_with( 31 | 'https://dd.weather.gc.ca/citypage_weather/xml/NS/s0000318_e.xml' 32 | ) 33 | -------------------------------------------------------------------------------- /env.sh: -------------------------------------------------------------------------------- 1 | export PATH=$PATH:/Users/slott/miniconda3/envs/py39/bin:/Users/slott/miniconda3/envs/CaseStudy/bin 2 | export GRAPHVIZ_DOT=/Users/slott/miniconda3/envs/CaseStudy/bin/dot 3 | -------------------------------------------------------------------------------- /python3.8.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM python3.8.bat 3 | @C:\ProgramData\Miniconda3\envs\tox-py38\python.exe %* 4 | -------------------------------------------------------------------------------- /python3.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM python3.bat 3 | @C:\ProgramData\Miniconda3\envs\tox-py38\python.exe %* 4 | -------------------------------------------------------------------------------- /tools/tree.py: -------------------------------------------------------------------------------- 1 | """ 2 | Recursive directory tree display. 3 | Uses asciitree. 4 | """ 5 | import argparse 6 | from pathlib import Path 7 | import sys 8 | from asciitree import LeftAligned # type: ignore[import] 9 | from textwrap import indent 10 | from typing import List, Dict, Any 11 | 12 | 13 | def display(tree: Dict[str, Any], prefix: int = 0) -> None: 14 | for name in tree: 15 | if tree[name]: # Non-empty: i.e., a directory 16 | print(indent(name, prefix * " ")) 17 | display(tree[name], prefix + 4) 18 | else: 19 | print(indent(name, prefix * " ")) 20 | 21 | 22 | def build(root: Path) -> Dict[str, Any]: 23 | tree: Dict[str, Dict[str, Any]] = {} 24 | for path in root.glob("**/*"): 25 | if any(n.startswith(".") for n in path.parts): 26 | continue 27 | if any(n.startswith("__") and n.endswith("__") for n in path.parts): 28 | continue 29 | here = tree 30 | for name in path.parts[:-1]: 31 | here = here.setdefault(f"{name}/", {}) 32 | if path.is_dir(): 33 | here.setdefault(f"{path.parts[-1]}/", {}) 34 | else: 35 | here.setdefault(path.parts[-1], {}) 36 | return tree 37 | 38 | 39 | def main(argv: List[str] = sys.argv[1:]) -> None: 40 | parser = argparse.ArgumentParser() 41 | parser.add_argument("directory", nargs=1, type=Path) 42 | options = parser.parse_args(argv) 43 | for directory in options.directory: 44 | tree = build(directory) 45 | tr = LeftAligned() 46 | print(tr(tree)) 47 | # display(tree) 48 | 49 | 50 | if __name__ == "__main__": 51 | main() 52 | --------------------------------------------------------------------------------