├── .gitattributes
├── Chapter6
├── 6_01_empty_object.py
├── 6_15_stupid_adding_integer.py
├── 6_04_dict_stocks.py
├── 6_09_counter_frequency.py
├── 6_03_named_tuple.py
├── 6_20_collect_remaining_links.py
├── 6_21_print_collected_links.py
├── case_study_serve
│ ├── yoga.html
│ ├── contact.html
│ ├── blog.html
│ ├── index.html
│ ├── hobbies.html
│ └── esme.html
├── 6_08_defaultdict_custom_function.py
├── 6_07_defaultdict_frequency.py
├── 6_06_setdefault_frequency.py
├── 6_02_pass_tuple_to_function.py
├── 6_14_oop_pairs.py
├── 6_18_normalize_url.py
├── 6_09_list_tuple_frequency.py
├── 6_09_counter_poll.py
├── 6_12_set_operations.py
├── 6_11_song_artist_set.py
├── 6_17_link_parser.py
├── 6_13_set_operations2.py
├── 6_05_random_key_dict.py
├── 6_10_object_comparison.py
├── 6_16_dictsorted.py
├── 6_19_visited_links_sets.py
├── 6_22_dict_link_collector.py
├── link_collector.py
└── 6_23_queue_link_collector.py
├── Chapter8
├── case_study_input
│ ├── header.html
│ ├── footer.html
│ ├── menu.html
│ ├── context.json
│ └── main.html
├── 8_07_unlabelled_kw.py
├── 8_17_bytearray_replace.py
├── 8_18_bytearray_index.py
├── 8_02_format_empty.py
├── 8_14_format_datetime.py
├── 8_03_format_position.py
├── 8_15_encode_bytes.py
├── 8_04_format_some_positions_broken.py
├── 8_01_string_creation.py
├── 8_11_no_format.py
├── 8_16_decode_unicode.py
├── 8_23.1_basic_regex.py
├── 8_12_currency_format.py
├── 8_05_brace_escape.py
├── 8_23.3_match_group.py
├── 8_23_stringio.py
├── 8_23.2_regex_generic.py
├── 8_13_tabular.py
├── 8_06_format_kw_args.py
├── 8_08_tuple_dict_format.py
├── 8_09_tuple_in_dict_format.py
├── 8_24_basic_pickling.py
├── 8_10_object_formatting.py
├── 8_26_json_objects.py
├── 8_25_state_pickling.py
├── 8_27_template_boilerplate.py
├── 8_28_template_process.py
└── 8_29_template_processer_complete.py
├── Chapter7
├── 7_05_read_file.py
├── 7_07_with.py
├── addresses.db
├── 7_06_write.py
├── timer.pyc
├── 7_04_tdf_contact.txt
├── 7_20_link_downloader_vararg.py
├── 7_36_enter_exit.py
├── 7_18_bad_kw_default.py
├── __pycache__
│ └── timer.cpython-34.pyc
├── 7_03.1_zip_to_enumerate.py
├── 7_02_enumerate_line_numbers.py
├── 7_03_enumerate_max_min.py
├── 7_27_add_function_to_object.py
├── 7_33_mailing_list_get_emails.py
├── 7_34_send_mailing.py
├── 7_32_mailing_list_defaultdict_set.py
├── 7_19_link_downloader.py
├── 7_23_unpacking_arguments.py
├── 7_08_context_manager.py
├── 7_05_tdf_processor.py
├── 7_21_kwarg_options.py
├── 7_01_reversible_objects.py
├── 7_28_callable_repeat.py
├── 7_29_send_email.py
├── 7_35_load_save.py
├── 7_24_function_object.py
├── 7_30_send_email_dict_headers.py
├── timer.py
├── 7_25_timer.py
├── 7_22_all_arguments.py
├── 7_26_timer_test.py
└── mailing_list.py
├── Chapter12
├── 12_06_simplestpytest.py
├── dump.rdb
├── stats.pyc
├── 12_09_funcargs.pyc
├── 12_07_class_pytest.pyc
├── 12_12_pytest_echo.pyc
├── 12_06_simplestpytest.pyc
├── 12_08_setup_teardown.pyc
├── 12_10_funcarg_finalizer.pyc
├── 12_15_pytest_skipifmark.pyc
├── 12_13_pytest_simple_skip.pyc
├── 12_14_pytest_importorskip.pyc
├── 12_15_pytest_skipifmark.py
├── casestudy
│ ├── vigenere_cipher.pyc
│ ├── test_vigenere_cipher.pyc
│ ├── __pycache__
│ │ ├── vigenere_cipher.cpython-34.pyc
│ │ └── test_vigenere_cipher.cpython-34-PYTEST.pyc
│ ├── vigenere_cipher.py
│ ├── vigenere_cipher1.py
│ └── test_vigenere_cipher.py
├── 12_07_class_pytest.py
├── __pycache__
│ ├── stats.cpython-34.pyc
│ └── test_mock.cpython-34-PYTEST.pyc
├── 12_14_pytest_importorskip.py
├── 12_13_pytest_simple_skip.py
├── 12_30_coverage_unittest.py
├── 12_01_simplest_unittest.py
├── 12_11_echo_server.py
├── 12_02_assertraises_python.py
├── 12_09_funcargs.py
├── 12_10_funcarg_finalizer.py
├── 12_04_test_stats.py
├── test_mock.py
├── stats.py
├── 12_03_stats.py
├── 12_05_skipping_tests.py
├── 12_17_mock_redis.py
├── 12_12_pytest_echo.py
├── 12_08_setup_teardown.py
└── 12_16_mock_flightstatus.py
├── Chapter3
├── 3_01_inheriting_from_object.py
├── 3_17.1_abc_container.py
├── 3_26_rudimentary_agent.py
├── 3_08_send_mail.py
├── 3_10_friend_address_holder.py
├── 3_09_send_mail_multi.py
├── 3_02_simple_contact_class_to_inherit_from.py
├── 3_24_house_rental.py
├── 3_05_dictionary_long_name.py
├── 3_20_validation_function.py
├── 3_11_friend_multi.py
├── 3_17_ducktype_flac.py
├── 3_07_friend_overrides_init_super.py
├── 3_06_friend_overrides_init.py
├── 3_03_contact_inherit_supplier.py
├── 3_27_type_map.py
├── 3_17.2_abc_media.py
├── 3_04_contact_list_inheritance.py
├── 3_16_polymorphic_audio.py
├── 3_15_friend_multi_super.py
├── 3_25_remaining_subclasses.py
├── 3_14_contrived_diamond_super.py
├── 3_18_property.py
├── 3_12_contrived_diamond.py
├── 3_28_add_property.py
├── 3_21_apartment_nice_prompt.py
├── 3_22_house.py
├── 3_19_apartment_ugly_prompt.py
└── 3_23_purchase_and_rental.py
├── Chapter10
├── 10_10_decorator_syntax.py
├── Benson.jpg
├── TILED.jpg
├── sales.db
├── 10_03_simple_client.py
├── 10_06_calling_decorated_sockets.py
├── 10_16_singleton_using_new.py
├── 10_02_simple_socket.py
├── 10_19_template_abstract_noimple.py
├── 10_14_simple_xml_to_parse.xml
├── 10_04_logging_decorator.py
├── 10_09_logging_decorator.py
├── 10_11_observer_core.py
├── 10_05_gzip_decorator.py
├── 10_18_create_database_for_template.py
├── 10_20_template_abstract_implemented.py
├── 10_12_observer_observing.py
├── 10_21_template_concretes.py
├── 10_13_strategy_tile.py
├── 10_15_xml_states.py
└── 10_17_xml_singletonstates.py
├── Chapter5
├── 5_09_property_decorator_get.py
├── 5_27_string_property.py
├── 5_16_average_property.py
├── zip_processor.pyc
├── 5_12_property_decorator_get_set.py
├── 5_13_read_only_setattr.py
├── 5_14_read_only_getattribute.py
├── 5_05_python_pretty_as_python.py
├── 5_04_pytho_ugly_as_java.py
├── 5_01_distances_no_objects.py
├── 5_06_setting_name_in_method.py
├── 5_15_cache_getter.py
├── 5_07_setting_name_property.py
├── 5_11_property_decorator_arguments.py
├── 5_08_property_arguments.py
├── 5_20_scaleimage_inheritance.py
├── 5_28_Character_class.py
├── 5_24_most_basic_document.py
├── 5_02_distances_by_object.py
├── 5_25_document_cursor.py
├── 5_03_object_polygon_init.py
├── zip_processor.py
├── 5_18_zipprocessor.py
├── 5_19_zipreplace_inheritance.py
├── 5_26_document_using_cursor.py
├── 5_17_zipsearch.py
├── Document.py
└── 5_29_document_with_character.py
├── Chapter4
├── auth.pyc
├── 4_10_defining_an_exception.py
├── 4_08_catch_as_keyword.py
├── __pycache__
│ └── auth.cpython-34.pyc
├── 4_02_exception_quits.py
├── 4_05_catch_specific_exception.py
├── 4_01_even_integers.py
├── 4_04_try_except.py
├── 4_11_exception_with_custom_args.py
├── 04_03_method_calls_excepting.py
├── 4_06_catch_multiple_exceptions.py
├── 4_07_catch_multiple_different.py
├── 4_12_handle_custom_exception.py
├── 4_09_finally_and_else.py
├── 4_13_branching_vs_exceptions.py
├── 4_15_inventory_handling.py
├── 4_14_inventory_mock_object.py
├── 4_16_auth_user.py
├── 4_17_authenticator.py
├── 4_18_login.py
└── 4_20_test_auth.py
├── Chapter9
├── 9_08_list_comp_exclude.py
├── 9_20_basic_count_coroutine.py
├── 9_07_list_comp_converter.py
├── 9_06_for_loop_converter.py
├── 9_23_generate_colors.py
├── 9_15_log_delete_warnings_loop.py
├── 9_13_log_processor.py
├── 9_24_color_distance.py
├── 9_14_log_delete_warning_expression.py
├── 9_22_load_dataset.py
├── 9_26_write_results.py
├── 9_09_tdf_list_comp.py
├── 9_17_log_delete_warnings_generator.py
├── 9_18_log_delete_warnings_yield_from.py
├── example.log
├── 9_12_log_file.log
├── 9_01_iterator.py
├── 9_10_set_comprehension.py
├── 9_11_dict_comprehension.py
├── EXAMPLE_LOG.log
├── 9_16_log_delete_warnings_object.py
├── kivy_color_classifier
│ ├── main.py
│ └── colorclassifier.kv
├── 9_21_kernel_log.py
├── kivy_color_checker
│ ├── colorchecker.kv
│ └── main.py
├── 9_19_yield_from_filesystem.py
├── 9_25_knearest.py
└── case_study_machine_learn.py
├── Chapter13
├── case_study
│ ├── row.bmp
│ ├── big.bmp
│ ├── big2.bmp
│ ├── big3.bmp
│ ├── big4.bmp
│ ├── big5.bmp
│ ├── big6.bmp
│ ├── big7.bmp
│ ├── big8.bmp
│ ├── bricks.bmp
│ ├── decompress_to_bmp.py
│ └── compress_bmp.py
├── 13_01_two_basic_threads.py
├── 13_03_parallel.py
├── 13_07_basic_async.py
├── 13_04_prime_factor.py
├── 13_10_async_client.py
├── 13_06_futures.py
├── 13_05_post_search.py
├── 13_02_thread_wait.py
├── 13_09_async_multiprocessing.py
└── 13_08_asyncio_dns.py
├── Chapter11
├── 11_12_window_command_function.py
├── 11_05_flyweight_factory.py
├── 11_09_window_commands.py
├── 11_01_age_calculator.py
├── 11_03_age_calculator_adapt_date.py
├── 11_16_composite_folder_methods.py
├── 11_13_document_command_callable.py
├── 11_02_age_calculator_adapted.py
├── 11_06_flyweight_init.py
├── 11_10_window_command_invokers.py
├── 11_17_component_hierarchy.py
├── 11_18_add_child.py
├── 11_07_flyweight_check_serial.py
├── 11_04_email_facade.py
├── 11_08_car_class.py
├── 11_11_window_command_commands.py
├── 11_14_formatters.py
└── 11_15_formatter_factories.py
├── LICENSE
└── README.md
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.bmp filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/Chapter6/6_01_empty_object.py:
--------------------------------------------------------------------------------
1 | class MyObject:
2 | pass
3 |
--------------------------------------------------------------------------------
/Chapter8/case_study_input/header.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Chapter8/case_study_input/footer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/Chapter7/7_05_read_file.py:
--------------------------------------------------------------------------------
1 | file = open('filename')
2 | print(file.read())
3 |
--------------------------------------------------------------------------------
/Chapter12/12_06_simplestpytest.py:
--------------------------------------------------------------------------------
1 | def test_int_float():
2 | assert 1 == 1.0
3 |
--------------------------------------------------------------------------------
/Chapter3/3_01_inheriting_from_object.py:
--------------------------------------------------------------------------------
1 | class MySubClass(object):
2 | pass
3 |
--------------------------------------------------------------------------------
/Chapter8/8_07_unlabelled_kw.py:
--------------------------------------------------------------------------------
1 | print("{} {label} {}".format("x", "y", label="z"))
2 |
--------------------------------------------------------------------------------
/Chapter8/8_17_bytearray_replace.py:
--------------------------------------------------------------------------------
1 | b = bytearray(b"abcdefgh")
2 | b[4:6] = b"\x15\xa3"
3 | print(b)
4 |
--------------------------------------------------------------------------------
/Chapter10/10_10_decorator_syntax.py:
--------------------------------------------------------------------------------
1 | @log_calls
2 | def test1(a,b,c):
3 | print("\ttest1 called")
4 |
--------------------------------------------------------------------------------
/Chapter8/8_18_bytearray_index.py:
--------------------------------------------------------------------------------
1 | b = bytearray(b'abcdef')
2 | b[3] = ord(b'g')
3 | b[4] = 68
4 | print(b)
5 |
--------------------------------------------------------------------------------
/Chapter6/6_15_stupid_adding_integer.py:
--------------------------------------------------------------------------------
1 | class SillyInt(int):
2 | def __add__(self, num):
3 | return 0
4 |
--------------------------------------------------------------------------------
/Chapter7/7_07_with.py:
--------------------------------------------------------------------------------
1 | with open('filename') as file:
2 | for line in file:
3 | print(line, end='')
4 |
--------------------------------------------------------------------------------
/Chapter8/case_study_input/menu.html:
--------------------------------------------------------------------------------
1 | First Link
2 | Second Link
3 |
--------------------------------------------------------------------------------
/Chapter5/5_09_property_decorator_get.py:
--------------------------------------------------------------------------------
1 | class Foo:
2 | @property
3 | def foo(self):
4 | return "bar"
5 |
--------------------------------------------------------------------------------
/Chapter5/5_27_string_property.py:
--------------------------------------------------------------------------------
1 | @property
2 | def string(self):
3 | return "".join(self.characters)
4 |
--------------------------------------------------------------------------------
/Chapter6/6_04_dict_stocks.py:
--------------------------------------------------------------------------------
1 | stocks = {"GOOG": (613.30, 625.86, 610.50),
2 | "MSFT": (30.25, 30.70, 30.19)}
3 |
--------------------------------------------------------------------------------
/Chapter7/addresses.db:
--------------------------------------------------------------------------------
1 | friend1@example.com friends
2 | friend2@example.com friends
3 | family1@example.com family,friends
4 |
--------------------------------------------------------------------------------
/Chapter8/8_02_format_empty.py:
--------------------------------------------------------------------------------
1 | template = "Hello {}, you are currently {}."
2 | print(template.format('Dusty', 'writing'))
3 |
--------------------------------------------------------------------------------
/Chapter7/7_06_write.py:
--------------------------------------------------------------------------------
1 | contents = "Some file contents"
2 | file = open("filename", "w")
3 | file.write(contents)
4 | file.close()
5 |
--------------------------------------------------------------------------------
/Chapter8/8_14_format_datetime.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | print("{0:%Y-%m-%d %I:%M%p }".format(
3 | datetime.datetime.now()))
4 |
5 |
--------------------------------------------------------------------------------
/Chapter4/auth.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter4/auth.pyc
--------------------------------------------------------------------------------
/Chapter8/8_03_format_position.py:
--------------------------------------------------------------------------------
1 | template = "Hello {0}, you are {1}. Your name is {0}."
2 | print(template.format('Dusty', 'writing'))
3 |
--------------------------------------------------------------------------------
/Chapter8/8_15_encode_bytes.py:
--------------------------------------------------------------------------------
1 | characters = b'\x63\x6c\x69\x63\x68\xe9'
2 | print(characters)
3 | print(characters.decode("latin-1"))
4 |
--------------------------------------------------------------------------------
/Chapter10/Benson.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter10/Benson.jpg
--------------------------------------------------------------------------------
/Chapter10/TILED.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter10/TILED.jpg
--------------------------------------------------------------------------------
/Chapter10/sales.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter10/sales.db
--------------------------------------------------------------------------------
/Chapter12/dump.rdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/dump.rdb
--------------------------------------------------------------------------------
/Chapter12/stats.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/stats.pyc
--------------------------------------------------------------------------------
/Chapter6/6_09_counter_frequency.py:
--------------------------------------------------------------------------------
1 | from collections import Counter
2 | def letter_frequency(sentence):
3 | return Counter(sentence)
4 |
--------------------------------------------------------------------------------
/Chapter7/timer.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter7/timer.pyc
--------------------------------------------------------------------------------
/Chapter5/5_16_average_property.py:
--------------------------------------------------------------------------------
1 | class AverageList(list):
2 | @property
3 | def average(self):
4 | return sum(self) / len(self)
5 |
--------------------------------------------------------------------------------
/Chapter7/7_04_tdf_contact.txt:
--------------------------------------------------------------------------------
1 | first last email
2 | john smith jsmith@example.com
3 | jane doan janed@example.com
4 | david neilson dn@example.com
5 |
--------------------------------------------------------------------------------
/Chapter8/8_04_format_some_positions_broken.py:
--------------------------------------------------------------------------------
1 | template = "Hello {}, you are {}. Your name is {0}."
2 | print(template.format('Dusty', 'writing'))
3 |
--------------------------------------------------------------------------------
/Chapter5/zip_processor.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter5/zip_processor.pyc
--------------------------------------------------------------------------------
/Chapter9/9_08_list_comp_exclude.py:
--------------------------------------------------------------------------------
1 | input_strings = ['1', '5', '28', '131', '3']
2 |
3 | output_integers = [int(n) for n in input_strings if len(n) < 3]
4 |
--------------------------------------------------------------------------------
/Chapter12/12_09_funcargs.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_09_funcargs.pyc
--------------------------------------------------------------------------------
/Chapter4/4_10_defining_an_exception.py:
--------------------------------------------------------------------------------
1 | class InvalidWithdrawal(Exception):
2 | pass
3 |
4 | raise InvalidWithdrawal("You don't have $50 in your account")
5 |
--------------------------------------------------------------------------------
/Chapter12/12_07_class_pytest.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_07_class_pytest.pyc
--------------------------------------------------------------------------------
/Chapter12/12_12_pytest_echo.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_12_pytest_echo.pyc
--------------------------------------------------------------------------------
/Chapter7/7_20_link_downloader_vararg.py:
--------------------------------------------------------------------------------
1 | def get_pages(*links):
2 | for link in links:
3 | #download the link with urllib
4 | print(link)
5 |
6 |
--------------------------------------------------------------------------------
/Chapter9/9_20_basic_count_coroutine.py:
--------------------------------------------------------------------------------
1 |
2 | def tally():
3 | score = 0
4 | while True:
5 | increment = yield score
6 | score += increment
7 |
--------------------------------------------------------------------------------
/Chapter12/12_06_simplestpytest.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_06_simplestpytest.pyc
--------------------------------------------------------------------------------
/Chapter12/12_08_setup_teardown.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_08_setup_teardown.pyc
--------------------------------------------------------------------------------
/Chapter13/case_study/row.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:1e7b82cb4945f230f808b9b21ebc14dffb7e60be6fea68a877960d18ac80b5b7
3 | size 1322
4 |
--------------------------------------------------------------------------------
/Chapter4/4_08_catch_as_keyword.py:
--------------------------------------------------------------------------------
1 | try:
2 | raise ValueError("This is an argument")
3 | except ValueError as e:
4 | print("The exception arguments were", e.args)
5 |
--------------------------------------------------------------------------------
/Chapter12/12_10_funcarg_finalizer.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_10_funcarg_finalizer.pyc
--------------------------------------------------------------------------------
/Chapter12/12_15_pytest_skipifmark.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_15_pytest_skipifmark.pyc
--------------------------------------------------------------------------------
/Chapter13/case_study/big.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big2.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big3.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big4.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big5.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big6.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big7.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/big8.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:68c8c29b6548cc6af6c801e9b324e9c4bf57ee1d07341c8a0e1f9f2df21600ae
3 | size 120960122
4 |
--------------------------------------------------------------------------------
/Chapter13/case_study/bricks.bmp:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:e42a6abb2d9915c841300189cfafe30ddea27b2ee8df3134c42980abb475416f
3 | size 120122
4 |
--------------------------------------------------------------------------------
/Chapter7/7_36_enter_exit.py:
--------------------------------------------------------------------------------
1 | def __enter__(self):
2 | self.load()
3 | return self
4 |
5 | def __exit__(self, type, value, tb):
6 | self.save()
7 |
--------------------------------------------------------------------------------
/Chapter12/12_13_pytest_simple_skip.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_13_pytest_simple_skip.pyc
--------------------------------------------------------------------------------
/Chapter12/12_14_pytest_importorskip.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/12_14_pytest_importorskip.pyc
--------------------------------------------------------------------------------
/Chapter12/12_15_pytest_skipifmark.py:
--------------------------------------------------------------------------------
1 | import py.test
2 |
3 | @py.test.mark.skipif("sys.version_info <= (3,0)")
4 | def test_python3():
5 | assert b"hello".decode() == "hello"
6 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/vigenere_cipher.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/casestudy/vigenere_cipher.pyc
--------------------------------------------------------------------------------
/Chapter4/__pycache__/auth.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter4/__pycache__/auth.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter6/6_03_named_tuple.py:
--------------------------------------------------------------------------------
1 | from collections import namedtuple
2 | Stock = namedtuple("Stock", "symbol current high low")
3 | stock = Stock("FB", 75.00, high=75.03, low=74.90)
4 |
--------------------------------------------------------------------------------
/Chapter9/9_07_list_comp_converter.py:
--------------------------------------------------------------------------------
1 | input_strings = ['1', '5', '28', '131', '3']
2 |
3 | output_integers = [int(num) for num in input_strings]
4 |
5 | print(output_integers)
6 |
--------------------------------------------------------------------------------
/Chapter12/12_07_class_pytest.py:
--------------------------------------------------------------------------------
1 |
2 | class TestNumbers:
3 | def test_int_float(self):
4 | assert 1 == 1.0
5 |
6 | def test_int_str(self):
7 | assert 1 == "1"
8 |
--------------------------------------------------------------------------------
/Chapter12/__pycache__/stats.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/__pycache__/stats.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter7/7_18_bad_kw_default.py:
--------------------------------------------------------------------------------
1 | number = 5
2 | def funky_function(number=number):
3 | print(number)
4 |
5 | number=6
6 | funky_function(8)
7 | funky_function()
8 | print(number)
9 |
--------------------------------------------------------------------------------
/Chapter7/__pycache__/timer.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter7/__pycache__/timer.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter9/9_06_for_loop_converter.py:
--------------------------------------------------------------------------------
1 | input_strings = ['1', '5', '28', '131', '3']
2 |
3 | output_integers = []
4 | for num in input_strings:
5 | output_integers.append(int(num))
6 |
--------------------------------------------------------------------------------
/Chapter12/12_14_pytest_importorskip.py:
--------------------------------------------------------------------------------
1 | import py.test
2 |
3 | py.test.importorskip("postgresql")
4 |
5 | def test_connect():
6 | pass
7 |
8 | def test_query():
9 | pass
10 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/test_vigenere_cipher.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/casestudy/test_vigenere_cipher.pyc
--------------------------------------------------------------------------------
/Chapter8/8_01_string_creation.py:
--------------------------------------------------------------------------------
1 | a = "hello"
2 | b = 'world'
3 | c = '''a multiple
4 | line string'''
5 | d = """More
6 | multiple"""
7 | e = ("Three " "Strings "
8 | "Together")
9 |
--------------------------------------------------------------------------------
/Chapter8/8_11_no_format.py:
--------------------------------------------------------------------------------
1 | subtotal = 12.32
2 | tax = subtotal * 0.07
3 | total = subtotal + tax
4 |
5 | print("Sub: ${0} Tax: ${1} Total: ${total}".format(
6 | subtotal, tax, total=total))
7 |
--------------------------------------------------------------------------------
/Chapter7/7_03.1_zip_to_enumerate.py:
--------------------------------------------------------------------------------
1 |
2 | def zip_enumerate(container):
3 | return zip(range(len(container)), container)
4 |
5 | for idx, val in zip_enumerate("hello world"):
6 | print(idx, val)
7 |
--------------------------------------------------------------------------------
/Chapter12/__pycache__/test_mock.cpython-34-PYTEST.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/__pycache__/test_mock.cpython-34-PYTEST.pyc
--------------------------------------------------------------------------------
/Chapter8/8_16_decode_unicode.py:
--------------------------------------------------------------------------------
1 | characters = "cliché"
2 | print(characters.encode("UTF-8"))
3 | print(characters.encode("latin-1"))
4 | print(characters.encode("CP437"))
5 | print(characters.encode("ascii"))
6 |
--------------------------------------------------------------------------------
/Chapter3/3_17.1_abc_container.py:
--------------------------------------------------------------------------------
1 |
2 | class OddContainer:
3 | def __contains__(self, x):
4 | if not isinstance(x, int) or not x % 2:
5 | return False
6 | return True
7 |
8 |
--------------------------------------------------------------------------------
/Chapter6/6_20_collect_remaining_links.py:
--------------------------------------------------------------------------------
1 | for link in unvisited_links:
2 | if not link.startswith(self.url):
3 | continue
4 | self.collect_links(urlparse(link).path)
5 |
--------------------------------------------------------------------------------
/Chapter6/6_21_print_collected_links.py:
--------------------------------------------------------------------------------
1 | if __name__ == "__main__":
2 | collector = LinkCollector(sys.argv[1])
3 | collector.collect_links()
4 | for link in collector.collected_links:
5 | print(link)
6 |
--------------------------------------------------------------------------------
/Chapter7/7_02_enumerate_line_numbers.py:
--------------------------------------------------------------------------------
1 | import sys
2 | filename = sys.argv[1]
3 |
4 | with open(filename) as file:
5 | for index, line in enumerate(file):
6 | print("{0}: {1}".format(index+1, line), end='')
7 |
--------------------------------------------------------------------------------
/Chapter8/8_23.1_basic_regex.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | search_string = "hello world"
4 | pattern = "hello world"
5 |
6 | match = re.match(pattern, search_string)
7 |
8 | if match:
9 | print("regex matches")
10 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/__pycache__/vigenere_cipher.cpython-34.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/casestudy/__pycache__/vigenere_cipher.cpython-34.pyc
--------------------------------------------------------------------------------
/Chapter5/5_12_property_decorator_get_set.py:
--------------------------------------------------------------------------------
1 | class Foo:
2 | @property
3 | def foo(self):
4 | return self._foo
5 |
6 | @foo.setter
7 | def foo(self, value):
8 | self._foo = value
9 |
10 |
--------------------------------------------------------------------------------
/Chapter5/5_13_read_only_setattr.py:
--------------------------------------------------------------------------------
1 | class ReadOnlyX:
2 | def __setattr__(self, attr, value):
3 | if attr == "x":
4 | raise AttributeError("X is immutable")
5 | super().__setattr__(attr, value)
6 |
--------------------------------------------------------------------------------
/Chapter8/case_study_input/context.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dusty",
3 | "book_list": [
4 | "Thief Of Time",
5 | "The Thief",
6 | "Snow Crash",
7 | "Lathe Of Heaven"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/Chapter5/5_14_read_only_getattribute.py:
--------------------------------------------------------------------------------
1 | class ReadOnlyY:
2 | def __getattribute__(self, attr):
3 | if attr == "y":
4 | return "Just Try and Change Me!"
5 | return super().__getattribute__(attr)
6 |
--------------------------------------------------------------------------------
/Chapter10/10_03_simple_client.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4 | client.connect(('localhost', 2401))
5 | print("Received: {0}".format(client.recv(1024)))
6 | client.close()
7 |
8 |
--------------------------------------------------------------------------------
/Chapter3/3_26_rudimentary_agent.py:
--------------------------------------------------------------------------------
1 | class Agent:
2 | def __init__(self):
3 | self.property_list = []
4 |
5 | def display_properties():
6 | for property in self.property_list:
7 | property.display()
8 |
--------------------------------------------------------------------------------
/Chapter3/3_08_send_mail.py:
--------------------------------------------------------------------------------
1 | class MailSender:
2 | def send_mail(self, message):
3 | print("Sending mail to " + self.email)
4 | # Add e-mail logic here
5 |
6 | class EmailableContact(Contact, MailSender):
7 | pass
8 |
--------------------------------------------------------------------------------
/Chapter3/3_10_friend_address_holder.py:
--------------------------------------------------------------------------------
1 | class AddressHolder:
2 | def __init__(self, street, city, state, code):
3 | self.street = street
4 | self.city = city
5 | self.state = state
6 | self.code = code
7 |
--------------------------------------------------------------------------------
/Chapter4/4_02_exception_quits.py:
--------------------------------------------------------------------------------
1 | def no_return():
2 | print("I am about to raise an exception")
3 | raise Exception("This is always raised")
4 | print("This line will never execute")
5 | return "I won't be returned"
6 |
7 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/yoga.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I have studied Kung Fu, Tai Chi, Chi Kung, fan, and karate.
4 | Now I practice yoga at Be Luminous.
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Chapter8/8_12_currency_format.py:
--------------------------------------------------------------------------------
1 | subtotal = 12.32
2 | tax = subtotal * 0.07
3 | total = subtotal + tax
4 |
5 | print("Sub: ${0:0.2f} Tax: ${1:0.2f} "
6 | "Total: ${total:0.2f}".format(
7 | subtotal, tax, total=total))
8 |
--------------------------------------------------------------------------------
/Chapter3/3_09_send_mail_multi.py:
--------------------------------------------------------------------------------
1 | class MailSender:
2 | def send_mail(self, message):
3 | print("Sending mail to " + self.email)
4 | # Add e-mail logic here
5 |
6 | class EmailableContact(Friend, MailSender):
7 | pass
8 |
--------------------------------------------------------------------------------
/Chapter6/6_08_defaultdict_custom_function.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 | num_items = 0
3 | def tuple_counter():
4 | global num_items
5 | num_items += 1
6 | return (num_items, [])
7 |
8 | d = defaultdict(tuple_counter)
9 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/__pycache__/test_vigenere_cipher.cpython-34-PYTEST.pyc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/PacktPublishing/Python-3-Object-Oriented-Programming-Second-Edition/HEAD/Chapter12/casestudy/__pycache__/test_vigenere_cipher.cpython-34-PYTEST.pyc
--------------------------------------------------------------------------------
/Chapter5/5_05_python_pretty_as_python.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self.rgb_value = rgb_value
4 | self.name = name
5 |
6 | c = Color("#ff0000", "bright red")
7 | print(c.name)
8 | c.name = "red"
9 |
--------------------------------------------------------------------------------
/Chapter6/6_07_defaultdict_frequency.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 | def letter_frequency(sentence):
3 | frequencies = defaultdict(int)
4 | for letter in sentence:
5 | frequencies[letter] += 1
6 | return frequencies
7 |
--------------------------------------------------------------------------------
/Chapter8/8_05_brace_escape.py:
--------------------------------------------------------------------------------
1 | template = """
2 | public class {0} {{
3 | public static void main(String[] args) {{
4 | System.out.println("{1}");
5 | }}
6 | }}"""
7 |
8 | print(template.format("MyClass", "print('hello world')"));
9 |
--------------------------------------------------------------------------------
/Chapter3/3_02_simple_contact_class_to_inherit_from.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
--------------------------------------------------------------------------------
/Chapter3/3_24_house_rental.py:
--------------------------------------------------------------------------------
1 | class HouseRental(Rental, House):
2 | def prompt_init():
3 | init = House.prompt_init()
4 | init.update(Rental.prompt_init())
5 | return init
6 | prompt_init = staticmethod(prompt_init)
7 |
8 |
--------------------------------------------------------------------------------
/Chapter7/7_03_enumerate_max_min.py:
--------------------------------------------------------------------------------
1 | from operator import itemgetter
2 |
3 | def min_max_indexes(seq):
4 | minimum = min(enumerate(seq), key=itemgetter(1))
5 | maximum = max(enumerate(seq), key=itemgetter(1))
6 | return minimum[0], maximum[0]
7 |
--------------------------------------------------------------------------------
/Chapter7/7_27_add_function_to_object.py:
--------------------------------------------------------------------------------
1 | class A:
2 | def print(self):
3 | print("my class is A")
4 |
5 | def fake_print():
6 | print("my class is not A")
7 |
8 | a = A()
9 | a.print()
10 | a.print = fake_print
11 | a.print()
12 |
--------------------------------------------------------------------------------
/Chapter6/6_06_setdefault_frequency.py:
--------------------------------------------------------------------------------
1 | def letter_frequency(sentence):
2 | frequencies = {}
3 | for letter in sentence:
4 | frequency = frequencies.setdefault(letter, 0)
5 | frequencies[letter] = frequency + 1
6 | return frequencies
7 |
--------------------------------------------------------------------------------
/Chapter9/9_23_generate_colors.py:
--------------------------------------------------------------------------------
1 | from random import random
2 |
3 |
4 | def generate_colors(count=100):
5 | for i in range(count):
6 | yield (random(), random(), random())
7 |
8 | for color in generate_colors():
9 | print(color)
10 |
--------------------------------------------------------------------------------
/Chapter8/8_23.3_match_group.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | pattern = "^[a-zA-Z.]+@([a-z.]*\.[a-z]+)$"
4 | search_string = "some.user@example.com"
5 | match = re.match(pattern, search_string)
6 |
7 | if match:
8 | domain = match.groups()[0]
9 | print(domain)
10 |
--------------------------------------------------------------------------------
/Chapter3/3_05_dictionary_long_name.py:
--------------------------------------------------------------------------------
1 | class LongNameDict(dict):
2 | def longest_key(self):
3 | longest = None
4 | for key in self:
5 | if not longest or len(key) > len(longest):
6 | longest = key
7 | return longest
8 |
--------------------------------------------------------------------------------
/Chapter6/6_02_pass_tuple_to_function.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | def middle(stock, date):
3 | symbol, current, high, low = stock
4 | return (((high + low) /2), date)
5 |
6 | mid_value, date = middle(("GOOG", 613.30, 625.86, 610.50),
7 | datetime.date(2010, 1, 6))
8 |
--------------------------------------------------------------------------------
/Chapter12/12_13_pytest_simple_skip.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import py.test
3 |
4 | def test_simple_skip():
5 | if sys.platform != "fakeos":
6 | py.test.skip("Test works only on fakeOS")
7 |
8 | fakeos.do_something_fake()
9 | assert fakeos.did_not_happen
10 |
--------------------------------------------------------------------------------
/Chapter10/10_06_calling_decorated_sockets.py:
--------------------------------------------------------------------------------
1 | client, addr = server.accept()
2 | if log_send:
3 | client = LoggingSocket(client)
4 | if client.getpeername()[0] in compress_hosts:
5 | client = GzipSocket(client)
6 | respond(client)
7 |
--------------------------------------------------------------------------------
/Chapter7/7_33_mailing_list_get_emails.py:
--------------------------------------------------------------------------------
1 |
2 | def emails_in_groups(self, *groups):
3 | groups = set(groups)
4 | emails = set()
5 | for e, g in self.email_map.items():
6 | if g & groups:
7 | emails.add(e)
8 | return emails
9 |
--------------------------------------------------------------------------------
/Chapter12/12_30_coverage_unittest.py:
--------------------------------------------------------------------------------
1 | from stats import StatsList
2 | import unittest
3 |
4 | class TestMean(unittest.TestCase):
5 | def test_mean(self):
6 | self.assertEqual(StatsList([1,2,2,3,3,4]).mean(), 2.5)
7 |
8 | if __name__ == "__main__":
9 | unittest.main()
10 |
--------------------------------------------------------------------------------
/Chapter7/7_34_send_mailing.py:
--------------------------------------------------------------------------------
1 |
2 | def send_mailing(self, subject, message, from_addr,
3 | *groups, headers=None):
4 | emails = self.emails_in_groups(*groups)
5 | send_email(subject, message, from_addr,
6 | *emails, headers=headers)
7 |
--------------------------------------------------------------------------------
/Chapter10/10_16_singleton_using_new.py:
--------------------------------------------------------------------------------
1 | class OneOnly:
2 | _singleton = None
3 | def __new__(cls, *args, **kwargs):
4 | if not cls._singleton:
5 | cls._singleton = super(OneOnly, cls).__new__(
6 | cls, *args, **kwargs)
7 | return cls._singleton
8 |
--------------------------------------------------------------------------------
/Chapter11/11_12_window_command_function.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class MenuItem:
8 | def click(self):
9 | self.command()
10 |
11 | window = Window()
12 | menu_item = MenuItem()
13 | menu_item.command = window.exit
14 |
--------------------------------------------------------------------------------
/Chapter3/3_20_validation_function.py:
--------------------------------------------------------------------------------
1 | def get_valid_input(input_string, valid_options):
2 | input_string += " ({}) ".format(", ".join(valid_options))
3 | response = input(input_string)
4 | while response.lower() not in valid_options:
5 | response = input(input_string)
6 | return response
7 |
--------------------------------------------------------------------------------
/Chapter5/5_04_pytho_ugly_as_java.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self._rgb_value = rgb_value
4 | self._name = name
5 |
6 | def set_name(self, name):
7 | self._name = name
8 |
9 | def get_name(self):
10 | return self._name
11 |
--------------------------------------------------------------------------------
/Chapter9/9_15_log_delete_warnings_loop.py:
--------------------------------------------------------------------------------
1 | import sys
2 | inname, outname = sys.argv[1:3]
3 |
4 | with open(inname) as infile:
5 | with open(outname, "w") as outfile:
6 | for l in infile:
7 | if 'WARNING' in l:
8 | outfile.write(l.replace('\tWARNING', ''))
9 |
10 |
--------------------------------------------------------------------------------
/Chapter12/12_01_simplest_unittest.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | class CheckNumbers(unittest.TestCase):
4 | def test_int_float(self):
5 | self.assertEqual(1, 1.0)
6 |
7 | def test_str_float(self):
8 | self.assertEqual(1, "1")
9 |
10 | if __name__ == "__main__":
11 | unittest.main()
12 |
--------------------------------------------------------------------------------
/Chapter6/6_14_oop_pairs.py:
--------------------------------------------------------------------------------
1 | c = a + b
2 | c = a.add(b)
3 |
4 | l[0] = 5
5 | l.setitem(0, 5)
6 |
7 | d[key] = value
8 | d.setitem(key, value)
9 |
10 | for x in alist:
11 | #do something with x
12 | it = alist.iterator()
13 | while it.has_next():
14 | x = it.next()
15 | #do something with x
16 |
17 |
--------------------------------------------------------------------------------
/Chapter3/3_11_friend_multi.py:
--------------------------------------------------------------------------------
1 | class Friend(Contact, AddressHolder):
2 | def __init__(self, name, email, phone,
3 | street, city, state, code):
4 | Contact.__init__(self, name, email)
5 | AddressHolder.__init__(
6 | self, street, city, state, code)
7 | self.phone = phone
8 |
--------------------------------------------------------------------------------
/Chapter4/4_05_catch_specific_exception.py:
--------------------------------------------------------------------------------
1 | def funny_division(anumber):
2 | try:
3 | return 100 / anumber
4 | except ZeroDivisionError:
5 | return "Silly wabbit, you can't divide by zero!"
6 |
7 | print(funny_division(0))
8 | print(funny_division(50.0))
9 | print(funny_division("hello"))
10 |
--------------------------------------------------------------------------------
/Chapter7/7_32_mailing_list_defaultdict_set.py:
--------------------------------------------------------------------------------
1 |
2 | class MailingList:
3 | '''Manage groups of e-mail addresses for sending e-mails.'''
4 |
5 | def __init__(self):
6 | self.email_map = defaultdict(set)
7 |
8 | def add_to_group(self, email, group):
9 | self.email_map[email].add(group)
10 |
--------------------------------------------------------------------------------
/Chapter9/9_13_log_processor.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | inname = sys.argv[1]
4 | outname = sys.argv[2]
5 |
6 | with open(inname) as infile:
7 | with open(outname, "w") as outfile:
8 | warnings = (l for l in infile if 'WARNING' in l)
9 | for l in warnings:
10 | outfile.write(l)
11 |
12 |
--------------------------------------------------------------------------------
/Chapter4/4_01_even_integers.py:
--------------------------------------------------------------------------------
1 | class EvenOnly(list):
2 | def append(self, integer):
3 | if not isinstance(integer, int):
4 | raise TypeError("Only integers can be added")
5 | if integer % 2:
6 | raise ValueError("Only even numbers can be added")
7 | super().append(integer)
8 |
--------------------------------------------------------------------------------
/Chapter6/6_18_normalize_url.py:
--------------------------------------------------------------------------------
1 | def normalize_url(self, path, link):
2 | if link.startswith("http://"):
3 | return link
4 | elif link.startswith("/"):
5 | return self.url + link
6 | else:
7 | return self.url + path.rpartition('/'
8 | )[0] + '/' + link
9 |
--------------------------------------------------------------------------------
/Chapter7/7_19_link_downloader.py:
--------------------------------------------------------------------------------
1 | from collections import Iterable
2 |
3 | def get_pages(links):
4 | if not isinstance(links, Iterable) or isinstance(
5 | links, (bytes, str)):
6 | links = [links]
7 | for link in links:
8 | #download the link with urllib
9 | print(link)
10 |
11 |
--------------------------------------------------------------------------------
/Chapter3/3_17_ducktype_flac.py:
--------------------------------------------------------------------------------
1 | class FlacFile:
2 | def __init__(self, filename):
3 | if not filename.endswith(".flac"):
4 | raise Exception("Invalid file format")
5 |
6 | self.filename = filename
7 |
8 | def play(self):
9 | print("playing {} as flac movie".format(self.filename))
10 |
--------------------------------------------------------------------------------
/Chapter8/8_23_stringio.py:
--------------------------------------------------------------------------------
1 | #coding=utf-8
2 | from io import StringIO, BytesIO
3 | source_file = StringIO("an oft-repeated cliché")
4 | dest_file = BytesIO()
5 |
6 | char = source_file.read(1)
7 | while char:
8 | dest_file.write(char.encode("ascii", "replace"))
9 | char = source_file.read(1)
10 |
11 | print(dest_file.getvalue())
12 |
13 |
--------------------------------------------------------------------------------
/Chapter9/9_24_color_distance.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 |
4 | def color_distance(color1, color2):
5 | channels = zip(color1, color2)
6 | sum_distance_squared = 0
7 | for c1, c2 in channels:
8 | sum_distance_squared += (c1 - c2) ** 2
9 | return math.sqrt(sum_distance_squared)
10 |
11 | print(color_distance((1, 1), (4, 5)))
12 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/contact.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I can be contacted in lots of ways. Here are some links:
4 |
5 | My Blog
6 | Home
7 | Contact
8 | Dad's books
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter9/9_14_log_delete_warning_expression.py:
--------------------------------------------------------------------------------
1 | import sys
2 | inname, outname = sys.argv[1:3]
3 |
4 | with open(inname) as infile:
5 | with open(outname, "w") as outfile:
6 | warnings = (l.replace('\tWARNING', '')
7 | for l in infile if 'WARNING' in l)
8 | for l in warnings:
9 | outfile.write(l)
10 |
11 |
--------------------------------------------------------------------------------
/Chapter4/4_04_try_except.py:
--------------------------------------------------------------------------------
1 | def no_return():
2 | print("I am about to raise an exception")
3 | raise Exception("This is always raised")
4 | print("This line will never execute")
5 | return "I won't be returned"
6 |
7 | try:
8 | no_return()
9 | except:
10 | print("I caught an exception")
11 | print("executed after the exception")
12 |
--------------------------------------------------------------------------------
/Chapter7/7_23_unpacking_arguments.py:
--------------------------------------------------------------------------------
1 | def show_args(arg1, arg2, arg3="THREE"):
2 | print(arg1, arg2, arg3)
3 |
4 | some_args = range(3)
5 | more_args = {
6 | "arg1": "ONE",
7 | "arg2": "TWO"}
8 |
9 | print("Unpacking a sequence:", end=" ")
10 | show_args(*some_args)
11 | print("Unpacking a dict:", end=" ")
12 | show_args(**more_args)
13 |
--------------------------------------------------------------------------------
/Chapter8/8_23.2_regex_generic.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import re
3 |
4 | pattern = sys.argv[1]
5 | search_string = sys.argv[2]
6 | match = re.match(pattern, search_string)
7 |
8 | if match:
9 | template = "'{}' matches pattern '{}'"
10 | else:
11 | template = "'{}' does not match pattern '{}'"
12 |
13 | print(template.format(search_string, pattern))
14 |
--------------------------------------------------------------------------------
/Chapter5/5_01_distances_no_objects.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | def distance(p1, p2):
4 | return math.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2)
5 |
6 | def perimeter(polygon):
7 | perimeter = 0
8 | points = polygon + [polygon[0]]
9 | for i in range(len(polygon)):
10 | perimeter += distance(points[i], points[i+1])
11 | return perimeter
12 |
--------------------------------------------------------------------------------
/Chapter6/6_09_list_tuple_frequency.py:
--------------------------------------------------------------------------------
1 | import string
2 | CHARACTERS = list(string.ascii_letters) + [" "]
3 |
4 | def letter_frequency(sentence):
5 | frequencies = [(c, 0) for c in CHARACTERS]
6 | for letter in sentence:
7 | index = CHARACTERS.index(letter)
8 | frequencies[index] = (letter,frequencies[index][1] + 1)
9 | return frequencies
10 |
--------------------------------------------------------------------------------
/Chapter6/6_09_counter_poll.py:
--------------------------------------------------------------------------------
1 | from collections import Counter
2 |
3 | responses = [
4 | "vanilla",
5 | "chocolate",
6 | "vanilla",
7 | "vanilla",
8 | "caramel",
9 | "strawberry",
10 | "vanilla"
11 | ]
12 |
13 | print(
14 | "The children voted for {} ice cream".format(
15 | Counter(responses).most_common(1)[0][0]
16 | )
17 | )
18 |
--------------------------------------------------------------------------------
/Chapter8/8_13_tabular.py:
--------------------------------------------------------------------------------
1 | orders = [('burger', 2, 5),
2 | ('fries', 3.5, 1),
3 | ('cola', 1.75, 3)]
4 |
5 | print("PRODUCT QUANTITY PRICE SUBTOTAL")
6 | for product, price, quantity in orders:
7 | subtotal = price * quantity
8 | print("{0:10s}{1: ^9d} ${2: <8.2f}${3: >7.2f}".format(
9 | product, quantity, price, subtotal))
10 |
11 |
--------------------------------------------------------------------------------
/Chapter5/5_06_setting_name_in_method.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self._rgb_value = rgb_value
4 | self._name = name
5 |
6 | def set_name(self, name):
7 | if not name:
8 | raise Exception("Invalid Name")
9 | self._name = name
10 |
11 | def get_name(self):
12 | return self._name
13 |
--------------------------------------------------------------------------------
/Chapter8/8_06_format_kw_args.py:
--------------------------------------------------------------------------------
1 | template = """
2 | From: <{from_email}>
3 | To: <{to_email}>
4 | Subject: {subject}
5 | {message}"""
6 | print(template.format(
7 | from_email = "a@example.com",
8 | to_email = "b@example.com",
9 | message = "Here's some mail for you. "
10 | " Hope you enjoy the message!",
11 | subject = "You have mail!"
12 | ))
13 |
14 |
--------------------------------------------------------------------------------
/Chapter8/8_08_tuple_dict_format.py:
--------------------------------------------------------------------------------
1 | emails = ("a@example.com", "b@example.com")
2 | message = {
3 | 'subject': "You Have Mail!",
4 | 'message': "Here's some mail for you!"
5 | }
6 | template = """
7 | From: <{0[0]}>
8 | To: <{0[1]}>
9 | Subject: {message[subject]}
10 | {message[message]}"""
11 | print(template.format(emails, message=message))
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Chapter11/11_05_flyweight_factory.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/blog.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | My real blog is here if you are
4 | interested.
5 |
6 | Here are some links:
7 | The first page
8 | Some contact info
9 | All about my dog!
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Chapter8/8_09_tuple_in_dict_format.py:
--------------------------------------------------------------------------------
1 | emails = ("a@example.com", "b@example.com")
2 | message = {
3 | 'emails': emails,
4 | 'subject': "You Have Mail!",
5 | 'message': "Here's some mail for you!"
6 | }
7 | template = """
8 | From: <{0[emails][0]}>
9 | To: <{0[emails][1]}>
10 | Subject: {0[subject]}
11 | {0[message]}"""
12 | print(template.format(message))
13 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Contact us
4 | Blog
5 | My Dog
6 | Some hobbies
7 | Contact AGAIN
8 | Favourite OS
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Chapter7/7_08_context_manager.py:
--------------------------------------------------------------------------------
1 | class StringJoiner(list):
2 | def __enter__(self):
3 | return self
4 |
5 | def __exit__(self, type, value, tb):
6 | self.result = "".join(self)
7 |
8 | import random, string
9 | with StringJoiner() as joiner:
10 | for i in range(15):
11 | joiner.append(random.choice(string.ascii_letters))
12 |
13 | print(joiner.result)
14 |
--------------------------------------------------------------------------------
/Chapter3/3_07_friend_overrides_init_super.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
9 | class Friend(Contact):
10 | def __init__(self, name, email, phone):
11 | super().__init__(name, email)
12 | self.phone = phone
13 |
--------------------------------------------------------------------------------
/Chapter11/11_09_window_commands.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class Document:
8 | def __init__(self, filename):
9 | self.filename = filename
10 | self.contents = "This file cannot be modified"
11 |
12 | def save(self):
13 | with open(self.filename, 'w') as file:
14 | file.write(self.contents)
15 |
--------------------------------------------------------------------------------
/Chapter4/4_11_exception_with_custom_args.py:
--------------------------------------------------------------------------------
1 | class InvalidWithdrawal(Exception):
2 | def __init__(self, balance, amount):
3 | super().__init__("account doesn't have ${}".format(
4 | amount))
5 | self.amount = amount
6 | self.balance = balance
7 |
8 | def overage(self):
9 | return self.amount - self.balance
10 |
11 | raise InvalidWithdrawal(25, 50)
12 |
--------------------------------------------------------------------------------
/Chapter5/5_15_cache_getter.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 |
3 | class WebPage:
4 | def __init__(self, url):
5 | self.url = url
6 | self._content = None
7 |
8 | @property
9 | def content(self):
10 | if not self._content:
11 | print("Retrieving New Page...")
12 | self._content = urlopen(self.url).read()
13 | return self._content
14 |
--------------------------------------------------------------------------------
/Chapter3/3_06_friend_overrides_init.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
9 | class Friend(Contact):
10 | def __init__(self, name, email, phone):
11 | self.name = name
12 | self.email = email
13 | self.phone = phone
14 |
--------------------------------------------------------------------------------
/Chapter8/8_24_basic_pickling.py:
--------------------------------------------------------------------------------
1 | import pickle
2 |
3 | some_data = ["a list", "containing", 5,
4 | "values including another list",
5 | ["inner", "list"]]
6 |
7 | with open("pickled_list", 'wb') as file:
8 | pickle.dump(some_data, file)
9 |
10 | with open("pickled_list", 'rb') as file:
11 | loaded_data = pickle.load(file)
12 |
13 | print(loaded_data)
14 | assert loaded_data == some_data
15 |
--------------------------------------------------------------------------------
/Chapter12/12_11_echo_server.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
4 | s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
5 | s.bind(('localhost',1028))
6 | s.listen(1)
7 |
8 | try:
9 | while True:
10 | client, address = s.accept()
11 | data = client.recv(1024)
12 | client.send(data)
13 | client.close()
14 | finally:
15 | s.close()
16 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/hobbies.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I do yoga, woodworking, skating, writing, and programming.
4 |
5 | My studio
6 | Yoga stuff
7 | The home page
8 | Esme is my hobby too
9 | Recent stuff
10 |
11 |
12 |
--------------------------------------------------------------------------------
/Chapter4/04_03_method_calls_excepting.py:
--------------------------------------------------------------------------------
1 | def no_return():
2 | print("I am about to raise an exception")
3 | raise Exception("This is always raised")
4 | print("This line will never execute")
5 | return "I won't be returned"
6 |
7 | def call_exceptor():
8 | print("call_exceptor starts here...")
9 | no_return()
10 | print("an exception was raised...")
11 | print("...so these lines don't run")
12 |
--------------------------------------------------------------------------------
/Chapter5/5_07_setting_name_property.py:
--------------------------------------------------------------------------------
1 | class Color:
2 | def __init__(self, rgb_value, name):
3 | self.rgb_value = rgb_value
4 | self._name = name
5 |
6 | def _set_name(self, name):
7 | if not name:
8 | raise Exception("Invalid Name")
9 | self._name = name
10 |
11 | def _get_name(self):
12 | return self._name
13 |
14 | name = property(_get_name, _set_name)
15 |
--------------------------------------------------------------------------------
/Chapter9/9_22_load_dataset.py:
--------------------------------------------------------------------------------
1 | import csv
2 |
3 | dataset_filename = 'colors.csv'
4 |
5 |
6 | def load_colors(filename):
7 | with open(filename) as dataset_file:
8 | lines = csv.reader(dataset_file)
9 | for line in lines:
10 | yield tuple(float(y) for y in line[0:3]), line[3]
11 |
12 | for color, name in load_colors(dataset_filename):
13 | print("RGB {} is named {}".format(color, name))
14 |
--------------------------------------------------------------------------------
/Chapter3/3_03_contact_inherit_supplier.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name, email):
5 | self.name = name
6 | self.email = email
7 | Contact.all_contacts.append(self)
8 |
9 | class Supplier(Contact):
10 | def order(self, order):
11 | print("If this were a real system we would send "
12 | "{} order to {}".format(order, self.name))
13 |
--------------------------------------------------------------------------------
/Chapter9/9_26_write_results.py:
--------------------------------------------------------------------------------
1 | import csv
2 |
3 |
4 | def write_results(filename="output.csv"):
5 | with open(filename, "w") as file:
6 | writer = csv.writer(file)
7 | while True:
8 | color, name = yield
9 | writer.writerow(list(color) + [name])
10 |
11 | results = write_results()
12 | next(results)
13 | for i in range(3):
14 | print(i)
15 | results.send(((i, i, i), i * 10))
16 |
--------------------------------------------------------------------------------
/Chapter4/4_06_catch_multiple_exceptions.py:
--------------------------------------------------------------------------------
1 | def funny_division2(anumber):
2 | try:
3 | if anumber == 13:
4 | raise ValueError("13 is an unlucky number")
5 | return 100 / anumber
6 | except (ZeroDivisionError, TypeError):
7 | return "Enter a number other than zero"
8 |
9 | for val in (0, "hello", 50.0, 13):
10 | print("Testing {}:".format(val), end=" ")
11 | print(funny_division2(val))
12 |
--------------------------------------------------------------------------------
/Chapter7/7_05_tdf_processor.py:
--------------------------------------------------------------------------------
1 | import sys
2 | filename = sys.argv[1]
3 |
4 | with open(filename) as file:
5 | header = file.readline().strip().split('\t')
6 | contacts = [
7 | dict(
8 | zip(header, line.strip().split('\t'))
9 | ) for line in file
10 | ]
11 |
12 | for contact in contacts:
13 | print("email: {email} -- {last}, {first}".format(
14 | **contact))
15 |
--------------------------------------------------------------------------------
/Chapter9/9_09_tdf_list_comp.py:
--------------------------------------------------------------------------------
1 | import sys
2 | filename = sys.argv[1]
3 |
4 | with open(filename) as file:
5 | header = file.readline().strip().split('\t')
6 | contacts = [
7 | dict(
8 | zip(header, line.strip().split('\t'))
9 | ) for line in file
10 | ]
11 |
12 | for contact in contacts:
13 | print("email: {email} -- {last}, {first}".format(
14 | **contact))
15 |
--------------------------------------------------------------------------------
/Chapter9/9_17_log_delete_warnings_generator.py:
--------------------------------------------------------------------------------
1 | import sys
2 | inname, outname = sys.argv[1:3]
3 |
4 | def warnings_filter(insequence):
5 | for l in insequence:
6 | if 'WARNING' in l:
7 | yield l.replace('\tWARNING', '')
8 |
9 | with open(inname) as infile:
10 | with open(outname, "w") as outfile:
11 | filter = warnings_filter(infile)
12 | for l in filter:
13 | outfile.write(l)
14 |
--------------------------------------------------------------------------------
/Chapter6/6_12_set_operations.py:
--------------------------------------------------------------------------------
1 | my_artists = {"Sarah Brightman", "Guns N' Roses",
2 | "Opeth", "Vixy and Tony"}
3 |
4 | auburns_artists = {"Nickelback", "Guns N' Roses",
5 | "Savage Garden"}
6 |
7 | print("All: {}".format(my_artists.union(auburns_artists)))
8 | print("Both: {}".format(auburns_artists.intersection(my_artists)))
9 | print("Either but not both: {}".format(
10 | my_artists.symmetric_difference(auburns_artists)))
11 |
--------------------------------------------------------------------------------
/Chapter10/10_02_simple_socket.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | def respond(client):
4 | response = input("Enter a value: ")
5 | client.send(bytes(response, 'utf8'))
6 | client.close()
7 |
8 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
9 | server.bind(('localhost',2401))
10 | server.listen(1)
11 | try:
12 | while True:
13 | client, addr = server.accept()
14 | respond(client)
15 | finally:
16 | server.close()
17 |
--------------------------------------------------------------------------------
/Chapter11/11_01_age_calculator.py:
--------------------------------------------------------------------------------
1 | class AgeCalculator:
2 | def __init__(self, birthday):
3 | self.year, self.month, self.day = (
4 | int(x) for x in birthday.split('-'))
5 |
6 | def calculate_age(self, date):
7 | year, month, day = (
8 | int(x) for x in date.split('-'))
9 | age = year - self.year
10 | if (month,day) < (self.month,self.day):
11 | age -= 1
12 | return age
13 |
--------------------------------------------------------------------------------
/Chapter8/case_study_input/main.html:
--------------------------------------------------------------------------------
1 | /** include header.html **/
2 | This is the title of the front page
3 | /** include menu.html **/
4 | My name is /** variable name **/.
5 | This is the content of my front page. It goes below the menu.
6 |
7 | | Favourite Books |
8 | /** loopover book_list **/
9 | | /** loopvar **/ |
10 | /** endloop **/
11 |
12 | /** include footer.html **/
13 | Copyright © Today
14 |
--------------------------------------------------------------------------------
/Chapter6/case_study_serve/esme.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | I have a Cavalier King Charles Spaniel named Esme. She's named after Esme (for
4 | Esmerelda) Weatherwax, from Terry Pratchett's Discworld series.
5 |
6 | Here's some links:
7 |
8 | My hobbies
9 | Wikipedia
10 | on Cavaliers
11 |
12 |
13 |
--------------------------------------------------------------------------------
/Chapter7/7_21_kwarg_options.py:
--------------------------------------------------------------------------------
1 | class Options:
2 | default_options = {
3 | 'port': 21,
4 | 'host': 'localhost',
5 | 'username': None,
6 | 'password': None,
7 | 'debug': False,
8 | }
9 | def __init__(self, **kwargs):
10 | self.options = dict(Options.default_options)
11 | self.options.update(kwargs)
12 |
13 | def __getitem__(self, key):
14 | return self.options[key]
15 |
--------------------------------------------------------------------------------
/Chapter9/9_18_log_delete_warnings_yield_from.py:
--------------------------------------------------------------------------------
1 | import sys
2 | inname, outname = sys.argv[1:3]
3 |
4 |
5 | def warnings_filter(infilename):
6 | with open(infilename) as infile:
7 | yield from (
8 | l.replace('\tWARNING', '')
9 | for l in infile
10 | if 'WARNING' in l
11 | )
12 |
13 | filter = warnings_filter(inname)
14 | with open(outname, "w") as outfile:
15 | for l in filter:
16 | outfile.write(l)
17 |
--------------------------------------------------------------------------------
/Chapter3/3_27_type_map.py:
--------------------------------------------------------------------------------
1 | class Agent:
2 | type_map = {
3 | ("house", "rental"): HouseRental,
4 | ("house", "purchase"): HousePurchase,
5 | ("apartment", "rental"): ApartmentRental,
6 | ("apartment", "purchase"): ApartmentPurchase
7 | }
8 |
9 | def __init__(self):
10 | self.property_list = []
11 |
12 | def display_properties():
13 | for property in self.property_list:
14 | property.display()
15 |
--------------------------------------------------------------------------------
/Chapter12/12_02_assertraises_python.py:
--------------------------------------------------------------------------------
1 | import unittest
2 |
3 | def average(seq):
4 | return sum(seq) / len(seq)
5 |
6 | class TestAverage(unittest.TestCase):
7 | def test_zero(self):
8 | self.assertRaises(ZeroDivisionError,
9 | average,
10 | [])
11 |
12 | def test_with_zero(self):
13 | with self.assertRaises(ZeroDivisionError):
14 | average([])
15 |
16 | if __name__ == "__main__":
17 | unittest.main()
18 |
--------------------------------------------------------------------------------
/Chapter5/5_11_property_decorator_arguments.py:
--------------------------------------------------------------------------------
1 | class Silly:
2 | @property
3 | def silly(self):
4 | "This is a silly property"
5 | print("You are getting silly")
6 | return self._silly
7 |
8 | @silly.setter
9 | def silly(self, value):
10 | print("You are making silly {}".format(value))
11 | self._silly = value
12 |
13 | @silly.deleter
14 | def silly(self):
15 | print("Whoah, you killed silly!")
16 | del self._silly
17 |
--------------------------------------------------------------------------------
/Chapter3/3_17.2_abc_media.py:
--------------------------------------------------------------------------------
1 | import abc
2 |
3 | class MediaLoader(metaclass=abc.ABCMeta):
4 | @abc.abstractmethod
5 | def play(self):
6 | pass
7 |
8 | @abc.abstractproperty
9 | def ext(self):
10 | pass
11 |
12 | @classmethod
13 | def __subclasshook__(cls, C):
14 | if cls is MediaLoader:
15 | attrs = set(dir(C))
16 | if set(cls.__abstractmethods__) <= attrs:
17 | return True
18 |
19 | return NotImplemented
20 |
--------------------------------------------------------------------------------
/Chapter6/6_11_song_artist_set.py:
--------------------------------------------------------------------------------
1 | song_library = [("Phantom Of The Opera", "Sarah Brightman"),
2 | ("Knocking On Heaven's Door", "Guns N' Roses"),
3 | ("Captain Nemo", "Sarah Brightman"),
4 | ("Patterns In The Ivy", "Opeth"),
5 | ("November Rain", "Guns N' Roses"),
6 | ("Beautiful", "Sarah Brightman"),
7 | ("Mal's Song", "Vixy and Tony")]
8 |
9 | artists = set()
10 | for song, artist in song_library:
11 | artists.add(artist)
12 |
13 | print(artists)
14 |
--------------------------------------------------------------------------------
/Chapter10/10_19_template_abstract_noimple.py:
--------------------------------------------------------------------------------
1 | class QueryTemplate:
2 | def connect(self):
3 | pass
4 | def construct_query(self):
5 | pass
6 | def do_query(self):
7 | pass
8 | def format_results(self):
9 | pass
10 | def output_results(self):
11 | pass
12 |
13 | def process_format(self):
14 | self.connect()
15 | self.construct_query()
16 | self.do_query()
17 | self.format_results()
18 | self.output_results()
19 |
--------------------------------------------------------------------------------
/Chapter5/5_08_property_arguments.py:
--------------------------------------------------------------------------------
1 | class Silly:
2 | def _get_silly(self):
3 | print("You are getting silly")
4 | return self._silly
5 | def _set_silly(self, value):
6 | print("You are making silly {}".format(value))
7 | self._silly = value
8 | def _del_silly(self):
9 | print("Whoah, you killed silly!")
10 | del self._silly
11 |
12 | silly = property(_get_silly, _set_silly, _del_silly,
13 | "This is a silly property")
14 |
15 |
16 |
--------------------------------------------------------------------------------
/Chapter10/10_14_simple_xml_to_parse.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Dusty Phillips
4 | Packt Publishing
5 | Python 3 Object Oriented Programming
6 |
7 |
8 | 1
9 | Object Oriented Design
10 |
11 |
12 | 2
13 | Objects In Python
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Chapter7/7_01_reversible_objects.py:
--------------------------------------------------------------------------------
1 | normal_list=[1,2,3,4,5]
2 |
3 | class CustomSequence():
4 | def __len__(self):
5 | return 5
6 |
7 | def __getitem__(self, index):
8 | return "x{0}".format(index)
9 |
10 | class FunkyBackwards():
11 | def __reversed__(self):
12 | return "BACKWARDS!"
13 |
14 | for seq in normal_list, CustomSequence(), FunkyBackwards():
15 | print("\n{}: ".format(seq.__class__.__name__), end="")
16 | for item in reversed(seq):
17 | print(item, end=", ")
18 |
--------------------------------------------------------------------------------
/Chapter13/13_01_two_basic_threads.py:
--------------------------------------------------------------------------------
1 | from threading import Thread
2 |
3 | class InputReader(Thread):
4 | def run(self):
5 | self.line_of_text = input()
6 |
7 | print("Enter some text and press enter: ")
8 | thread = InputReader()
9 | thread.start()
10 |
11 | count = result = 1
12 | while thread.is_alive():
13 | result = count * count
14 | count += 1
15 |
16 | print("calculated squares up to {0} * {0} = {1}".format(
17 | count, result))
18 | print("while you typed '{}'".format(thread.line_of_text))
19 |
--------------------------------------------------------------------------------
/Chapter12/12_09_funcargs.py:
--------------------------------------------------------------------------------
1 | from stats import StatsList
2 |
3 | def pytest_funcarg__valid_stats(request):
4 | return StatsList([1,2,2,3,3,4])
5 |
6 |
7 | def test_mean(valid_stats):
8 | assert valid_stats.mean() == 2.5
9 |
10 | def test_median(valid_stats):
11 | assert valid_stats.median() == 2.5
12 | valid_stats.append(4)
13 | assert valid_stats.median() == 3
14 |
15 | def test_mode(valid_stats):
16 | assert valid_stats.mode() == [2,3]
17 | valid_stats.remove(2)
18 | assert valid_stats.mode() == [3]
19 |
--------------------------------------------------------------------------------
/Chapter5/5_20_scaleimage_inheritance.py:
--------------------------------------------------------------------------------
1 | from zip_processor import ZipProcessor
2 | import sys
3 | from PIL import Image
4 |
5 | class ScaleZip(ZipProcessor):
6 |
7 | def process_files(self):
8 | '''Scale each image in the directory to 640x480'''
9 | for filename in self.temp_directory.iterdir():
10 | im = Image.open(str(filename))
11 | scaled = im.resize((640, 480))
12 | scaled.save(str(filename))
13 |
14 | if __name__ == "__main__":
15 | ScaleZip(*sys.argv[1:4]).process_zip()
16 |
--------------------------------------------------------------------------------
/Chapter8/8_10_object_formatting.py:
--------------------------------------------------------------------------------
1 | class EMail:
2 | def __init__(self, from_addr, to_addr, subject, message):
3 | self.from_addr = from_addr
4 | self.to_addr = to_addr
5 | self.subject = subject
6 | self.message = message
7 |
8 | email = EMail("a@example.com", "b@example.com",
9 | "You Have Mail!",
10 | "Here's some mail for you!")
11 |
12 | template = """
13 | From: <{0.from_addr}>
14 | To: <{0.to_addr}>
15 | Subject: {0.subject}
16 |
17 | {0.message}"""
18 | print(template.format(email))
19 |
--------------------------------------------------------------------------------
/Chapter13/13_03_parallel.py:
--------------------------------------------------------------------------------
1 | from multiprocessing import Process, cpu_count
2 | from threading import Thread
3 | import time
4 | import os
5 |
6 | class MuchCPU(Process):
7 | def run(self):
8 | print(os.getpid())
9 | for i in range(200000000):
10 | pass
11 |
12 | if __name__ == '__main__':
13 | procs = [MuchCPU() for f in range(cpu_count())]
14 |
15 | t = time.time()
16 | for p in procs:
17 | p.start()
18 | for p in procs:
19 | p.join()
20 | print('work took {} seconds'.format(time.time() - t))
21 |
--------------------------------------------------------------------------------
/Chapter5/5_28_Character_class.py:
--------------------------------------------------------------------------------
1 | class Character:
2 | def __init__(self, character,
3 | bold=False, italic=False, underline=False):
4 | assert len(character) == 1
5 | self.character = character
6 | self.bold = bold
7 | self.italic = italic
8 | self.underline = underline
9 |
10 | def __str__(self):
11 | bold = "*" if self.bold else ''
12 | italic = "/" if self.italic else ''
13 | underline = "_" if self.underline else ''
14 | return bold + italic + underline + self.character
15 |
--------------------------------------------------------------------------------
/Chapter4/4_07_catch_multiple_different.py:
--------------------------------------------------------------------------------
1 | def funny_division3(anumber):
2 | try:
3 | if anumber == 13:
4 | raise ValueError("13 is an unlucky number")
5 | return 100 / anumber
6 | except ZeroDivisionError:
7 | return "Enter a number other than zero"
8 | except TypeError:
9 | return "Enter a numerical value"
10 | except ValueError:
11 | print("No, No, not 13!")
12 | raise
13 |
14 | for val in (0, "hello", 50.0, 13):
15 | print("Testing %s:" % val, end=" ")
16 | print(funny_division3(val))
17 |
--------------------------------------------------------------------------------
/Chapter7/7_28_callable_repeat.py:
--------------------------------------------------------------------------------
1 | from timer import Timer
2 | import datetime
3 |
4 | def format_time(message, *args):
5 | now = datetime.datetime.now().strftime("%I:%M:%S")
6 | print(message.format(*args, now=now))
7 |
8 | class Repeater:
9 | def __init__(self):
10 | self.count = 0
11 | def __call__(self, timer):
12 | format_time("{now}: repeat {0}", self.count)
13 | self.count += 1
14 | timer.call_after(5, self)
15 |
16 | timer = Timer()
17 | timer.call_after(5, Repeater())
18 | format_time("{now}: Starting")
19 | timer.run()
20 |
--------------------------------------------------------------------------------
/Chapter4/4_12_handle_custom_exception.py:
--------------------------------------------------------------------------------
1 | class InvalidWithdrawal(Exception):
2 | def __init__(self, balance, amount):
3 | super().__init__("account doesn't have ${}".format(
4 | amount))
5 | self.amount = amount
6 | self.balance = balance
7 |
8 | def overage(self):
9 | return self.amount - self.balance
10 |
11 | try:
12 | raise InvalidWithdrawal(25, 50)
13 | except InvalidWithdrawal as e:
14 | print("I'm sorry, but your withdrawal is "
15 | "more than your balance by "
16 | "${}".format(e.overage()))
17 |
--------------------------------------------------------------------------------
/Chapter12/12_10_funcarg_finalizer.py:
--------------------------------------------------------------------------------
1 | import tempfile
2 | import shutil
3 | import os.path
4 |
5 | def pytest_funcarg__temp_dir(request):
6 | dir = tempfile.mkdtemp()
7 | print(dir)
8 |
9 | def cleanup():
10 | shutil.rmtree(dir)
11 | request.addfinalizer(cleanup)
12 | return dir
13 |
14 | def test_osfiles(temp_dir):
15 | os.mkdir(os.path.join(temp_dir, 'a'))
16 | os.mkdir(os.path.join(temp_dir, 'b'))
17 | dir_contents = os.listdir(temp_dir)
18 | assert len(dir_contents) == 2
19 | assert 'a' in dir_contents
20 | assert 'b' in dir_contents
21 |
--------------------------------------------------------------------------------
/Chapter3/3_04_contact_list_inheritance.py:
--------------------------------------------------------------------------------
1 | class ContactList(list):
2 | def search(self, name):
3 | '''Return all contacts that contain the search value
4 | in their name.'''
5 | matching_contacts = []
6 | for contact in self:
7 | if name in contact.name:
8 | matching_contacts.append(contact)
9 | return matching_contacts
10 |
11 | class Contact:
12 | all_contacts = ContactList()
13 |
14 | def __init__(self, name, email):
15 | self.name = name
16 | self.email = email
17 | self.all_contacts.append(self)
18 |
--------------------------------------------------------------------------------
/Chapter9/example.log:
--------------------------------------------------------------------------------
1 | Jan 26, 2015 11:25:25 DEBUG This is a debugging message.
2 | Jan 26, 2015 11:25:36 INFO This is an information method.
3 | Jan 26, 2015 11:25:46 WARNING This is a warning. It could be serious.
4 | Jan 26, 2015 11:25:52 WARNING Another warning sent.
5 | Jan 26, 2015 11:25:59 INFO Here's some information.
6 | Jan 26, 2015 11:26:13 DEBUG Debug messages are only useful if you want to figure something out.
7 | Jan 26, 2015 11:26:32 INFO Information is usually harmless, but helpful.
8 | Jan 26, 2015 11:26:40 WARNING Warnings should be heeded.
9 | Jan 26, 2015 11:26:54 WARNING Watch for warnings.
10 |
--------------------------------------------------------------------------------
/Chapter7/7_29_send_email.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | from email.mime.text import MIMEText
3 |
4 | def send_email(subject, message, from_addr, *to_addrs,
5 | host="localhost", port=1025, **headers):
6 |
7 | email = MIMEText(message)
8 | email['Subject'] = subject
9 | email['From'] = from_addr
10 | for header, value in headers.items():
11 | email[header] = value
12 |
13 | sender = smtplib.SMTP(host, port)
14 | for addr in to_addrs:
15 | del email['To']
16 | email['To'] = addr
17 | sender.sendmail(from_addr, addr, email.as_string())
18 | sender.quit()
19 |
--------------------------------------------------------------------------------
/Chapter11/11_03_age_calculator_adapt_date.py:
--------------------------------------------------------------------------------
1 | class AgeCalculator:
2 | def __init__(self, birthday):
3 | self.year, self.month, self.day = (
4 | int(x) for x in birthday.split('-'))
5 |
6 | def calculate_age(self, date):
7 | year, month, day = (
8 | int(x) for x in date.split('-'))
9 | age = year - self.year
10 | if (month,day) < (self.month,self.day):
11 | age -= 1
12 | return age
13 |
14 | import datetime
15 | class AgeableDate(datetime.date):
16 | def split(self, char):
17 | return self.year, self.month, self.day
18 |
--------------------------------------------------------------------------------
/Chapter4/4_09_finally_and_else.py:
--------------------------------------------------------------------------------
1 | import random
2 | some_exceptions = [ValueError, TypeError, IndexError, None]
3 |
4 | try:
5 | choice = random.choice(some_exceptions)
6 | print("raising {}".format(choice))
7 | if choice:
8 | raise choice("An error")
9 | except ValueError:
10 | print("Caught a ValueError")
11 | except TypeError:
12 | print("Caught a TypeError")
13 | except Exception as e:
14 | print("Caught some other error: %s" % e.__class__.__name__)
15 | else:
16 | print("This code called if there is no exception")
17 | finally:
18 | print("This cleanup code is always called")
19 |
--------------------------------------------------------------------------------
/Chapter7/7_35_load_save.py:
--------------------------------------------------------------------------------
1 | def save(self):
2 | with open(self.data_file, 'w') as file:
3 | for email, groups in self.email_map.items():
4 | file.write(
5 | '{} {}\n'.format(email, ','.join(groups))
6 | )
7 |
8 | def load(self):
9 | self.email_map = defaultdict(set)
10 | try:
11 | with open(self.data_file) as file:
12 | for line in file:
13 | email, groups = line.strip().split(' ')
14 | groups = set(groups.split(','))
15 | self.email_map[email] = groups
16 | except IOError:
17 | pass
18 |
--------------------------------------------------------------------------------
/Chapter4/4_13_branching_vs_exceptions.py:
--------------------------------------------------------------------------------
1 | def divide_with_exception(number, divisor):
2 | try:
3 | print("{} / {} = {}".format(
4 | number, divisor, number / divisor * 1.0))
5 | except ZeroDivisionError:
6 | print("You can't divide by zero")
7 |
8 | def divide_with_if(number, divisor):
9 | if divisor == 0:
10 | print("You can't divide by zero")
11 | else:
12 | print("{} / {} = {}".format(
13 | number, divisor, number / divisor * 1.0))
14 |
15 | divide_with_exception(10, 5)
16 | divide_with_if(10, 5)
17 | divide_with_if(10, 0)
18 | divide_with_exception(10, 0)
19 |
--------------------------------------------------------------------------------
/Chapter5/5_24_most_basic_document.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self):
3 | self.characters = []
4 | self.cursor = 0
5 | self.filename = ''
6 |
7 | def insert(self, character):
8 | self.characters.insert(self.cursor, character)
9 | self.cursor += 1
10 |
11 | def delete(self):
12 | del self.characters[self.cursor]
13 |
14 | def save(self):
15 | with open(self.filename, 'w') as f:
16 | f.write(''.join(self.characters))
17 |
18 | def forward(self):
19 | self.cursor += 1
20 |
21 | def back(self):
22 | self.cursor -= 1
23 |
--------------------------------------------------------------------------------
/Chapter6/6_17_link_parser.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | LINK_REGEX = re.compile(
6 | "]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 | class LinkCollector:
9 | def __init__(self, url):
10 | self.url = "http://" + urlparse(url).netloc
11 |
12 | def collect_links(self, path="/"):
13 | full_url = self.url + path
14 | page = str(urlopen(full_url).read())
15 | links = LINK_REGEX.findall(page)
16 | print(links)
17 |
18 | if __name__ == "__main__":
19 | LinkCollector(sys.argv[1]).collect_links()
20 |
--------------------------------------------------------------------------------
/Chapter6/6_13_set_operations2.py:
--------------------------------------------------------------------------------
1 | my_artists = {"Sarah Brightman", "Guns N' Roses",
2 | "Opeth", "Vixy and Tony"}
3 |
4 | bands = {"Guns N' Roses", "Opeth"}
5 |
6 | print("my_artists is to bands:")
7 | print("issuperset: {}".format(my_artists.issuperset(bands)))
8 | print("issubset: {}".format(my_artists.issubset(bands)))
9 | print("difference: {}".format(my_artists.difference(bands)))
10 | print("*"*20)
11 | print("bands is to my_artists:")
12 | print("issuperset: {}".format(bands.issuperset(my_artists)))
13 | print("issubset: {}".format(bands.issubset(my_artists)))
14 | print("difference: {}".format(bands.difference(my_artists)))
15 |
--------------------------------------------------------------------------------
/Chapter9/9_12_log_file.log:
--------------------------------------------------------------------------------
1 | Jan 26, 2010 11:25:25 DEBUG This is a debugging message.
2 | Jan 26, 2010 11:25:36 INFO This is an information method.
3 | Jan 26, 2010 11:25:46 WARNING This is a warning. It could be serious.
4 | Jan 26, 2010 11:25:52 WARNING Another warning sent.
5 | Jan 26, 2010 11:25:59 INFO Here's some information.
6 | Jan 26, 2010 11:26:13 DEBUG Debug messages are only useful if you want to figure something out.
7 | Jan 26, 2010 11:26:32 INFO Information is usually harmless, but helpful.
8 | Jan 26, 2010 11:26:40 WARNING Warnings should be heeded.
9 | Jan 26, 2010 11:26:54 WARNING Watch for warnings.
10 |
--------------------------------------------------------------------------------
/Chapter9/9_01_iterator.py:
--------------------------------------------------------------------------------
1 |
2 | class CapitalIterable:
3 | def __init__(self, string):
4 | self.string = string
5 |
6 | def __iter__(self):
7 | return CapitalIterator(self.string)
8 |
9 |
10 | class CapitalIterator:
11 | def __init__(self, string):
12 | self.words = [w.capitalize() for w in string.split()]
13 | self.index = 0
14 |
15 | def __next__(self):
16 | if self.index == len(self.words):
17 | raise StopIteration()
18 |
19 | word = self.words[self.index]
20 | self.index += 1
21 | return word
22 |
23 | def __iter__(self):
24 | return self
25 |
--------------------------------------------------------------------------------
/Chapter9/9_10_set_comprehension.py:
--------------------------------------------------------------------------------
1 | from collections import namedtuple
2 |
3 | Book = namedtuple("Book", "author title genre")
4 | books = [
5 | Book("Pratchett", "Nightwatch", "fantasy"),
6 | Book("Pratchett", "Thief Of Time", "fantasy"),
7 | Book("Le Guin", "The Dispossessed", "scifi"),
8 | Book("Le Guin", "A Wizard Of Earthsea", "fantasy"),
9 | Book("Turner", "The Thief", "fantasy"),
10 | Book("Phillips", "Preston Diamond", "western"),
11 | Book("Phillips", "Twice Upon A Time", "scifi"),
12 | ]
13 |
14 | fantasy_authors = {
15 | b.author for b in books if b.genre == 'fantasy'}
16 |
--------------------------------------------------------------------------------
/Chapter6/6_05_random_key_dict.py:
--------------------------------------------------------------------------------
1 | random_keys = {}
2 | random_keys["astring"] = "somestring"
3 | random_keys[5] = "aninteger"
4 | random_keys[25.2] = "floats work too"
5 | random_keys[("abc", 123)] = "so do tuples"
6 |
7 |
8 | class AnObject:
9 | def __init__(self, avalue):
10 | self.avalue = avalue
11 |
12 | my_object = AnObject(14)
13 | random_keys[my_object] = "We can even store objects"
14 | my_object.avalue = 12
15 | try:
16 | random_keys[[1,2,3]] = "we can't store lists though"
17 | except:
18 | print("unable to store list\n")
19 |
20 | for key, value in random_keys.items():
21 | print("{} has value {}".format(key, value))
22 |
--------------------------------------------------------------------------------
/Chapter9/9_11_dict_comprehension.py:
--------------------------------------------------------------------------------
1 | from collections import namedtuple
2 |
3 | Book = namedtuple("Book", "author title genre")
4 | books = [
5 | Book("Pratchett", "Nightwatch", "fantasy"),
6 | Book("Pratchett", "Thief Of Time", "fantasy"),
7 | Book("Le Guin", "The Dispossessed", "scifi"),
8 | Book("Le Guin", "A Wizard Of Earthsea", "fantasy"),
9 | Book("Turner", "The Thief", "fantasy"),
10 | Book("Phillips", "Preston Diamond", "western"),
11 | Book("Phillips", "Twice Upon A Time", "scifi"),
12 | ]
13 |
14 | fantasy_titles = {
15 | b.title: b for b in books if b.genre == 'fantasy'}
16 |
--------------------------------------------------------------------------------
/Chapter3/3_16_polymorphic_audio.py:
--------------------------------------------------------------------------------
1 | class AudioFile:
2 | def __init__(self, filename):
3 | if not filename.endswith(self.ext):
4 | raise Exception("Invalid file format")
5 |
6 | self.filename = filename
7 |
8 | class MP3File(AudioFile):
9 | ext = "mp3"
10 | def play(self):
11 | print("playing {} as mp3".format(self.filename))
12 |
13 | class WavFile(AudioFile):
14 | ext = "wav"
15 | def play(self):
16 | print("playing {} as wav".format(self.filename))
17 |
18 | class OggFile(AudioFile):
19 | ext = "ogg"
20 | def play(self):
21 | print("playing {} as ogg".format(self.filename))
22 |
--------------------------------------------------------------------------------
/Chapter4/4_15_inventory_handling.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def lock(self, item_type):
3 | pass
4 |
5 | def unlock(self, item_type):
6 | pass
7 |
8 | def purchase(self, item_type):
9 | pass
10 |
11 | item_type = 'widget'
12 | inv = Inventory()
13 | inv.lock(item_type)
14 | try:
15 | num_left = inv.purchase(item_type)
16 | except InvalidItemType:
17 | print("Sorry, we don't sell {}".format(item_type))
18 | except OutOfStock:
19 | print("Sorry, that item is out of stock.")
20 | else:
21 | print("Purchase complete. There are "
22 | "{} {}s left".format(num_left, item_type))
23 | finally:
24 | inv.unlock()
25 |
--------------------------------------------------------------------------------
/Chapter11/11_16_composite_folder_methods.py:
--------------------------------------------------------------------------------
1 | class Folder:
2 | def __init__(self, name):
3 | self.name = name
4 | self.children = {}
5 |
6 | def add_child(self, child):
7 | pass
8 |
9 | def move(self, new_path):
10 | pass
11 |
12 | def copy(self, new_path):
13 | pass
14 |
15 | def delete(self):
16 | pass
17 |
18 | class File:
19 | def __init__(self, name, contents):
20 | self.name = name
21 | self.contents = contents
22 |
23 | def move(self, new_path):
24 | pass
25 |
26 | def copy(self, new_path):
27 | pass
28 |
29 | def delete(self):
30 | pass
31 |
--------------------------------------------------------------------------------
/Chapter5/5_02_distances_by_object.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | class Point:
4 | def __init__(self, x, y):
5 | self.x = x
6 | self.y = y
7 |
8 | def distance(self, p2):
9 | return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
10 |
11 | class Polygon:
12 | def __init__(self):
13 | self.vertices = []
14 |
15 | def add_point(self, point):
16 | self.vertices.append((point))
17 |
18 | def perimeter(self):
19 | perimeter = 0
20 | points = self.vertices + [self.vertices[0]]
21 | for i in range(len(self.vertices)):
22 | perimeter += points[i].distance(points[i+1])
23 | return perimeter
24 |
--------------------------------------------------------------------------------
/Chapter7/7_24_function_object.py:
--------------------------------------------------------------------------------
1 | def my_function():
2 | print("The Function Was Called")
3 | my_function.description = "A silly function"
4 |
5 | def second_function():
6 | print("The second was called")
7 | second_function.description = "A sillier function."
8 |
9 | def another_function(function):
10 | print("The description:", end=" ")
11 | print(function.description)
12 | print("The name:", end=" ")
13 | print(function.__name__)
14 | print("The class:", end=" ")
15 | print(function.__class__)
16 | print("Now I'll call the function passed in")
17 | function()
18 |
19 | another_function(my_function)
20 | another_function(second_function)
21 |
--------------------------------------------------------------------------------
/Chapter9/EXAMPLE_LOG.log:
--------------------------------------------------------------------------------
1 | unrelated log messages
2 | sd 0:0:0:0 Attached Disk Drive
3 | unrelated log messages
4 | sd 0:0:0:0 (SERIAL=ZZ12345)
5 | unrelated log messages
6 | sd 0:0:0:0 [sda] Options
7 | unrelated log messages
8 | XFS ERROR [sda]
9 | unrelated log messages
10 | sd 2:0:0:1 Attached Disk Drive
11 | unrelated log messages
12 | sd 2:0:0:1 (SERIAL=ZZ67890)
13 | unrelated log messages
14 | sd 2:0:0:1 [sdb] Options
15 | unrelated log messages
16 | sd 3:0:1:8 Attached Disk Drive
17 | unrelated log messages
18 | sd 3:0:1:8 (SERIAL=WW11111)
19 | unrelated log messages
20 | sd 3:0:1:8 [sdc] Options
21 | unrelated log messages
22 | XFS ERROR [sdc]
23 | unrelated log messages
24 |
--------------------------------------------------------------------------------
/Chapter7/7_30_send_email_dict_headers.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | from email.mime.text import MIMEText
3 |
4 | def send_email(subject, message, from_addr, *to_addrs,
5 | host="localhost", port=1025, headers=None):
6 |
7 | headers = {} if headers is None else headers
8 |
9 | email = MIMEText(message)
10 | email['Subject'] = subject
11 | email['From'] = from_addr
12 | for header, value in headers.items():
13 | email[header] = value
14 |
15 | sender = smtplib.SMTP(host, port)
16 | for addr in to_addrs:
17 | del email['To']
18 | email['To'] = addr
19 | sender.sendmail(from_addr, addr, email.as_string())
20 | sender.quit()
21 |
--------------------------------------------------------------------------------
/Chapter12/12_04_test_stats.py:
--------------------------------------------------------------------------------
1 | from stats import StatsList
2 | import unittest
3 |
4 | class TestValidInputs(unittest.TestCase):
5 | def setUp(self):
6 | self.stats = StatsList([1,2,2,3,3,4])
7 |
8 | def test_mean(self):
9 | self.assertEqual(self.stats.mean(), 2.5)
10 |
11 | def test_median(self):
12 | self.assertEqual(self.stats.median(), 2.5)
13 | self.stats.append(4)
14 | self.assertEqual(self.stats.median(), 3)
15 |
16 | def test_mode(self):
17 | self.assertEqual(self.stats.mode(), [2,3])
18 | self.stats.remove(2)
19 | self.assertEqual(self.stats.mode(), [3])
20 |
21 | if __name__ == "__main__":
22 | unittest.main()
23 |
--------------------------------------------------------------------------------
/Chapter12/test_mock.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import redis
3 |
4 | class FlightStatusTracker:
5 | ALLOWED_STATUSES = {'CANCELLED', 'DELAYED', 'ON TIME', 'ARRIVED'}
6 |
7 | def __init__(self, redis_instance=None):
8 | self.redis = if redis_instance else redis.StrictRedis()
9 |
10 | def change_status(self, flight, status):
11 | status = status.upper()
12 | if status not in self.ALLOWED_STATUSES:
13 | raise ValueError(
14 | "{} is not a valid status".format(status)
15 | )
16 |
17 | key = "flightno:{}".format(flight)
18 | value = "{}|{}".format(datetime.datetime.now().isoformat(), status)
19 | self.redis.set(key, value)
20 |
--------------------------------------------------------------------------------
/Chapter9/9_16_log_delete_warnings_object.py:
--------------------------------------------------------------------------------
1 | import sys
2 | inname, outname = sys.argv[1:3]
3 |
4 | class WarningFilter:
5 | def __init__(self, insequence):
6 | self.insequence = insequence
7 | def __iter__(self):
8 | return self
9 | def __next__(self):
10 | l = self.insequence.readline()
11 | while l and 'WARNING' not in l:
12 | l = self.insequence.readline()
13 | if not l:
14 | raise StopIteration
15 | return l.replace('\tWARNING', '')
16 |
17 | with open(inname) as infile:
18 | with open(outname, "w") as outfile:
19 | filter = WarningFilter(infile)
20 | for l in filter:
21 | outfile.write(l)
22 |
23 |
--------------------------------------------------------------------------------
/Chapter9/kivy_color_classifier/main.py:
--------------------------------------------------------------------------------
1 | from random import random
2 | import csv
3 |
4 | from kivy.app import App
5 | from kivy.uix.widget import Widget
6 | from kivy.properties import ListProperty
7 |
8 |
9 | class ColorBox(Widget):
10 | color = ListProperty([random(), random(), random()])
11 |
12 |
13 | class ColorClassifierApp(App):
14 | def __init__(self):
15 | super(ColorClassifierApp, self).__init__()
16 | self.file = csv.writer(open("colors.csv", "a"))
17 |
18 | def store_color(self, color_name):
19 | self.file.writerow(self.root.colorbox.color + [color_name])
20 | self.root.colorbox.color = [random(), random(), random()]
21 |
22 | ColorClassifierApp().run()
23 |
--------------------------------------------------------------------------------
/Chapter12/stats.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 |
3 | class StatsList(list):
4 | def mean(self):
5 | return sum(self) / len(self)
6 |
7 | def median(self):
8 | if len(self) % 2:
9 | return self[int(len(self) / 2)]
10 | else:
11 | idx = int(len(self) / 2)
12 | return (self[idx] + self[idx-1]) / 2
13 |
14 | def mode(self):
15 | freqs = defaultdict(int)
16 | for item in self:
17 | freqs[item] += 1
18 | mode_freq = max(freqs.values())
19 | modes = []
20 | for item, value in freqs.items():
21 | if value == mode_freq:
22 | modes.append(item)
23 | return modes
24 |
--------------------------------------------------------------------------------
/Chapter12/12_03_stats.py:
--------------------------------------------------------------------------------
1 | from collections import defaultdict
2 |
3 | class StatsList(list):
4 | def mean(self):
5 | return sum(self) / len(self)
6 |
7 | def median(self):
8 | if len(self) % 2:
9 | return self[int(len(self) / 2)]
10 | else:
11 | idx = int(len(self) / 2)
12 | return (self[idx] + self[idx-1]) / 2
13 |
14 | def mode(self):
15 | freqs = defaultdict(int)
16 | for item in self:
17 | freqs[item] += 1
18 | mode_freq = max(freqs.values())
19 | modes = []
20 | for item, value in freqs.items():
21 | if value == mode_freq:
22 | modes.append(item)
23 | return modes
24 |
--------------------------------------------------------------------------------
/Chapter11/11_13_document_command_callable.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self, filename):
3 | self.filename = filename
4 | self.contents = "This file cannot be modified"
5 |
6 | def save(self):
7 | with open(self.filename, 'w') as file:
8 | file.write(self.contents)
9 |
10 | class KeyboardShortcut:
11 | def keypress(self):
12 | self.command()
13 |
14 | class SaveCommand:
15 | def __init__(self, document):
16 | self.document = document
17 |
18 | def __call__(self):
19 | self.document.save()
20 |
21 | document = Document("a_file.txt")
22 | shortcut = KeyboardShortcut()
23 | save_command = SaveCommand(document)
24 | shortcut.command = save_command
25 |
--------------------------------------------------------------------------------
/Chapter13/13_07_basic_async.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import random
3 |
4 | @asyncio.coroutine
5 | def random_sleep(counter):
6 | delay = random.random() * 5
7 | print("{} sleeps for {:.2f} seconds".format(counter, delay))
8 | yield from asyncio.sleep(delay)
9 | print("{} awakens".format(counter))
10 |
11 | @asyncio.coroutine
12 | def five_sleepers():
13 | print("Creating five tasks")
14 | tasks = [asyncio.async(random_sleep(i)) for i in range(5)]
15 | print("Sleeping after starting five tasks")
16 | yield from asyncio.sleep(2)
17 | print("Waking and waiting for five tasks")
18 | yield from asyncio.wait(tasks)
19 |
20 | asyncio.get_event_loop().run_until_complete(five_sleepers())
21 | print("Done five tasks")
22 |
--------------------------------------------------------------------------------
/Chapter3/3_15_friend_multi_super.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | all_contacts = []
3 |
4 | def __init__(self, name='', email='', **kwargs):
5 | super().__init__(**kwargs)
6 | self.name = name
7 | self.email = email
8 | self.all_contacts.append(self)
9 |
10 | class AddressHolder:
11 | def __init__(self, street='', city='', state='', code='', **kwargs):
12 | super().__init__(**kwargs)
13 | self.street = street
14 | self.city = city
15 | self.state = state
16 | self.code = code
17 |
18 | class Friend(Contact, AddressHolder):
19 | def __init__(self, phone='', **kwargs):
20 | print(kwargs)
21 | super().__init__(**kwargs)
22 | self.phone = phone
23 |
--------------------------------------------------------------------------------
/Chapter3/3_25_remaining_subclasses.py:
--------------------------------------------------------------------------------
1 | class ApartmentRental(Rental, Apartment):
2 | def prompt_init():
3 | init = Apartment.prompt_init()
4 | init.update(Rental.prompt_init())
5 | return init
6 | prompt_init = staticmethod(prompt_init)
7 |
8 | class ApartmentPurchase(Purchase, Apartment):
9 | def prompt_init():
10 | init = Apartment.prompt_init()
11 | init.update(Purchase.prompt_init())
12 | return init
13 | prompt_init = staticmethod(prompt_init)
14 |
15 | class HousePurchase(Purchase, House):
16 | def prompt_init():
17 | init = House.prompt_init()
18 | init.update(Purchase.prompt_init())
19 | return init
20 | prompt_init = staticmethod(prompt_init)
21 |
--------------------------------------------------------------------------------
/Chapter12/12_05_skipping_tests.py:
--------------------------------------------------------------------------------
1 | import unittest
2 | import sys
3 |
4 | class SkipTests(unittest.TestCase):
5 | @unittest.expectedFailure
6 | def test_fails(self):
7 | self.assertEqual(False, True)
8 |
9 | @unittest.skip("Test is useless")
10 | def test_skip(self):
11 | self.assertEqual(False, True)
12 |
13 | @unittest.skipIf(sys.version_info.minor == 4,
14 | "broken on 3.4")
15 | def test_skipif(self):
16 | self.assertEqual(False, True)
17 |
18 | @unittest.skipUnless(sys.platform.startswith('linux'),
19 | "broken unless on linux")
20 | def test_skipunless(self):
21 | self.assertEqual(False, True)
22 |
23 | if __name__ == "__main__":
24 | unittest.main()
25 |
--------------------------------------------------------------------------------
/Chapter6/6_10_object_comparison.py:
--------------------------------------------------------------------------------
1 | from functools import total_ordering
2 |
3 | @total_ordering
4 | class WeirdSortee:
5 | def __init__(self, string, number, sort_num):
6 | self.string = string
7 | self.number = number
8 | self.sort_num = sort_num
9 |
10 | def __lt__(self, object):
11 | if self.sort_num:
12 | return self.number < object.number
13 | return self.string < object.string
14 |
15 | def __repr__(self):
16 | return"{}:{}".format(self.string, self.number)
17 |
18 | def __eq__(self, object):
19 | return all((
20 | self.string == object.string,
21 | self.number == object.number,
22 | self.sort_num == object.number
23 | ))
24 |
--------------------------------------------------------------------------------
/Chapter12/12_17_mock_redis.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import redis
3 |
4 | class FlightStatusTracker:
5 | ALLOWED_STATUSES = {'CANCELLED', 'DELAYED', 'ON TIME', 'ARRIVED'}
6 |
7 |
8 | def __init__(self, redis_instance=None):
9 | self.redis = redis_instance if redis_instance else redis.StrictRedis()
10 |
11 |
12 |
13 | def change_status(self, flight, status):
14 | status = status.upper()
15 | if status not in self.ALLOWED_STATUSES:
16 | raise ValueError(
17 | "{} is not a valid status".format(status)
18 | )
19 |
20 | key = "flightno:{}".format(flight)
21 | value = "{}|{}".format(datetime.datetime.now().isoformat(), status)
22 | self.redis.set(key, value)
23 |
--------------------------------------------------------------------------------
/Chapter13/case_study/decompress_to_bmp.py:
--------------------------------------------------------------------------------
1 | from PIL import Image
2 | import sys
3 |
4 | def decompress(width, height, bytes):
5 | image = Image.new('1', (width, height))
6 |
7 | col = 0
8 | row = 0
9 | for byte in bytes:
10 | color = (byte & 128) >> 7
11 | count = byte & ~128
12 | for i in range(count):
13 | image.putpixel((row, col), color)
14 | row += 1
15 | if not row % width:
16 | col += 1
17 | row = 0
18 | return image
19 |
20 |
21 | with open(sys.argv[1], 'rb') as file:
22 | width = int.from_bytes(file.read(2), 'little')
23 | height = int.from_bytes(file.read(2), 'little')
24 |
25 | image = decompress(width, height, file.read())
26 | image.save(sys.argv[2], 'bmp')
27 |
--------------------------------------------------------------------------------
/Chapter5/5_25_document_cursor.py:
--------------------------------------------------------------------------------
1 | class Cursor:
2 | def __init__(self, document):
3 | self.document = document
4 | self.position = 0
5 |
6 | def forward(self):
7 | self.position += 1
8 |
9 | def back(self):
10 | self.position -= 1
11 |
12 | def home(self):
13 | while self.document.characters[
14 | self.position-1] != '\n':
15 | self.position -= 1
16 | if self.position == 0:
17 | # Got to beginning of file before newline
18 | break
19 |
20 | def end(self):
21 | while self.position < len(self.document.characters
22 | ) and self.document.characters[self.position] != '\n':
23 | self.position += 1
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Chapter10/10_04_logging_decorator.py:
--------------------------------------------------------------------------------
1 | import socket
2 |
3 | class LogSocket:
4 | def __init__(self, socket):
5 | self.socket = socket
6 |
7 | def send(self, data):
8 | print("Sending {0} to {1}".format(
9 | data, self.socket.getpeername()[0]))
10 | self.socket.send(data)
11 |
12 | def close(self):
13 | self.socket.close()
14 |
15 | def respond(client):
16 | response = input("Enter a value: ")
17 | client.send(bytes(response, 'utf8'))
18 | client.close()
19 |
20 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
21 | server.bind(('',2401))
22 | server.listen(1)
23 | try:
24 | while True:
25 | client, addr = server.accept()
26 | respond(LogSocket(client))
27 | finally:
28 | server.close()
29 |
--------------------------------------------------------------------------------
/Chapter8/8_26_json_objects.py:
--------------------------------------------------------------------------------
1 | class Contact:
2 | def __init__(self, first, last):
3 | self.first = first
4 | self.last = last
5 |
6 | @property
7 | def full_name(self):
8 | return("{} {}".format(self.first, self.last))
9 |
10 |
11 | import json
12 | class ContactEncoder(json.JSONEncoder):
13 | def default(self, obj):
14 | if isinstance(obj, Contact):
15 | return {'is_contact': True,
16 | 'first': obj.first,
17 | 'last': obj.last,
18 | 'full': obj.full_name}
19 | return super().default(obj)
20 |
21 | def decode_contact(dic):
22 | if dic.get('is_contact'):
23 | return Contact(dic['first'], dic['last'])
24 | else:
25 | return dic
26 |
--------------------------------------------------------------------------------
/Chapter10/10_09_logging_decorator.py:
--------------------------------------------------------------------------------
1 | import time
2 | def log_calls(func):
3 | def wrapper(*args, **kwargs):
4 | now = time.time()
5 | print("Calling {0} with {1} and {2}".format(
6 | func.__name__, args, kwargs))
7 | return_value = func(*args, **kwargs)
8 | print("Executed {0} in {1}ms".format(
9 | func.__name__, time.time() - now))
10 | return return_value
11 | return wrapper
12 |
13 | def test1(a,b,c):
14 | print("\ttest1 called")
15 |
16 | def test2(a,b):
17 | print("\ttest2 called")
18 |
19 | def test3(a,b):
20 | print("\ttest3 called")
21 | time.sleep(1)
22 |
23 | test1 = log_calls(test1)
24 | test2 = log_calls(test2)
25 | test3 = log_calls(test3)
26 |
27 | test1(1,2,3)
28 | test2(4,b=5)
29 | test3(6,7)
30 |
--------------------------------------------------------------------------------
/Chapter10/10_11_observer_core.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def __init__(self):
3 | self.observers = []
4 | self._product = None
5 | self._quantity = 0
6 |
7 | def attach(self, observer):
8 | self.observers.append(observer)
9 |
10 | @property
11 | def product(self):
12 | return self._product
13 | @product.setter
14 | def product(self, value):
15 | self._product = value
16 | self._update_observers()
17 |
18 | @property
19 | def quantity(self):
20 | return self._quantity
21 | @quantity.setter
22 | def quantity(self, value):
23 | self._quantity = value
24 | self._update_observers()
25 |
26 | def _update_observers(self):
27 | for observer in self.observers:
28 | observer()
29 |
--------------------------------------------------------------------------------
/Chapter13/13_04_prime_factor.py:
--------------------------------------------------------------------------------
1 | import random
2 | from multiprocessing.pool import Pool
3 |
4 | def prime_factor(value):
5 | factors = []
6 | for divisor in range(2, value-1):
7 | quotient, remainder = divmod(value, divisor)
8 | if not remainder:
9 | factors.extend(prime_factor(divisor))
10 | factors.extend(prime_factor(quotient))
11 | break
12 | else:
13 | factors = [value]
14 | return factors
15 |
16 | if __name__ == '__main__':
17 | pool = Pool()
18 |
19 | to_factor = [
20 | random.randint(100000, 50000000) for i in range(20)
21 | ]
22 | results = pool.map(prime_factor, to_factor)
23 | for value, factors in zip(to_factor, results):
24 | print("The factors of {} are {}".format(value, factors))
25 |
--------------------------------------------------------------------------------
/Chapter9/9_21_kernel_log.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 |
4 | def match_regex(filename, regex):
5 | with open(filename) as file:
6 | lines = file.readlines()
7 | for line in reversed(lines):
8 | match = re.match(regex, line)
9 | if match:
10 | regex = yield match.groups()[0]
11 |
12 |
13 | def get_serials(filename):
14 | ERROR_RE = 'XFS ERROR (\[sd[a-z]\])'
15 | matcher = match_regex(filename, ERROR_RE)
16 | device = next(matcher)
17 | while True:
18 | bus = matcher.send('(sd \S+) {}.*'.format(re.escape(device)))
19 | serial = matcher.send('{} \(SERIAL=([^)]*)\)'.format(bus))
20 | yield serial
21 | device = matcher.send(ERROR_RE)
22 |
23 |
24 | for serial_number in get_serials('EXAMPLE_LOG.log'):
25 | print(serial_number)
26 |
--------------------------------------------------------------------------------
/Chapter9/kivy_color_checker/colorchecker.kv:
--------------------------------------------------------------------------------
1 | BoxLayout:
2 | colorbox: colorbox
3 | BoxLayout:
4 | orientation: "vertical"
5 | ColorBox:
6 | id: colorbox
7 | size_hint_y: 2
8 | Label:
9 | text: app.color_name
10 |
11 | BoxLayout:
12 | orientation: "vertical"
13 | Button:
14 | text: "Yes"
15 | on_press: app.process_response(True)
16 | Button:
17 | text: "No"
18 | on_press: app.process_response(False)
19 | Label:
20 | text: "{:.2f}%".format(app.score)
21 |
22 | :
23 | color: app.color
24 | canvas:
25 | Color:
26 | rgb: root.color
27 | Rectangle:
28 | pos: root.pos
29 | size: root.size
30 |
--------------------------------------------------------------------------------
/Chapter13/13_10_async_client.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import random
3 | import json
4 |
5 | @asyncio.coroutine
6 | def remote_sort():
7 | reader, writer = yield from asyncio.open_connection('127.0.0.1', 2015)
8 | print("Generating random list...")
9 | numbers = [random.randrange(10000) for r in range(10000)]
10 | data = json.dumps(numbers).encode()
11 | print("List Generated, Sending data")
12 | writer.write(len(data).to_bytes(8, 'big'))
13 | writer.write(data)
14 |
15 | print("Waiting for data...")
16 | data = yield from reader.readexactly(len(data))
17 | print("Received data")
18 | sorted_values = json.loads(data.decode())
19 | print(sorted_values)
20 | print('\n')
21 | writer.close()
22 |
23 | loop = asyncio.get_event_loop()
24 | loop.run_until_complete(remote_sort())
25 | loop.close()
26 |
--------------------------------------------------------------------------------
/Chapter3/3_14_contrived_diamond_super.py:
--------------------------------------------------------------------------------
1 | class BaseClass:
2 | num_base_calls = 0
3 | def call_me(self):
4 | print("Calling method on Base Class")
5 | self.num_base_calls += 1
6 |
7 | class LeftSubclass(BaseClass):
8 | num_left_calls = 0
9 | def call_me(self):
10 | super().call_me()
11 | print("Calling method on Left Subclass")
12 | self.num_left_calls += 1
13 |
14 | class RightSubclass(BaseClass):
15 | num_right_calls = 0
16 | def call_me(self):
17 | super().call_me()
18 | print("Calling method on Right Subclass")
19 | self.num_right_calls += 1
20 |
21 | class Subclass(LeftSubclass, RightSubclass):
22 | num_sub_calls = 0
23 | def call_me(self):
24 | super().call_me()
25 | print("Calling method on Subclass")
26 | self.num_sub_calls += 1
27 |
--------------------------------------------------------------------------------
/Chapter5/5_03_object_polygon_init.py:
--------------------------------------------------------------------------------
1 | import math
2 |
3 | class Point:
4 | def __init__(self, x, y):
5 | self.x = x
6 | self.y = y
7 |
8 | def distance(self, p2):
9 | return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2)
10 |
11 | class Polygon:
12 | def __init__(self, points = []):
13 | self.vertices = []
14 | for point in points:
15 | if isinstance(point, tuple):
16 | point = Point(*point)
17 | self.vertices.append(point)
18 |
19 | def add_point(self, point):
20 | self.vertices.append((point))
21 |
22 | def perimeter(self):
23 | perimeter = 0
24 | points = self.vertices + [self.vertices[0]]
25 | for i in range(len(self.vertices)):
26 | perimeter += points[i].distance(points[i+1])
27 | return perimeter
28 |
--------------------------------------------------------------------------------
/Chapter11/11_02_age_calculator_adapted.py:
--------------------------------------------------------------------------------
1 | class AgeCalculator:
2 | def __init__(self, birthday):
3 | self.year, self.month, self.day = (
4 | int(x) for x in birthday.split('-'))
5 |
6 | def calculate_age(self, date):
7 | year, month, day = (
8 | int(x) for x in date.split('-'))
9 | age = year - self.year
10 | if (month,day) < (self.month,self.day):
11 | age -= 1
12 | return age
13 |
14 | class DateAgeAdapter:
15 | def _str_date(self, date):
16 | return date.strftime("%Y-%m-%d")
17 |
18 | def __init__(self, birthday):
19 | birthday = self._str_date(birthday)
20 | self.calculator = AgeCalculator(birthday)
21 |
22 | def get_age(self, date):
23 | date = self._str_date(date)
24 | return self.calculator.calculate_age(date)
25 |
--------------------------------------------------------------------------------
/Chapter3/3_18_property.py:
--------------------------------------------------------------------------------
1 | class Property:
2 | def __init__(self, square_feet='', beds='',
3 | baths='', **kwargs):
4 | super().__init__(**kwargs)
5 | self.square_feet = square_feet
6 | self.num_bedrooms = beds
7 | self.num_baths = baths
8 |
9 | def display(self):
10 | print("PROPERTY DETAILS")
11 | print("================")
12 | print("square footage: {}".format(self.square_feet))
13 | print("bedrooms: {}".format(self.num_bedrooms))
14 | print("bathrooms: {}".format(self.num_baths))
15 | print()
16 |
17 | def prompt_init():
18 | return dict(square_feet=input("Enter the square feet: "),
19 | beds=input("Enter number of bedrooms: "),
20 | baths=input("Enter number of baths: "))
21 | prompt_init = staticmethod(prompt_init)
22 |
23 |
--------------------------------------------------------------------------------
/Chapter10/10_05_gzip_decorator.py:
--------------------------------------------------------------------------------
1 | import socket
2 | import gzip
3 | from io import BytesIO
4 |
5 | class GzipSocket:
6 | def __init__(self, socket):
7 | self.socket = socket
8 |
9 | def send(self, data):
10 | buf = BytesIO()
11 | zipfile = gzip.GzipFile(fileobj=buf, mode="w")
12 | zipfile.write(data)
13 | zipfile.close()
14 | self.socket.send(buf.getvalue())
15 |
16 | def close(self):
17 | self.socket.close()
18 |
19 | def respond(client):
20 | response = input("Enter a value: ")
21 | client.send(bytes(response, 'utf8'))
22 | client.close()
23 |
24 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
25 | server.bind(('',2401))
26 | server.listen(1)
27 | try:
28 | while True:
29 | client, addr = server.accept()
30 | respond(GzipSocket(client))
31 | finally:
32 | server.close()
33 |
--------------------------------------------------------------------------------
/Chapter5/zip_processor.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import zipfile
4 | from pathlib import Path
5 |
6 |
7 | class ZipProcessor:
8 | def __init__(self, zipname):
9 | self.zipname = zipname
10 | self.temp_directory = Path("unzipped-{}".format(
11 | zipname[:-4]))
12 |
13 | def process_zip(self):
14 | self.unzip_files()
15 | self.process_files()
16 | self.zip_files()
17 |
18 | def unzip_files(self):
19 | self.temp_directory.mkdir()
20 | with zipfile.ZipFile(self.zipname) as zip:
21 | zip.extractall(str(self.temp_directory))
22 |
23 | def zip_files(self):
24 | with zipfile.ZipFile(self.zipname, 'w') as file:
25 | for filename in self.temp_directory.iterdir():
26 | file.write(str(filename), filename.name)
27 | shutil.rmtree(str(self.temp_directory))
28 |
--------------------------------------------------------------------------------
/Chapter7/timer.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import time
3 |
4 | class TimedEvent:
5 | def __init__(self, endtime, callback):
6 | self.endtime = endtime
7 | self.callback = callback
8 |
9 | def ready(self):
10 | return self.endtime <= datetime.datetime.now()
11 |
12 | class Timer:
13 | def __init__(self):
14 | self.events = []
15 |
16 | def call_after(self, delay, callback):
17 | end_time = datetime.datetime.now() + \
18 | datetime.timedelta(seconds=delay)
19 |
20 | self.events.append(TimedEvent(end_time, callback))
21 |
22 | def run(self):
23 | while True:
24 | ready_events = (e for e in self.events if e.ready())
25 | for event in ready_events:
26 | event.callback(self)
27 | self.events.remove(event)
28 | time.sleep(0.5)
29 |
--------------------------------------------------------------------------------
/Chapter5/5_18_zipprocessor.py:
--------------------------------------------------------------------------------
1 | import os
2 | import shutil
3 | import zipfile
4 | from pathlib import Path
5 |
6 |
7 | class ZipProcessor:
8 | def __init__(self, zipname):
9 | self.zipname = zipname
10 | self.temp_directory = Path("unzipped-{}".format(
11 | zipname[:-4]))
12 |
13 | def process_zip(self):
14 | self.unzip_files()
15 | self.process_files()
16 | self.zip_files()
17 |
18 | def unzip_files(self):
19 | self.temp_directory.mkdir()
20 | with zipfile.ZipFile(self.zipname) as zip:
21 | zip.extractall(str(self.temp_directory))
22 |
23 | def zip_files(self):
24 | with zipfile.ZipFile(self.zipname, 'w') as file:
25 | for filename in self.temp_directory.iterdir():
26 | file.write(str(filename), filename.name)
27 | shutil.rmtree(str(self.temp_directory))
28 |
--------------------------------------------------------------------------------
/Chapter7/7_25_timer.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import time
3 |
4 | class TimedEvent:
5 | def __init__(self, endtime, callback):
6 | self.endtime = endtime
7 | self.callback = callback
8 |
9 | def ready(self):
10 | return self.endtime <= datetime.datetime.now()
11 |
12 | class Timer:
13 | def __init__(self):
14 | self.events = []
15 |
16 | def call_after(self, delay, callback):
17 | end_time = datetime.datetime.now() + \
18 | datetime.timedelta(seconds=delay)
19 |
20 | self.events.append(TimedEvent(end_time, callback))
21 |
22 | def run(self):
23 | while True:
24 | ready_events = (e for e in self.events if e.ready())
25 | for event in ready_events:
26 | event.callback(self)
27 | self.events.remove(event)
28 | time.sleep(0.5)
29 |
--------------------------------------------------------------------------------
/Chapter10/10_18_create_database_for_template.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | conn = sqlite3.connect("sales.db")
4 |
5 | conn.execute("CREATE TABLE Sales (salesperson text, "
6 | "amt currency, year integer, model text, new boolean)")
7 | conn.execute("INSERT INTO Sales values"
8 | " ('Tim', 16000, 2010, 'Honda Fit', 'true')")
9 | conn.execute("INSERT INTO Sales values"
10 | " ('Tim', 9000, 2006, 'Ford Focus', 'false')")
11 | conn.execute("INSERT INTO Sales values"
12 | " ('Gayle', 8000, 2004, 'Dodge Neon', 'false')")
13 | conn.execute("INSERT INTO Sales values"
14 | " ('Gayle', 28000, 2009, 'Ford Mustang', 'true')")
15 | conn.execute("INSERT INTO Sales values"
16 | " ('Gayle', 50000, 2010, 'Lincoln Navigator', 'true')")
17 | conn.execute("INSERT INTO Sales values"
18 | " ('Don', 20000, 2008, 'Toyota Prius', 'false')")
19 | conn.commit()
20 | conn.close()
21 |
--------------------------------------------------------------------------------
/Chapter9/kivy_color_classifier/colorclassifier.kv:
--------------------------------------------------------------------------------
1 | BoxLayout:
2 | colorbox: colorbox
3 | ColorBox:
4 | id: colorbox
5 | GridLayout:
6 | cols: 3
7 | ColorName:
8 | text: "Red"
9 | ColorName:
10 | text: "Purple"
11 | ColorName:
12 | text: "Blue"
13 | ColorName:
14 | text: "Green"
15 | ColorName:
16 | text: "Yellow"
17 | ColorName:
18 | text: "Orange"
19 | ColorName:
20 | text: "Grey"
21 | ColorName:
22 | text: "White"
23 | ColorName:
24 | text: "Pink"
25 |
26 | :
27 | canvas:
28 | Color:
29 | rgb: root.color
30 | Rectangle:
31 | pos: root.pos
32 | size: root.size
33 |
34 | :
35 | on_press: app.store_color(self.text)
36 |
--------------------------------------------------------------------------------
/Chapter10/10_20_template_abstract_implemented.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | class QueryTemplate:
4 | def connect(self):
5 | self.conn = sqlite3.connect("sales.db")
6 |
7 | def construct_query(self):
8 | raise NotImplementedError()
9 |
10 | def do_query(self):
11 | results = self.conn.execute(self.query)
12 | self.results = results.fetchall()
13 |
14 | def format_results(self):
15 | output = []
16 | for row in self.results:
17 | row =[str(i) for i in row]
18 | output.append(", ".join(row))
19 | self.formatted_results = "\n".join(output)
20 |
21 | def output_results(self):
22 | raise NotImplementedError()
23 |
24 | def process_format(self):
25 | self.connect()
26 | self.construct_query()
27 | self.do_query()
28 | self.format_results()
29 | self.output_results()
30 |
--------------------------------------------------------------------------------
/Chapter3/3_12_contrived_diamond.py:
--------------------------------------------------------------------------------
1 | class BaseClass:
2 | num_base_calls = 0
3 | def call_me(self):
4 | print("Calling method on Base Class")
5 | self.num_base_calls += 1
6 |
7 | class LeftSubclass(BaseClass):
8 | num_left_calls = 0
9 | def call_me(self):
10 | BaseClass.call_me(self)
11 | print("Calling method on Left Subclass")
12 | self.num_left_calls += 1
13 |
14 | class RightSubclass(BaseClass):
15 | num_right_calls = 0
16 | def call_me(self):
17 | BaseClass.call_me(self)
18 | print("Calling method on Right Subclass")
19 | self.num_right_calls += 1
20 |
21 | class Subclass(LeftSubclass, RightSubclass):
22 | num_sub_calls = 0
23 | def call_me(self):
24 | LeftSubclass.call_me(self)
25 | RightSubclass.call_me(self)
26 | print("Calling method on Subclass")
27 | self.num_sub_calls += 1
28 |
--------------------------------------------------------------------------------
/Chapter8/8_25_state_pickling.py:
--------------------------------------------------------------------------------
1 | from threading import Timer
2 | import datetime
3 | from urllib.request import urlopen
4 |
5 | class UpdatedURL:
6 | def __init__(self, url):
7 | self.url = url
8 | self.contents = ''
9 | self.last_updated = None
10 | self.update()
11 |
12 | def update(self):
13 | self.contents = urlopen(self.url).read()
14 | self.last_updated = datetime.datetime.now()
15 | self.schedule()
16 |
17 | def schedule(self):
18 | self.timer = Timer(3600, self.update)
19 | self.timer.setDaemon(True)
20 | self.timer.start()
21 |
22 | def __getstate__(self):
23 | new_state = self.__dict__.copy()
24 | if 'timer' in new_state:
25 | del new_state['timer']
26 | return new_state
27 |
28 | def __setstate__(self, data):
29 | self.__dict__ = data
30 | self.schedule()
31 |
--------------------------------------------------------------------------------
/Chapter8/8_27_template_boilerplate.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import json
4 | from pathlib import Path
5 |
6 | DIRECTIVE_RE = re.compile(
7 | r'/\*\*\s*(include|variable|loopover|endloop|loopvar)'
8 | r'\s*([^ *]*)\s*\*\*/')
9 |
10 |
11 | class TemplateEngine:
12 | def __init__(self, infilename, outfilename, contextfilename):
13 | self.template = open(infilename).read()
14 | self.working_dir = Path(infilename).absolute().parent
15 | self.pos = 0
16 | self.outfile = open(outfilename, 'w')
17 | with open(contextfilename) as contextfile:
18 | self.context = json.load(contextfile)
19 |
20 | def process(self):
21 | print("PROCESSING...")
22 |
23 |
24 | if __name__ == '__main__':
25 | infilename, outfilename, contextfilename = sys.argv[1:]
26 | engine = TemplateEngine(infilename, outfilename, contextfilename)
27 | engine.process()
28 |
--------------------------------------------------------------------------------
/Chapter4/4_14_inventory_mock_object.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def lock(self, item_type):
3 | '''Select the type of item that is going to
4 | be manipulated. This method will lock the
5 | item so nobody else can manipulate the
6 | inventory until it's returned. This prevents
7 | selling the same item to two different
8 | customers.'''
9 | pass
10 |
11 | def unlock(self, item_type):
12 | '''Release the given type so that other
13 | customers can access it.'''
14 | pass
15 |
16 | def purchase(self, item_type):
17 | '''If the item is not locked, raise an
18 | exception. If the itemtype does not exist,
19 | raise an exception. If the item is currently
20 | out of stock, raise an exception. If the item
21 | is available, subtract one item and return
22 | the number of items left.'''
23 | pass
24 |
25 |
--------------------------------------------------------------------------------
/Chapter5/5_19_zipreplace_inheritance.py:
--------------------------------------------------------------------------------
1 | from zip_processor import ZipProcessor
2 | import sys
3 | import os
4 |
5 | class ZipReplace(ZipProcessor):
6 | def __init__(self, filename, search_string,
7 | replace_string):
8 | super().__init__(filename)
9 | self.search_string = search_string
10 | self.replace_string = replace_string
11 |
12 | def process_files(self):
13 | '''perform a search and replace on all files in the
14 | temporary directory'''
15 | for filename in self.temp_directory.iterdir():
16 | with filename.open() as file:
17 | contents = file.read()
18 | contents = contents.replace(
19 | self.search_string, self.replace_string)
20 | with filename.open("w") as file:
21 | file.write(contents)
22 |
23 | if __name__ == "__main__":
24 | ZipReplace(*sys.argv[1:4]).process_zip()
25 |
--------------------------------------------------------------------------------
/Chapter11/11_06_flyweight_init.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
14 | def __init__(self, model_name, air=False, tilt=False,
15 | cruise_control=False, power_locks=False,
16 | alloy_wheels=False, usb_charger=False):
17 | if not hasattr(self, "initialized"):
18 | self.model_name = model_name
19 | self.air = air
20 | self.tilt = tilt
21 | self.cruise_control = cruise_control
22 | self.power_locks = power_locks
23 | self.alloy_wheels = alloy_wheels
24 | self.usb_charger = usb_charger
25 | self.initialized=True
26 |
--------------------------------------------------------------------------------
/Chapter12/12_12_pytest_echo.py:
--------------------------------------------------------------------------------
1 | import subprocess
2 | import socket
3 | import time
4 |
5 | def pytest_funcarg__echoserver(request):
6 | def setup():
7 | p = subprocess.Popen(
8 | ['python3', '12_11_echo_server.py'])
9 | time.sleep(1)
10 | return p
11 |
12 | def cleanup(p):
13 | p.terminate()
14 |
15 | return request.cached_setup(
16 | setup=setup,
17 | teardown=cleanup,
18 | scope="session")
19 |
20 | def pytest_funcarg__clientsocket(request):
21 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
22 | s.connect(('localhost', 1028))
23 | request.addfinalizer(lambda: s.close())
24 | return s
25 |
26 | def test_echo(echoserver, clientsocket):
27 | clientsocket.send(b"abc")
28 | assert clientsocket.recv(3) == b'abc'
29 |
30 | def test_echo2(echoserver, clientsocket):
31 | clientsocket.send(b"def")
32 | assert clientsocket.recv(3) == b'def'
33 |
--------------------------------------------------------------------------------
/Chapter3/3_28_add_property.py:
--------------------------------------------------------------------------------
1 | class Agent:
2 | type_map = {
3 | ("house", "rental"): HouseRental,
4 | ("house", "purchase"): HousePurchase,
5 | ("apartment", "rental"): ApartmentRental,
6 | ("apartment", "purchase"): ApartmentPurchase
7 | }
8 |
9 | def __init__(self):
10 | self.property_list = []
11 |
12 | def display_properties():
13 | for property in self.property_list:
14 | property.display()
15 |
16 | def add_property(self):
17 | property_type = get_valid_input(
18 | "What type of property? ",
19 | ("house", "apartment")).lower()
20 | payment_type = get_valid_input(
21 | "What payment type? ",
22 | ("purchase", "rental")).lower()
23 |
24 | PropertyClass = self.type_map[(property_type, payment_type)]
25 | init_args = PropertyClass.prompt_init()
26 | self.property_list.append(PropertyClass(**init_args))
27 |
--------------------------------------------------------------------------------
/Chapter7/7_22_all_arguments.py:
--------------------------------------------------------------------------------
1 | import shutil
2 | import os.path
3 | def augmented_move(target_folder, *filenames,
4 | verbose=False, **specific):
5 | '''Move all filenames into the target_folder, allowing
6 | specific treatment of certain files.'''
7 |
8 | def print_verbose(message, filename):
9 | '''print the message only if verbose is enabled'''
10 | if verbose:
11 | print(message.format(filename))
12 |
13 | for filename in filenames:
14 | target_path = os.path.join(target_folder, filename)
15 | if filename in specific:
16 | if specific[filename] == 'ignore':
17 | print_verbose("Ignoring {0}", filename)
18 | elif specific[filename] == 'copy':
19 | print_verbose("Copying {0}", filename)
20 | shutil.copyfile(filename, target_path)
21 | else:
22 | print_verbose("Moving {0}", filename)
23 | shutil.move(filename, target_path)
24 |
--------------------------------------------------------------------------------
/Chapter9/9_19_yield_from_filesystem.py:
--------------------------------------------------------------------------------
1 |
2 | class File:
3 | def __init__(self, name):
4 | self.name = name
5 |
6 |
7 | class Folder(File):
8 | def __init__(self, name):
9 | super().__init__(name)
10 | self.children = []
11 |
12 | root = Folder('')
13 | etc = Folder('etc')
14 | root.children.append(etc)
15 | etc.children.append(File('passwd'))
16 | etc.children.append(File('groups'))
17 | httpd = Folder('httpd')
18 | etc.children.append(httpd)
19 | httpd.children.append(File('http.conf'))
20 | var = Folder('var')
21 | root.children.append(var)
22 | log = Folder('log')
23 | var.children.append(log)
24 | log.children.append(File('messages'))
25 | log.children.append(File('kernel'))
26 |
27 |
28 | def walk(file):
29 | if isinstance(file, Folder):
30 | yield file.name + '/'
31 | for f in file.children:
32 | yield from walk(f)
33 | else:
34 | yield file.name
35 |
36 | for filename in walk(root):
37 | print(filename)
38 |
--------------------------------------------------------------------------------
/Chapter6/6_16_dictsorted.py:
--------------------------------------------------------------------------------
1 | from collections import KeysView, ItemsView, ValuesView
2 | class DictSorted(dict):
3 | def __new__(*args, **kwargs):
4 | new_dict = dict.__new__(*args, **kwargs)
5 | new_dict.ordered_keys = []
6 | return new_dict
7 |
8 | def __setitem__(self, key, value):
9 | '''self[key] = value syntax'''
10 | if key not in self.ordered_keys:
11 | self.ordered_keys.append(key)
12 | super().__setitem__(key, value)
13 |
14 | def setdefault(self, key, value):
15 | if key not in self.ordered_keys:
16 | self.ordered_keys.append(key)
17 | return super().setdefault(key, value)
18 |
19 | def keys(self):
20 | return KeysView(self)
21 |
22 | def values(self):
23 | return ValuesView(self)
24 |
25 | def items(self):
26 | return ItemsView(self)
27 |
28 | def __iter__(self):
29 | '''for x in self syntax'''
30 | return self.ordered_keys.__iter__()
31 |
--------------------------------------------------------------------------------
/Chapter7/7_26_timer_test.py:
--------------------------------------------------------------------------------
1 | from timer import Timer
2 | import datetime
3 |
4 | def format_time(message, *args):
5 | now = datetime.datetime.now().strftime("%I:%M:%S")
6 | print(message.format(*args, now=now))
7 |
8 | def one(timer):
9 | format_time("{now}: Called One")
10 |
11 | def two(timer):
12 | format_time("{now}: Called Two")
13 |
14 | def three(timer):
15 | format_time("{now}: Called Three")
16 |
17 | class Repeater:
18 | def __init__(self):
19 | self.count = 0
20 | def repeater(self, timer):
21 | format_time("{now}: repeat {0}", self.count)
22 | self.count += 1
23 | timer.call_after(5, self.repeater)
24 |
25 | timer = Timer()
26 | timer.call_after(1, one)
27 | timer.call_after(2, one)
28 | timer.call_after(2, two)
29 | timer.call_after(4, two)
30 | timer.call_after(3, three)
31 | timer.call_after(6, three)
32 | repeater = Repeater()
33 | timer.call_after(5, repeater.repeater)
34 | format_time("{now}: Starting")
35 | timer.run()
36 |
--------------------------------------------------------------------------------
/Chapter10/10_12_observer_observing.py:
--------------------------------------------------------------------------------
1 | class Inventory:
2 | def __init__(self):
3 | self.observers = []
4 | self._product = None
5 | self._quantity = 0
6 |
7 | def attach(self, observer):
8 | self.observers.append(observer)
9 |
10 | @property
11 | def product(self):
12 | return self._product
13 | @product.setter
14 | def product(self, value):
15 | self._product = value
16 | self._update_observers()
17 |
18 | @property
19 | def quantity(self):
20 | return self._quantity
21 | @quantity.setter
22 | def quantity(self, value):
23 | self._quantity = value
24 | self._update_observers()
25 |
26 | def _update_observers(self):
27 | for observer in self.observers:
28 | observer()
29 |
30 | class ConsoleObserver:
31 | def __init__(self, inventory):
32 | self.inventory = inventory
33 |
34 | def __call__(self):
35 | print(self.inventory.product)
36 | print(self.inventory.quantity)
37 |
--------------------------------------------------------------------------------
/Chapter11/11_10_window_command_invokers.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class Document:
8 | def __init__(self, filename):
9 | self.filename = filename
10 | self.contents = "This file cannot be modified"
11 |
12 | def save(self):
13 | with open(self.filename, 'w') as file:
14 | file.write(self.contents)
15 |
16 | class ToolbarButton:
17 | def __init__(self, name, iconname):
18 | self.name = name
19 | self.iconname = iconname
20 |
21 | def click(self):
22 | self.command.execute()
23 |
24 | class MenuItem:
25 | def __init__(self, menu_name, menuitem_name):
26 | self.menu = menu_name
27 | self.item = menuitem_name
28 |
29 | def click(self):
30 | self.command.execute()
31 |
32 | class KeyboardShortcut:
33 | def __init__(self, key, modifier):
34 | self.key = key
35 | self.modifier = modifier
36 |
37 | def keypress(self):
38 | self.command.execute()
39 |
--------------------------------------------------------------------------------
/Chapter11/11_17_component_hierarchy.py:
--------------------------------------------------------------------------------
1 | class Component:
2 | def __init__(self, name):
3 | self.name = name
4 |
5 | def move(self, new_path):
6 | new_folder =get_path(new_path)
7 | del self.parent.children[self.name]
8 | new_folder.children[self.name] = self
9 | self.parent = new_folder
10 |
11 | def delete(self):
12 | del self.parent.children[self.name]
13 |
14 | class Folder(Component):
15 | def __init__(self, name):
16 | super().__init__(name)
17 | self.children = {}
18 |
19 | def add_child(self, child):
20 | pass
21 |
22 | def copy(self, new_path):
23 | pass
24 |
25 | class File(Component):
26 | def __init__(self, name, contents):
27 | super().__init__(name)
28 | self.contents = contents
29 |
30 | def copy(self, new_path):
31 | pass
32 |
33 | root = Folder('')
34 | def get_path(path):
35 | names = path.split('/')[1:]
36 | node = root
37 | for name in names:
38 | node = node.children[name]
39 | return node
40 |
41 |
--------------------------------------------------------------------------------
/Chapter11/11_18_add_child.py:
--------------------------------------------------------------------------------
1 | class Component:
2 | def __init__(self, name):
3 | self.name = name
4 |
5 | def move(self, new_path):
6 | new_folder =get_path(new_path)
7 | del self.parent.children[self.name]
8 | new_folder.children[self.name] = self
9 | self.parent = new_folder
10 |
11 | def delete(self):
12 | del self.parent.children[self.name]
13 |
14 | class Folder(Component):
15 | def __init__(self, name):
16 | super().__init__(name)
17 | self.children = {}
18 |
19 | def add_child(self, child):
20 | child.parent = self
21 | self.children[child.name] = child
22 |
23 | def copy(self, new_path):
24 | pass
25 |
26 | class File(Component):
27 | def __init__(self, name, contents):
28 | super().__init__(name)
29 | self.contents = contents
30 |
31 | def copy(self, new_path):
32 | pass
33 |
34 | root = Folder('')
35 | def get_path(path):
36 | names = path.split('/')[1:]
37 | node = root
38 | for name in names:
39 | node = node.children[name]
40 | return node
41 |
42 |
--------------------------------------------------------------------------------
/Chapter6/6_19_visited_links_sets.py:
--------------------------------------------------------------------------------
1 | class LinkCollector:
2 | def __init__(self, url):
3 | self.url = "http://" + urlparse(url).netloc
4 | self.collected_links = set()
5 | self.visited_links = set()
6 |
7 | def collect_links(self, path="/"):
8 | full_url = self.url + path
9 | self.visited_links.add(full_url)
10 | page = str(urlopen(full_url).read())
11 | links = LINK_REGEX.findall(page)
12 | links = {self.normalize_url(path, link
13 | ) for link in links}
14 | self.collected_links = links.union(
15 | self.collected_links)
16 | unvisited_links = links.difference(
17 | self.visited_links)
18 | print(links, self.visited_links,
19 | self.collected_links, unvisited_links)
20 |
21 | def normalize_url(self, path, link):
22 | if link.startswith("http://"):
23 | return link
24 | elif link.startswith("/"):
25 | return self.url + link
26 | else:
27 | return self.url + path.rpartition('/'
28 | )[0] + '/' + link
29 |
--------------------------------------------------------------------------------
/Chapter13/13_06_futures.py:
--------------------------------------------------------------------------------
1 | from concurrent.futures import ThreadPoolExecutor
2 | from pathlib import Path
3 | from os.path import sep as pathsep
4 | from collections import deque
5 |
6 | def find_files(path, query_string):
7 | subdirs = []
8 | for p in path.iterdir():
9 | full_path = str(p.absolute())
10 | if p.is_dir() and not p.is_symlink():
11 | subdirs.append(p)
12 | if query_string in full_path:
13 | print(full_path)
14 |
15 | return subdirs
16 |
17 | query = '.py'
18 | futures = deque()
19 | basedir = Path(pathsep).absolute()
20 |
21 | with ThreadPoolExecutor(max_workers=10) as executor:
22 | futures.append(
23 | executor.submit(find_files, basedir, query))
24 | while futures:
25 | future = futures.popleft()
26 | if future.exception():
27 | continue
28 | elif future.done():
29 | subdirs = future.result()
30 | for subdir in subdirs:
31 | futures.append(
32 | executor.submit(find_files, subdir, query))
33 | else:
34 | futures.append(future)
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2019 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 |
--------------------------------------------------------------------------------
/Chapter13/13_05_post_search.py:
--------------------------------------------------------------------------------
1 | def search(paths, query_q, results_q):
2 | lines = []
3 | for path in paths:
4 | lines.extend(l.strip() for l in path.open())
5 |
6 | query = query_q.get()
7 | while query:
8 | results_q.put([l for l in lines if query in l])
9 | query = query_q.get()
10 |
11 | if __name__ == '__main__':
12 | from multiprocessing import Process, Queue, cpu_count
13 | from path import path
14 | cpus = cpu_count()
15 | pathnames = [f for f in path('.').listdir() if f.isfile()]
16 | paths = [pathnames[i::cpus] for i in range(cpus)]
17 | query_queues = [Queue() for p in range(cpus)]
18 | results_queue = Queue()
19 |
20 | search_procs = [
21 | Process(target=search, args=(p, q, results_queue))
22 | for p, q in zip(paths, query_queues)
23 | ]
24 | for proc in search_procs: proc.start()
25 |
26 | for q in query_queues:
27 | q.put("def")
28 | q.put(None) # Signal process termination
29 |
30 | for i in range(cpus):
31 | for match in results_queue.get():
32 | print(match)
33 | for proc in search_procs: proc.join()
34 |
--------------------------------------------------------------------------------
/Chapter3/3_21_apartment_nice_prompt.py:
--------------------------------------------------------------------------------
1 | class Apartment(Property):
2 | valid_laundries = ("coin", "ensuite", "none")
3 | valid_balconies = ("yes", "no", "solarium")
4 |
5 | def __init__(self, balcony='', laundry='', **kwargs):
6 | super().__init__(**kwargs)
7 | self.balcony = balcony
8 | self.laundry = laundry
9 |
10 | def display(self):
11 | super().display()
12 | print("APARTMENT DETAILS")
13 | print("laundry: {}".format(self.laundry))
14 | print("has balcony: {}".format(self.balcony))
15 |
16 | def prompt_init():
17 | parent_init = Property.prompt_init()
18 | laundry = get_valid_input(
19 | "What laundry facilities does "
20 | "the property have? ",
21 | Apartment.valid_laundries)
22 | balcony = get_valid_input(
23 | "Does the property have a balcony? ",
24 | Apartment.valid_balconies)
25 | parent_init.update({
26 | "laundry": laundry,
27 | "balcony": balcony
28 | })
29 | return parent_init
30 | prompt_init = staticmethod(prompt_init)
31 |
--------------------------------------------------------------------------------
/Chapter13/13_02_thread_wait.py:
--------------------------------------------------------------------------------
1 | from threading import Thread
2 | import json
3 | from urllib.request import urlopen
4 | import time
5 |
6 | CITIES = [
7 | 'Edmonton', 'Victoria', 'Winnipeg', 'Fredericton',
8 | "St. John's", 'Halifax', 'Toronto', 'Charlottetown',
9 | 'Quebec City', 'Regina'
10 | ]
11 |
12 | class TempGetter(Thread):
13 | def __init__(self, city):
14 | super().__init__()
15 | self.city = city
16 |
17 | def run(self):
18 | url_template = (
19 | 'http://api.openweathermap.org/data/2.5/'
20 | 'weather?q={},CA&units=metric')
21 | response = urlopen(url_template.format(self.city))
22 | data = json.loads(response.read().decode())
23 | self.temperature = data['main']['temp']
24 |
25 | threads = [TempGetter(c) for c in CITIES]
26 | start = time.time()
27 | for thread in threads:
28 | thread.start()
29 |
30 | for thread in threads:
31 | thread.join()
32 |
33 | for thread in threads:
34 | print(
35 | "it is {0.temperature:.0f}°C in {0.city}".format(thread))
36 | print(
37 | "Got {} temps in {} seconds".format(
38 | len(threads), time.time() - start))
39 |
--------------------------------------------------------------------------------
/Chapter11/11_07_flyweight_check_serial.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
14 | def __init__(self, model_name, air=False, tilt=False,
15 | cruise_control=False, power_locks=False,
16 | alloy_wheels=False, usb_charger=False):
17 | if not hasattr(self, "initted"):
18 | self.model_name = model_name
19 | self.air = air
20 | self.tilt = tilt
21 | self.cruise_control = cruise_control
22 | self.power_locks = power_locks
23 | self.alloy_wheels = alloy_wheels
24 | self.usb_charger = usb_charger
25 | self.initted=True
26 |
27 | def check_serial(self, serial_number):
28 | print("Sorry, we are unable to check "
29 | "the serial number {0} on the {1} "
30 | "at this time".format(
31 | serial_number, self.model_name))
32 |
33 |
--------------------------------------------------------------------------------
/Chapter4/4_16_auth_user.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | class User:
4 | def __init__(self, username, password):
5 | '''Create a new user object. The password
6 | will be encrypted before storing.'''
7 | self.username = username
8 | self.password = self._encrypt_pw(password)
9 | self.is_logged_in = False
10 |
11 | def _encrypt_pw(self, password):
12 | '''Encrypt the password with the username and return
13 | the sha digest.'''
14 | hash_string = (self.username + password)
15 | hash_string = hash_string.encode("utf8")
16 | return hashlib.sha256(hash_string).hexdigest()
17 |
18 | def check_password(self, password):
19 | '''Return True if the password is valid for this
20 | user, false otherwise.'''
21 | encrypted = self._encrypt_pw(password)
22 | return encrypted == self.password
23 |
24 |
25 | class AuthException(Exception):
26 | def __init__(self, username, user=None):
27 | super().__init__(username)
28 | self.username = username
29 | self.user = user
30 |
31 | class UsernameAlreadyExists(AuthException):
32 | pass
33 |
34 | class PasswordTooShort(AuthException):
35 | pass
36 |
--------------------------------------------------------------------------------
/Chapter12/12_08_setup_teardown.py:
--------------------------------------------------------------------------------
1 | def setup_module(module):
2 | print("setting up MODULE {0}".format(
3 | module.__name__))
4 |
5 | def teardown_module(module):
6 | print("tearing down MODULE {0}".format(
7 | module.__name__))
8 |
9 | def test_a_function():
10 | print("RUNNING TEST FUNCTION")
11 |
12 | class BaseTest:
13 | def setup_class(cls):
14 | print("setting up CLASS {0}".format(
15 | cls.__name__))
16 |
17 | def teardown_class(cls):
18 | print("tearing down CLASS {0}\n".format(
19 | cls.__name__))
20 |
21 | def setup_method(self, method):
22 | print("setting up METHOD {0}".format(
23 | method.__name__))
24 |
25 | def teardown_method(self, method):
26 | print("tearing down METHOD {0}".format(
27 | method.__name__))
28 |
29 | class TestClass1(BaseTest):
30 | def test_method_1(self):
31 | print("RUNNING METHOD 1-1")
32 |
33 | def test_method_2(self):
34 | print("RUNNING METHOD 1-2")
35 |
36 | class TestClass2(BaseTest):
37 | def test_method_1(self):
38 | print("RUNNING METHOD 2-1")
39 |
40 | def test_method_2(self):
41 | print("RUNNING METHOD 2-2")
42 |
--------------------------------------------------------------------------------
/Chapter3/3_22_house.py:
--------------------------------------------------------------------------------
1 | class House(Property):
2 | valid_garage = ("attached", "detached", "none")
3 | valid_fenced = ("yes", "no")
4 |
5 | def __init__(self, num_stories='',
6 | garage='', fenced='', **kwargs):
7 | super().__init__(**kwargs)
8 | self.garage = garage
9 | self.fenced = fenced
10 | self.num_stories = num_stories
11 |
12 | def display(self):
13 | super().display()
14 | print("HOUSE DETAILS")
15 | print("# of stories: {}".format(self.num_stories))
16 | print("garage: {}".format(self.garage))
17 | print("fenced yard: {}".format(self.fenced))
18 |
19 | def prompt_init():
20 | parent_init = Property.prompt_init()
21 | fenced = get_valid_input("Is the yard fenced? ",
22 | House.valid_fenced)
23 | garage = get_valid_input("Is there a garage? ",
24 | House.valid_garage)
25 | num_stories = input("How many stories? ")
26 |
27 | parent_init.update({
28 | "fenced": fenced,
29 | "garage": garage,
30 | "num_stories": num_stories
31 | })
32 | return parent_init
33 | prompt_init = staticmethod(prompt_init)
34 |
--------------------------------------------------------------------------------
/Chapter8/8_28_template_process.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import json
4 | from pathlib import Path
5 |
6 | DIRECTIVE_RE = re.compile(
7 | r'/\*\*\s*(include|variable|loopover|endloop|loopvar)'
8 | r'\s*([^ *]*)\s*\*\*/')
9 |
10 |
11 | class TemplateEngine:
12 | def __init__(self, infilename, outfilename, contextfilename):
13 | self.template = open(infilename).read()
14 | self.working_dir = Path(infilename).absolute().parent
15 | self.pos = 0
16 | self.outfile = open(outfilename, 'w')
17 | with open(contextfilename) as contextfile:
18 | self.context = json.load(contextfile)
19 |
20 | def process(self):
21 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
22 | while match:
23 | self.outfile.write(self.template[self.pos:match.start()])
24 | directive, argument = match.groups()
25 | self.pos = match.end()
26 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
27 | self.outfile.write(self.template[self.pos:])
28 |
29 | if __name__ == '__main__':
30 | infilename, outfilename, contextfilename = sys.argv[1:]
31 | engine = TemplateEngine(infilename, outfilename, contextfilename)
32 | engine.process()
33 |
--------------------------------------------------------------------------------
/Chapter9/kivy_color_checker/main.py:
--------------------------------------------------------------------------------
1 | from random import random
2 | import csv
3 |
4 | from kivy.app import App
5 | from kivy.uix.widget import Widget
6 | from kivy.properties import ListProperty, NumericProperty, StringProperty
7 |
8 |
9 | class ColorBox(Widget):
10 | color = ListProperty([random(), random(), random()])
11 |
12 |
13 | class ColorCheckerApp(App):
14 | score = NumericProperty()
15 | color_name = StringProperty()
16 | color = ListProperty([0, 0, 0])
17 | total = NumericProperty()
18 | correct = NumericProperty()
19 |
20 | def __init__(self):
21 | super(ColorCheckerApp, self).__init__()
22 | self.file = csv.reader(open("output.csv", "r"))
23 | self.advance_color()
24 |
25 | def advance_color(self):
26 | try:
27 | r, g, b, name = next(self.file)
28 | except StopIteration:
29 | self.root.disabled = True
30 | else:
31 | self.color = float(r), float(g), float(b)
32 | self.color_name = name
33 |
34 | def process_response(self, is_correct):
35 | if is_correct:
36 | self.correct += 1
37 | self.total += 1
38 | self.score = 100.0 * self.correct / self.total
39 | self.advance_color()
40 |
41 |
42 | ColorCheckerApp().run()
43 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/vigenere_cipher.py:
--------------------------------------------------------------------------------
1 | def combine_character(plain, keyword):
2 | plain = plain.upper()
3 | keyword = keyword.upper()
4 | plain_num = ord(plain) - ord('A')
5 | keyword_num = ord(keyword) - ord('A')
6 | return chr(ord('A') + (plain_num + keyword_num) % 26)
7 |
8 | def separate_character(cypher, keyword):
9 | cypher = cypher.upper()
10 | keyword = keyword.upper()
11 | cypher_num = ord(cypher) - ord('A')
12 | keyword_num = ord(keyword) - ord('A')
13 | return chr(ord('A') + (cypher_num - keyword_num) % 26)
14 |
15 | class VigenereCipher:
16 | def __init__(self, keyword):
17 | self.keyword = keyword
18 |
19 | def extend_keyword(self, number):
20 | repeats = number // len(self.keyword) + 1
21 | return (self.keyword * repeats)[:number]
22 |
23 | def _code(self, text, combine_func):
24 | text = text.replace(" ", "").upper()
25 | combined = []
26 | keyword = self.extend_keyword(len(text))
27 | for p,k in zip(text, keyword):
28 | combined.append(combine_func(p,k))
29 | return "".join(combined)
30 |
31 | def encode(self, plaintext):
32 | return self._code(plaintext, combine_character)
33 |
34 | def decode(self, ciphertext):
35 | return self._code(ciphertext, separate_character)
36 |
--------------------------------------------------------------------------------
/Chapter5/5_26_document_using_cursor.py:
--------------------------------------------------------------------------------
1 | class Cursor:
2 | def __init__(self, document):
3 | self.document = document
4 | self.position = 0
5 |
6 | def forward(self):
7 | self.position += 1
8 |
9 | def back(self):
10 | self.position -= 1
11 |
12 | def home(self):
13 | while self.document.characters[
14 | self.position-1] != '\n':
15 | self.position -= 1
16 | if self.position == 0:
17 | # Got to beginning of file before newline
18 | break
19 |
20 | def end(self):
21 | while self.position < len(self.document.characters
22 | ) and self.document.characters[self.position] != '\n':
23 | self.position += 1
24 |
25 |
26 | class Document:
27 | def __init__(self):
28 | self.characters = []
29 | self.cursor = Cursor(self)
30 | self.filename = ''
31 |
32 | def insert(self, character):
33 | self.characters.insert(self.cursor.position,
34 | character)
35 | self.cursor.forward()
36 |
37 | def delete(self):
38 | del self.characters[self.cursor.position]
39 |
40 | def save(self):
41 | f = open(self.filename, 'w')
42 | f.write(''.join(self.characters))
43 | f.close()
44 |
--------------------------------------------------------------------------------
/Chapter13/13_09_async_multiprocessing.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | import json
3 | from concurrent.futures import ProcessPoolExecutor
4 |
5 | def sort_in_process(data):
6 | nums = json.loads(data.decode())
7 | curr = 1
8 | while curr < len(nums):
9 | if nums[curr] >= nums[curr-1]:
10 | curr += 1
11 | else:
12 | nums[curr], nums[curr-1] = \
13 | nums[curr-1], nums[curr]
14 | if curr > 1:
15 | curr -= 1
16 |
17 | return json.dumps(nums).encode()
18 |
19 | @asyncio.coroutine
20 | def sort_request(reader, writer):
21 | print("Received connection")
22 | length = yield from reader.read(8)
23 | data = yield from reader.readexactly(int.from_bytes(length, 'big'))
24 | result = yield from asyncio.get_event_loop().run_in_executor(None, sort_in_process, data)
25 | print("Sorted list")
26 | writer.write(result)
27 | writer.close()
28 | print("Connection closed")
29 |
30 | loop = asyncio.get_event_loop()
31 | loop.set_default_executor(ProcessPoolExecutor())
32 | server = loop.run_until_complete(
33 | asyncio.start_server(sort_request, '127.0.0.1', 2015))
34 | print("Sort Service running")
35 |
36 | loop.run_forever()
37 | server.close()
38 | loop.run_until_complete(server.wait_closed())
39 | loop.close()
40 |
--------------------------------------------------------------------------------
/Chapter11/11_04_email_facade.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | import imaplib
3 |
4 | class EmailFacade:
5 | def __init__(self, host, username, password):
6 | self.host = host
7 | self.username = username
8 | self.password = password
9 |
10 | def send_email(self, to_email, subject, message):
11 | if not "@" in self.username:
12 | from_email = "{0}@{1}".format(
13 | self.username, self.host)
14 | else:
15 | from_email = self.username
16 | message = ("From: {0}\r\n"
17 | "To: {1}\r\n"
18 | "Subject: {2}\r\n\r\n{3}").format(
19 | from_email,
20 | to_email,
21 | subject,
22 | message)
23 |
24 | smtp = smtplib.SMTP(self.host)
25 | smtp.login(self.username, self.password)
26 | smtp.sendmail(from_email, [to_email], message)
27 |
28 | def get_inbox(self):
29 | mailbox = imaplib.IMAP4(self.host)
30 | mailbox.login(bytes(self.username, 'utf8'),
31 | bytes(self.password, 'utf8'))
32 | mailbox.select()
33 | x, data = mailbox.search(None, 'ALL')
34 | messages = []
35 | for num in data[0].split():
36 | x, message = mailbox.fetch(num, '(RFC822)')
37 | messages.append(message[0][1])
38 | return messages
39 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/vigenere_cipher1.py:
--------------------------------------------------------------------------------
1 | def combine_character(plain, keyword):
2 | plain = plain.upper()
3 | keyword = keyword.upper()
4 | plain_num = ord(plain) - ord('A')
5 | keyword_num = ord(keyword) - ord('A')
6 | return chr(ord('A') + (plain_num + keyword_num) % 26)
7 |
8 | def separate_character(cypher, keyword):
9 | cypher = cypher.upper()
10 | keyword = keyword.upper()
11 | cypher_num = ord(cypher) - ord('A')
12 | keyword_num = ord(keyword) - ord('A')
13 | return chr(ord('A') + (cypher_num - keyword_num) % 26)
14 |
15 | class VigenereCipher:
16 | def __init__(self, keyword):
17 | self.keyword = keyword
18 |
19 | def extend_keyword(self, number):
20 | repeats = number // len(self.keyword) + 1
21 | return (self.keyword * repeats)[:number]
22 |
23 | def encode(self, plaintext):
24 | plaintext = plaintext.replace(" ", "").upper()
25 | cipher = []
26 | keyword = self.extend_keyword(len(plaintext))
27 | for p,k in zip(plaintext, keyword):
28 | cipher.append(combine_character(p,k))
29 | return "".join(cipher)
30 |
31 | def decode(self, ciphertext):
32 | plain = []
33 | keyword = self.extend_keyword(len(ciphertext))
34 | for p,k in zip(ciphertext, keyword):
35 | plain.append(separate_character(p,k))
36 | return "".join(plain)
37 |
--------------------------------------------------------------------------------
/Chapter3/3_19_apartment_ugly_prompt.py:
--------------------------------------------------------------------------------
1 | class Apartment(Property):
2 | valid_laundries = ("coin", "ensuite", "none")
3 | valid_balconies = ("yes", "no", "solarium")
4 |
5 | def __init__(self, balcony='', laundry='', **kwargs):
6 | super().__init__(**kwargs)
7 | self.balcony = balcony
8 | self.laundry = laundry
9 |
10 | def display(self):
11 | super().display()
12 | print("APARTMENT DETAILS")
13 | print("laundry: {}".format(self.laundry))
14 | print("has balcony: {}".format(self.balcony))
15 |
16 | def prompt_init():
17 | parent_init = Property.prompt_init()
18 | laundry = ''
19 | while laundry.lower() not in \
20 | Apartment.valid_laundries:
21 | laundry = input("What laundry facilities does "
22 | "the property have? ({})".format(
23 | ", ".join(Apartment.valid_laundries))
24 | balcony = ''
25 | while balcony.lower() not in \
26 | Apartment.valid_balconies:
27 | balcony = input(
28 | "Does the property have a balcony? "
29 | "({})".format(
30 | ", ".join(Apartment.valid_balconies))
31 | parent_init.update({
32 | "laundry": laundry,
33 | "balcony": balcony
34 | })
35 | return parent_init
36 | prompt_init = staticmethod(prompt_init)
37 |
--------------------------------------------------------------------------------
/Chapter11/11_08_car_class.py:
--------------------------------------------------------------------------------
1 | import weakref
2 |
3 | class CarModel:
4 | _models = weakref.WeakValueDictionary()
5 |
6 | def __new__(cls, model_name, *args, **kwargs):
7 | model = cls._models.get(model_name)
8 | if not model:
9 | model = super().__new__(cls)
10 | cls._models[model_name] = model
11 |
12 | return model
13 |
14 | def __init__(self, model_name, air=False, tilt=False,
15 | cruise_control=False, power_locks=False,
16 | alloy_wheels=False, usb_charger=False):
17 | if not hasattr(self, "initted"):
18 | self.model_name = model_name
19 | self.air = air
20 | self.tilt = tilt
21 | self.cruise_control = cruise_control
22 | self.power_locks = power_locks
23 | self.alloy_wheels = alloy_wheels
24 | self.usb_charger = usb_charger
25 | self.initted=True
26 |
27 | def check_serial(self, serial_number):
28 | print("Sorry, we are unable to check "
29 | "the serial number {0} on the {1} "
30 | "at this time".format(
31 | serial_number, self.model_name))
32 |
33 | class Car:
34 | def __init__(self, model, color, serial):
35 | self.model = model
36 | self.color = color
37 | self.serial = serial
38 |
39 | def check_serial(self):
40 | return self.model.check_serial(self.serial)
41 |
--------------------------------------------------------------------------------
/Chapter9/9_25_knearest.py:
--------------------------------------------------------------------------------
1 | import csv
2 | from random import random
3 | import math
4 |
5 | dataset_filename = 'colors.csv'
6 |
7 |
8 | def load_colors(filename):
9 | with open(filename) as dataset_file:
10 | lines = csv.reader(dataset_file)
11 | for line in lines:
12 | yield tuple(float(y) for y in line[0:3]), line[3]
13 |
14 |
15 | def generate_colors(count=100):
16 | for i in range(count):
17 | yield (random(), random(), random())
18 |
19 |
20 | def color_distance(color1, color2):
21 | channels = zip(color1, color2)
22 | sum_distance_squared = 0
23 | for c1, c2 in channels:
24 | sum_distance_squared += (c1 - c2) ** 2
25 | return math.sqrt(sum_distance_squared)
26 |
27 |
28 | def nearest_neighbors(model_colors, num_neighbors):
29 | model = list(model_colors)
30 | target = yield
31 | while True:
32 | distances = sorted(
33 | ((color_distance(c[0], target), c) for c in model),
34 | )
35 | target = yield [
36 | d[1] for d in distances[0:num_neighbors]
37 | ]
38 |
39 |
40 | model_colors = load_colors(dataset_filename)
41 | target_colors = generate_colors(3)
42 | get_neighbors = nearest_neighbors(model_colors, 5)
43 | next(get_neighbors)
44 |
45 | for color in target_colors:
46 | distances = get_neighbors.send(color)
47 | print(color)
48 | for d in distances:
49 | print(color_distance(color, d[0]), d[1])
50 |
--------------------------------------------------------------------------------
/Chapter5/5_17_zipsearch.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import shutil
3 | import zipfile
4 | from pathlib import Path
5 |
6 | class ZipReplace:
7 | def __init__(self, filename, search_string, replace_string):
8 | self.filename = filename
9 | self.search_string = search_string
10 | self.replace_string = replace_string
11 | self.temp_directory = Path("unzipped-{}".format(
12 | filename))
13 |
14 | def zip_find_replace(self):
15 | self.unzip_files()
16 | self.find_replace()
17 | self.zip_files()
18 |
19 | def unzip_files(self):
20 | self.temp_directory.mkdir()
21 | with zipfile.ZipFile(self.filename) as zip:
22 | zip.extractall(str(self.temp_directory))
23 |
24 | def find_replace(self):
25 | for filename in self.temp_directory.iterdir():
26 | with filename.open() as file:
27 | contents = file.read()
28 | contents = contents.replace(
29 | self.search_string, self.replace_string)
30 | with filename.open("w") as file:
31 | file.write(contents)
32 |
33 | def zip_files(self):
34 | with zipfile.ZipFile(self.filename, 'w') as file:
35 | for filename in self.temp_directory.iterdir():
36 | file.write(str(filename), filename.name)
37 | shutil.rmtree(str(self.temp_directory))
38 |
39 | if __name__ == "__main__":
40 | ZipReplace(*sys.argv[1:4]).zip_find_replace()
41 |
--------------------------------------------------------------------------------
/Chapter3/3_23_purchase_and_rental.py:
--------------------------------------------------------------------------------
1 | class Purchase:
2 | def __init__(self, price='', taxes='', **kwargs):
3 | super().__init__(**kwargs)
4 | self.price = price
5 | self.taxes = taxes
6 |
7 | def display(self):
8 | super().display()
9 | print("PURCHASE DETAILS")
10 | print("selling price: {}".format(self.price))
11 | print("estimated taxes: {}".format(self.taxes))
12 |
13 | def prompt_init():
14 | return dict(
15 | price=input("What is the selling price? "),
16 | taxes=input("What are the estimated taxes? "))
17 | prompt_init = staticmethod(prompt_init)
18 |
19 | class Rental:
20 | def __init__(self, furnished='', utilities='',
21 | rent='', **kwargs):
22 | super().__init__(**kwargs)
23 | self.furnished = furnished
24 | self.rent = rent
25 | self.utilities = utilities
26 |
27 | def display(self):
28 | super().display()
29 | print("RENTAL DETAILS")
30 | print("rent: {}".format(self.rent))
31 | print("estimated utilities: {}".format(
32 | self.utilities))
33 | print("furnished: {}".format(self.furnished))
34 |
35 | def prompt_init():
36 | return dict(
37 | rent=input("What is the monthly rent? "),
38 | utilities=input(
39 | "What are the estimated utilities? "),
40 | furnished = get_valid_input(
41 | "Is the property furnished? ",
42 | ("yes", "no")))
43 | prompt_init = staticmethod(prompt_init)
44 |
--------------------------------------------------------------------------------
/Chapter10/10_21_template_concretes.py:
--------------------------------------------------------------------------------
1 | import sqlite3
2 |
3 | class QueryTemplate:
4 | def connect(self):
5 | self.conn = sqlite3.connect("sales.db")
6 |
7 | def construct_query(self):
8 | raise NotImplementedError()
9 |
10 | def do_query(self):
11 | results = self.conn.execute(self.query)
12 | self.results = results.fetchall()
13 |
14 | def format_results(self):
15 | output = []
16 | for row in self.results:
17 | row =[str(i) for i in row]
18 | output.append(", ".join(row))
19 | self.formatted_results = "\n".join(output)
20 |
21 | def output_results(self):
22 | raise NotImplementedError()
23 |
24 | def process_format(self):
25 | self.connect()
26 | self.construct_query()
27 | self.do_query()
28 | self.format_results()
29 | self.output_results()
30 |
31 | import datetime
32 | class NewVehiclesQuery(QueryTemplate):
33 | def construct_query(self):
34 | self.query = "select * from Sales where new='true'"
35 |
36 | def output_results(self):
37 | print(self.formatted_results)
38 |
39 | class UserGrossQuery(QueryTemplate):
40 | def construct_query(self):
41 | self.query = ("select salesperson, sum(amt) " +
42 | " from Sales group by salesperson")
43 |
44 | def output_results(self):
45 | filename = "gross_sales_{0}".format(
46 | datetime.date.today().strftime("%Y%m%d")
47 | )
48 | with open(filename, 'w') as outfile:
49 | outfile.write(self.formatted_results)
50 |
--------------------------------------------------------------------------------
/Chapter10/10_13_strategy_tile.py:
--------------------------------------------------------------------------------
1 | from PIL import Image
2 |
3 |
4 | class TiledStrategy:
5 | def make_background(self, img_file, desktop_size):
6 | in_img = Image.open(img_file)
7 | out_img = Image.new('RGB', desktop_size)
8 | num_tiles = [
9 | o // i + 1 for o, i in
10 | zip(out_img.size, in_img.size)
11 | ]
12 | for x in range(num_tiles[0]):
13 | for y in range(num_tiles[1]):
14 | out_img.paste(
15 | in_img,
16 | (
17 | in_img.size[0] * x,
18 | in_img.size[1] * y,
19 | in_img.size[0] * (x+1),
20 | in_img.size[1] * (y+1)
21 | )
22 | )
23 | return out_img
24 |
25 |
26 | class CenteredStrategy:
27 | def make_background(self, img_file, desktop_size):
28 | in_img = Image.open(img_file)
29 | out_img = Image.new('RGB', desktop_size)
30 | left = (out_img.size[0] - in_img.size[0]) // 2
31 | top = (out_img.size[1] - in_img.size[1]) // 2
32 | out_img.paste(
33 | in_img,
34 | (
35 | left,
36 | top,
37 | left+in_img.size[0],
38 | top + in_img.size[1]
39 | )
40 | )
41 | return out_img
42 |
43 |
44 | class ScaledStrategy:
45 | def make_background(self, img_file, desktop_size):
46 | in_img = Image.open(img_file)
47 | out_img = in_img.resize(desktop_size)
48 | return out_img
49 |
--------------------------------------------------------------------------------
/Chapter12/casestudy/test_vigenere_cipher.py:
--------------------------------------------------------------------------------
1 | from vigenere_cipher import VigenereCipher
2 | from vigenere_cipher import combine_character
3 | from vigenere_cipher import separate_character
4 |
5 | def test_encode():
6 | cipher = VigenereCipher("TRAIN")
7 | encoded = cipher.encode("ENCODEDINPYTHON")
8 | assert encoded == "XECWQXUIVCRKHWA"
9 |
10 | def test_encode_character():
11 | cipher = VigenereCipher("TRAIN")
12 | encoded = cipher.encode("E")
13 | assert encoded == "X"
14 |
15 | def test_encode_spaces():
16 | cipher = VigenereCipher("TRAIN")
17 | encoded = cipher.encode("ENCODED IN PYTHON")
18 | assert encoded == "XECWQXUIVCRKHWA"
19 |
20 | def test_encode_lowercase():
21 | cipher = VigenereCipher("TRain")
22 | encoded = cipher.encode("encoded in Python")
23 | assert encoded == "XECWQXUIVCRKHWA"
24 |
25 | def test_combine_character():
26 | assert combine_character("E", "T") == "X"
27 | assert combine_character("N", "R") == "E"
28 |
29 | def test_extend_keyword():
30 | cipher = VigenereCipher("TRAIN")
31 | extended = cipher.extend_keyword(16)
32 | assert extended == "TRAINTRAINTRAINT"
33 |
34 | def test_extend_keyword_even():
35 | cipher = VigenereCipher("TRAIN")
36 | extended = cipher.extend_keyword(15)
37 | assert extended == "TRAINTRAINTRAIN"
38 |
39 | def test_separate_character():
40 | assert separate_character("X", "T") == "E"
41 | assert separate_character("E", "R") == "N"
42 |
43 | def test_decode():
44 | cipher = VigenereCipher("TRAIN")
45 | decoded = cipher.decode("XECWQXUIVCRKHWA")
46 | assert decoded == "ENCODEDINPYTHON"
47 |
--------------------------------------------------------------------------------
/Chapter6/6_22_dict_link_collector.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | LINK_REGEX = re.compile(
6 | "]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 | class LinkCollector:
9 | def __init__(self, url):
10 | self.url = "http://%s" % urlparse(url).netloc
11 | self.collected_links = {}
12 | self.visited_links = set()
13 |
14 | def collect_links(self, path="/"):
15 | full_url = self.url + path
16 | self.visited_links.add(full_url)
17 | page = str(urlopen(full_url).read())
18 | links = LINK_REGEX.findall(page)
19 | links = {self.normalize_url(path, link
20 | ) for link in links}
21 | self.collected_links[full_url] = links
22 | for link in links:
23 | self.collected_links.setdefault(link, set())
24 | unvisited_links = links.difference(
25 | self.visited_links)
26 | for link in unvisited_links:
27 | if link.startswith(self.url):
28 | self.collect_links(urlparse(link).path)
29 |
30 | def normalize_url(self, path, link):
31 | if link.startswith("http://"):
32 | return link
33 | elif link.startswith("/"):
34 | return self.url + link
35 | else:
36 | return self.url + path.rpartition('/'
37 | )[0] + '/' + link
38 |
39 | if __name__ == "__main__":
40 | collector = LinkCollector(sys.argv[1])
41 | collector.collect_links()
42 | for link, item in collector.collected_links.items():
43 | print("{}: {}".format(link, item))
44 |
--------------------------------------------------------------------------------
/Chapter4/4_17_authenticator.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | class User:
4 | def __init__(self, username, password):
5 | '''Create a new user object. The password
6 | will be encrypted before storing.'''
7 | self.username = username
8 | self.password = self._encrypt_pw(password)
9 | self.is_logged_in = False
10 |
11 | def _encrypt_pw(self, password):
12 | '''Encrypt the password with the username and return
13 | the sha digest.'''
14 | hash_string = (self.username + password)
15 | hash_string = hash_string.encode("utf8")
16 | return hashlib.sha256(hash_string).hexdigest()
17 |
18 | def check_password(self, password):
19 | '''Return True if the password is valid for this
20 | user, false otherwise.'''
21 | encrypted = self._encrypt_pw(password)
22 | return encrypted == self.password
23 |
24 |
25 | class AuthException(Exception):
26 | def __init__(self, username, user=None):
27 | super().__init__(username)
28 | self.username = username
29 | self.user = user
30 |
31 | class UsernameAlreadyExists(AuthException):
32 | pass
33 |
34 | class PasswordTooShort(AuthException):
35 | pass
36 |
37 | class Authenticator:
38 | def __init__(self):
39 | '''Construct an authenticator to manage
40 | users logging in and out.'''
41 | self.users = {}
42 |
43 | def add_user(self, username, password):
44 | if username in self.users:
45 | raise UsernameAlreadyExists(username)
46 | if len(password) < 6:
47 | raise PasswordTooShort(username)
48 | self.users[username] = User(username, password)
49 |
--------------------------------------------------------------------------------
/Chapter13/13_08_asyncio_dns.py:
--------------------------------------------------------------------------------
1 | import asyncio
2 | from contextlib import suppress
3 |
4 | ip_map = {
5 | b'facebook.com.': '173.252.120.6',
6 | b'yougov.com.': '213.52.133.246',
7 | b'wipo.int.': '193.5.93.80'
8 | }
9 |
10 | def lookup_dns(data):
11 | domain = b''
12 | pointer, part_length = 13, data[12]
13 | while part_length:
14 | domain += data[pointer:pointer+part_length] + b'.'
15 | pointer += part_length + 1
16 | part_length = data[pointer - 1]
17 |
18 | ip = ip_map.get(domain, '127.0.0.1')
19 |
20 | return domain, ip
21 |
22 | def create_response(data, ip):
23 | ba = bytearray
24 | packet = ba(data[:2]) + ba([129, 128]) + data[4:6] * 2
25 | packet += ba(4) + data[12:]
26 | packet += ba([192, 12, 0, 1, 0, 1, 0, 0, 0, 60, 0, 4])
27 | for x in ip.split('.'): packet.append(int(x))
28 | return packet
29 |
30 | class DNSProtocol(asyncio.DatagramProtocol):
31 | def connection_made(self, transport):
32 | self.transport = transport
33 |
34 | def datagram_received(self, data, addr):
35 | print("Received request from {}".format(addr[0]))
36 | domain, ip = lookup_dns(data)
37 | print("Sending IP {} for {} to {}".format(
38 | domain.decode(), ip, addr[0]))
39 | self.transport.sendto(create_response(data, ip), addr)
40 |
41 |
42 | loop = asyncio.get_event_loop()
43 | transport, protocol = loop.run_until_complete(
44 | loop.create_datagram_endpoint(
45 | DNSProtocol, local_addr=('127.0.0.1', 4343)))
46 | print("DNS Server running")
47 |
48 | with suppress(KeyboardInterrupt):
49 | loop.run_forever()
50 | transport.close()
51 | loop.close()
52 |
53 | # nslookup -port=4343 buchuki.com localhost
54 |
--------------------------------------------------------------------------------
/Chapter11/11_11_window_command_commands.py:
--------------------------------------------------------------------------------
1 | import sys
2 |
3 | class Window:
4 | def exit(self):
5 | sys.exit(0)
6 |
7 | class Document:
8 | def __init__(self, filename):
9 | self.filename = filename
10 | self.contents = "This file cannot be modified"
11 |
12 | def save(self):
13 | with open(self.filename, 'w') as file:
14 | file.write(self.contents)
15 |
16 | class ToolbarButton:
17 | def __init__(self, name, iconname):
18 | self.name = name
19 | self.iconname = iconname
20 |
21 | def click(self):
22 | self.command.execute()
23 |
24 | class MenuItem:
25 | def __init__(self, menu_name, menuitem_name):
26 | self.menu = menu_name
27 | self.item = menuitem_name
28 |
29 | def click(self):
30 | self.command.execute()
31 |
32 | class KeyboardShortcut:
33 | def __init__(self, key, modifier):
34 | self.key = key
35 | self.modifier = modifier
36 |
37 | def keypress(self):
38 | self.command.execute()
39 |
40 | class SaveCommand:
41 | def __init__(self, document):
42 | self.document = document
43 |
44 | def execute(self):
45 | self.document.save()
46 |
47 | class ExitCommand:
48 | def __init__(self, window):
49 | self.window = window
50 |
51 | def execute(self):
52 | self.window.exit()
53 |
54 | window = Window()
55 | document = Document("a_document.txt")
56 | save = SaveCommand(document)
57 | exit = ExitCommand(window)
58 |
59 | save_button = ToolbarButton('save', 'save.png')
60 | save_button.command = save
61 | save_keystroke = KeyboardShortcut("s", "ctrl")
62 | save_keystroke.command = save
63 | exit_menu = MenuItem("File", "Exit")
64 | exit_menu.command = exit
65 |
66 |
--------------------------------------------------------------------------------
/Chapter11/11_14_formatters.py:
--------------------------------------------------------------------------------
1 | class FranceDateFormatter:
2 | def format_date(self, y, m, d):
3 | y, m, d = (str(x) for x in (y,m,d))
4 | y = '20' + y if len(y) == 2 else y
5 | m = '0' + m if len(m) == 1 else m
6 | d = '0' + d if len(d) == 1 else d
7 | return("{0}/{1}/{2}".format(d,m,y))
8 |
9 | class USADateFormatter:
10 | def format_date(self, y, m, d):
11 | y, m, d = (str(x) for x in (y,m,d))
12 | y = '20' + y if len(y) == 2 else y
13 | m = '0' + m if len(m) == 1 else m
14 | d = '0' + d if len(d) == 1 else d
15 | return("{0}-{1}-{2}".format(m,d,y))
16 |
17 | class FranceCurrencyFormatter:
18 | def format_currency(self, base, cents):
19 | base, cents = (str(x) for x in (base, cents))
20 | if len(cents) == 0:
21 | cents = '00'
22 | elif len(cents) == 1:
23 | cents = '0' + cents
24 |
25 | digits = []
26 | for i,c in enumerate(reversed(base)):
27 | if i and not i % 3:
28 | digits.append(' ')
29 | digits.append(c)
30 | base = ''.join(reversed(digits))
31 | return "{0}€{1}".format(base, cents)
32 |
33 | class USACurrencyFormatter:
34 | def format_currency(self, base, cents):
35 | base, cents = (str(x) for x in (base, cents))
36 | if len(cents) == 0:
37 | cents = '00'
38 | elif len(cents) == 1:
39 | cents = '0' + cents
40 |
41 | digits = []
42 | for i,c in enumerate(reversed(base)):
43 | if i and not i % 3:
44 | digits.append(',')
45 | digits.append(c)
46 | base = ''.join(reversed(digits))
47 | return "${0}.{1}".format(base, cents)
48 |
49 |
--------------------------------------------------------------------------------
/Chapter12/12_16_mock_flightstatus.py:
--------------------------------------------------------------------------------
1 | import datetime
2 | import redis
3 |
4 | class FlightStatusTracker:
5 | ALLOWED_STATUSES = {'CANCELLED', 'DELAYED', 'ON TIME'}
6 |
7 | def __init__(self):
8 | self.redis = redis.StrictRedis()
9 |
10 | def change_status(self, flight, status):
11 | status = status.upper()
12 | if status not in self.ALLOWED_STATUSES:
13 | raise ValueError(
14 | "{} is not a valid status".format(status)
15 | )
16 |
17 | key = "flightno:{}".format(flight)
18 | value = "{}|{}".format(datetime.datetime.now().isoformat(), status)
19 | self.redis.set(key, value)
20 |
21 | def last_status(self, flight, status):
22 | key = "flightno:{}".format(flight)
23 | timestamp, status = self.redis.get(key).split('|')
24 | return timestamp, status
25 |
26 |
27 |
28 | from unittest.mock import Mock
29 | import py.test
30 | def pytest_funcarg__tracker():
31 | return FlightStatusTracker()
32 |
33 | def test_mock_method(tracker):
34 | tracker.redis.set = Mock()
35 | with py.test.raises(ValueError) as ex:
36 | tracker.change_status("AC101", "lost")
37 | assert ex.value.args[0] == "LOST is not a valid status"
38 | assert tracker.redis.set.call_count == 0
39 |
40 | from unittest.mock import patch
41 | def test_patch(tracker):
42 | tracker.redis.set = Mock()
43 | fake_now = datetime.datetime(2015, 4, 1)
44 | with patch('datetime.datetime') as dt:
45 | dt.now.return_value = fake_now
46 | tracker.change_status("AC102", "on time")
47 | dt.now.assert_called_once_with()
48 | tracker.redis.set.assert_called_once_with(
49 | "flightno:AC102",
50 | "2015-04-01T00:00:00|ON TIME"
51 | )
52 |
53 |
--------------------------------------------------------------------------------
/Chapter6/link_collector.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | from queue import Queue
6 | LINK_REGEX = re.compile("]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 |
9 | class LinkCollector:
10 | def __init__(self, url):
11 | self.url = "http://%s" % urlparse(url).netloc
12 | self.collected_links = {}
13 | self.visited_links = set()
14 |
15 | def collect_links(self):
16 | queue = Queue()
17 | queue.put(self.url)
18 | while not queue.empty():
19 | url = queue.get().rstrip('/')
20 | self.visited_links.add(url)
21 | page = str(urlopen(url).read())
22 | links = LINK_REGEX.findall(page)
23 | links = {
24 | self.normalize_url(urlparse(url).path, link)
25 | for link in links
26 | }
27 | self.collected_links[url] = links
28 | for link in links:
29 | self.collected_links.setdefault(link, set())
30 | unvisited_links = links.difference(self.visited_links)
31 | for link in unvisited_links:
32 | if link.startswith(self.url):
33 | queue.put(link)
34 |
35 | def normalize_url(self, path, link):
36 | if link.startswith("http://"):
37 | return link.rstrip('/')
38 | elif link.startswith("/"):
39 | return self.url + link.rstrip('/')
40 | else:
41 | return self.url + path.rpartition('/')[0] + '/' + link.rstrip('/')
42 |
43 | if __name__ == "__main__":
44 | collector = LinkCollector(sys.argv[1])
45 | collector.collect_links()
46 | for link, item in collector.collected_links.items():
47 | print("%s: %s" % (link, item))
48 |
--------------------------------------------------------------------------------
/Chapter6/6_23_queue_link_collector.py:
--------------------------------------------------------------------------------
1 | from urllib.request import urlopen
2 | from urllib.parse import urlparse
3 | import re
4 | import sys
5 | from queue import Queue
6 | LINK_REGEX = re.compile("]*href=['\"]([^'\"]+)['\"][^>]*>")
7 |
8 |
9 | class LinkCollector:
10 | def __init__(self, url):
11 | self.url = "http://%s" % urlparse(url).netloc
12 | self.collected_links = {}
13 | self.visited_links = set()
14 |
15 | def collect_links(self):
16 | queue = Queue()
17 | queue.put(self.url)
18 | while not queue.empty():
19 | url = queue.get().rstrip('/')
20 | self.visited_links.add(url)
21 | page = str(urlopen(url).read())
22 | links = LINK_REGEX.findall(page)
23 | links = {
24 | self.normalize_url(urlparse(url).path, link)
25 | for link in links
26 | }
27 | self.collected_links[url] = links
28 | for link in links:
29 | self.collected_links.setdefault(link, set())
30 | unvisited_links = links.difference(self.visited_links)
31 | for link in unvisited_links:
32 | if link.startswith(self.url):
33 | queue.put(link)
34 |
35 | def normalize_url(self, path, link):
36 | if link.startswith("http://"):
37 | return link.rstrip('/')
38 | elif link.startswith("/"):
39 | return self.url + link.rstrip('/')
40 | else:
41 | return self.url + path.rpartition('/')[0] + '/' + link.rstrip('/')
42 |
43 | if __name__ == "__main__":
44 | collector = LinkCollector(sys.argv[1])
45 | collector.collect_links()
46 | for link, item in collector.collected_links.items():
47 | print("%s: %s" % (link, item))
48 |
--------------------------------------------------------------------------------
/Chapter5/Document.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self):
3 | self.characters = []
4 | self.cursor = Cursor(self)
5 | self.filename = ''
6 |
7 | def insert(self, character):
8 | if not hasattr(character, 'character'):
9 | character = Character(character)
10 | self.characters.insert(self.cursor.position,
11 | character)
12 | self.cursor.forward()
13 |
14 | def delete(self):
15 | del self.characters[self.cursor.position]
16 |
17 | def save(self):
18 | f = open(self.filename, 'w')
19 | f.write(''.join(self.characters))
20 | f.close()
21 |
22 | @property
23 | def string(self):
24 | return "".join((str(c) for c in self.characters))
25 |
26 |
27 | class Cursor:
28 | def __init__(self, document):
29 | self.document = document
30 | self.position = 0
31 |
32 | def forward(self):
33 | self.position += 1
34 |
35 | def back(self):
36 | self.position -= 1
37 |
38 | def home(self):
39 | while self.document.characters[
40 | self.position-1].character != '\n':
41 | self.position -= 1
42 | if self.position == 0:
43 | # Got to beginning of file before newline
44 | break
45 |
46 | def end(self):
47 | while self.position < len(self.document.characters) and \
48 | self.document.characters[
49 | self.position].character != '\n':
50 | self.position += 1
51 |
52 | class Character:
53 | def __init__(self, character,
54 | bold=False, italic=False, underline=False):
55 | assert len(character) == 1
56 | self.character = character
57 | self.bold = bold
58 | self.italic = italic
59 | self.underline = underline
60 |
61 | def __str__(self):
62 | bold = "*" if self.bold else ''
63 | italic = "/" if self.italic else ''
64 | underline = "_" if self.underline else ''
65 | return bold + italic + underline + self.character
66 |
--------------------------------------------------------------------------------
/Chapter5/5_29_document_with_character.py:
--------------------------------------------------------------------------------
1 | class Document:
2 | def __init__(self):
3 | self.characters = []
4 | self.cursor = Cursor(self)
5 | self.filename = ''
6 |
7 | def insert(self, character):
8 | if not hasattr(character, 'character'):
9 | character = Character(character)
10 | self.characters.insert(self.cursor.position,
11 | character)
12 | self.cursor.forward()
13 |
14 | def delete(self):
15 | del self.characters[self.cursor.position]
16 |
17 | def save(self):
18 | f = open(self.filename, 'w')
19 | f.write(''.join(self.characters))
20 | f.close()
21 |
22 | @property
23 | def string(self):
24 | return "".join((str(c) for c in self.characters))
25 |
26 |
27 | class Cursor:
28 | def __init__(self, document):
29 | self.document = document
30 | self.position = 0
31 |
32 | def forward(self):
33 | self.position += 1
34 |
35 | def back(self):
36 | self.position -= 1
37 |
38 | def home(self):
39 | while self.document.characters[
40 | self.position-1].character != '\n':
41 | self.position -= 1
42 | if self.position == 0:
43 | # Got to beginning of file before newline
44 | break
45 |
46 | def end(self):
47 | while self.position < len(self.document.characters) and \
48 | self.document.characters[
49 | self.position].character != '\n':
50 | self.position += 1
51 |
52 | class Character:
53 | def __init__(self, character,
54 | bold=False, italic=False, underline=False):
55 | assert len(character) == 1
56 | self.character = character
57 | self.bold = bold
58 | self.italic = italic
59 | self.underline = underline
60 |
61 | def __str__(self):
62 | bold = "*" if self.bold else ''
63 | italic = "/" if self.italic else ''
64 | underline = "_" if self.underline else ''
65 | return bold + italic + underline + self.character
66 |
--------------------------------------------------------------------------------
/Chapter9/case_study_machine_learn.py:
--------------------------------------------------------------------------------
1 | import csv
2 | from random import random
3 | import math
4 | from collections import Counter
5 |
6 | dataset_filename = 'colors.csv'
7 |
8 |
9 | def load_colors(filename):
10 | with open(filename) as dataset_file:
11 | lines = csv.reader(dataset_file)
12 | for line in lines:
13 | yield tuple(float(y) for y in line[0:3]), line[3]
14 |
15 |
16 | def generate_colors(count=100):
17 | for i in range(count):
18 | yield (random(), random(), random())
19 |
20 |
21 | def color_distance(color1, color2):
22 | channels = zip(color1, color2)
23 | sum_distance_squared = 0
24 | for c1, c2 in channels:
25 | sum_distance_squared += (c1 - c2) ** 2
26 | return math.sqrt(sum_distance_squared)
27 |
28 |
29 | def nearest_neighbors(model_colors, num_neighbors):
30 | model = list(model_colors)
31 | target = yield
32 | while True:
33 | distances = sorted(
34 | ((color_distance(c[0], target), c) for c in model),
35 | )
36 | target = yield [
37 | d[1] for d in distances[0:num_neighbors]
38 | ]
39 |
40 |
41 | def name_colors(get_neighbors):
42 | color = yield
43 | while True:
44 | near = get_neighbors.send(color)
45 | name_guess = Counter(n[1] for n in near).most_common(1)[0][0]
46 | color = yield name_guess
47 |
48 |
49 | def write_results(filename="output.csv"):
50 | with open(filename, "w") as file:
51 | writer = csv.writer(file)
52 | while True:
53 | color, name = yield
54 | writer.writerow(list(color) + [name])
55 |
56 |
57 | def process_colors(dataset_filename="colors.csv"):
58 | model_colors = load_colors(dataset_filename)
59 | get_neighbors = nearest_neighbors(model_colors, 5)
60 | get_color_name = name_colors(get_neighbors)
61 | output = write_results()
62 | next(output)
63 | next(get_neighbors)
64 | next(get_color_name)
65 |
66 | for color in generate_colors():
67 | name = get_color_name.send(color)
68 | output.send((color, name))
69 |
70 | process_colors()
71 |
--------------------------------------------------------------------------------
/Chapter11/11_15_formatter_factories.py:
--------------------------------------------------------------------------------
1 | class FranceDateFormatter:
2 | def format_date(self, y, m, d):
3 | y, m, d = (str(x) for x in (y,m,d))
4 | y = '20' + y if len(y) == 2 else y
5 | m = '0' + m if len(m) == 1 else m
6 | d = '0' + d if len(d) == 1 else d
7 | return("{0}/{1}/{2}".format(d,m,y))
8 |
9 | class USADateFormatter:
10 | def format_date(self, y, m, d):
11 | y, m, d = (str(x) for x in (y,m,d))
12 | y = '20' + y if len(y) == 2 else y
13 | m = '0' + m if len(m) == 1 else m
14 | d = '0' + d if len(d) == 1 else d
15 | return("{0}-{1}-{2}".format(m,d,y))
16 |
17 | class FranceCurrencyFormatter:
18 | def format_currency(self, base, cents):
19 | base, cents = (str(x) for x in (base, cents))
20 | if len(cents) == 0:
21 | cents = '00'
22 | elif len(cents) == 1:
23 | cents = '0' + cents
24 |
25 | digits = []
26 | for i,c in enumerate(reversed(base)):
27 | if i and not i % 3:
28 | digits.append(' ')
29 | digits.append(c)
30 | base = ''.join(reversed(digits))
31 | return "{0}€{1}".format(base, cents)
32 |
33 | class USACurrencyFormatter:
34 | def format_currency(self, base, cents):
35 | base, cents = (str(x) for x in (base, cents))
36 | if len(cents) == 0:
37 | cents = '00'
38 | elif len(cents) == 1:
39 | cents = '0' + cents
40 |
41 | digits = []
42 | for i,c in enumerate(reversed(base)):
43 | if i and not i % 3:
44 | digits.append(',')
45 | digits.append(c)
46 | base = ''.join(reversed(digits))
47 | return "${0}.{1}".format(base, cents)
48 |
49 | class USAFormatterFactory:
50 | def create_date_formatter(self):
51 | return USADateFormatter()
52 | def create_currency_formatter(self):
53 | return USACurrencyFormatter()
54 |
55 | class FranceFormatterFactory:
56 | def create_date_formatter(self):
57 | return FranceDateFormatter()
58 | def create_currency_formatter(self):
59 | return FranceCurrencyFormatter()
60 |
61 | country_code = "US"
62 | factory_map = {
63 | "US": USAFormatterFactory,
64 | "FR": FranceFormatterFactory}
65 | formatter_factory = factory_map.get(country_code)()
66 |
--------------------------------------------------------------------------------
/Chapter4/4_18_login.py:
--------------------------------------------------------------------------------
1 | import hashlib
2 |
3 | class User:
4 | def __init__(self, username, password):
5 | '''Create a new user object. The password
6 | will be encrypted before storing.'''
7 | self.username = username
8 | self.password = self._encrypt_pw(password)
9 | self.is_logged_in = False
10 |
11 | def _encrypt_pw(self, password):
12 | '''Encrypt the password with the username and return
13 | the sha digest.'''
14 | hash_string = (self.username + password)
15 | hash_string = hash_string.encode("utf8")
16 | return hashlib.sha256(hash_string).hexdigest()
17 |
18 | def check_password(self, password):
19 | '''Return True if the password is valid for this
20 | user, false otherwise.'''
21 | encrypted = self._encrypt_pw(password)
22 | return encrypted == self.password
23 |
24 |
25 | class AuthException(Exception):
26 | def __init__(self, username, user=None):
27 | super().__init__(username)
28 | self.username = username
29 | self.user = user
30 |
31 | class UsernameAlreadyExists(AuthException):
32 | pass
33 |
34 | class PasswordTooShort(AuthException):
35 | pass
36 |
37 | class InvalidUsername(AuthException):
38 | pass
39 |
40 | class InvalidPassword(AuthException):
41 | pass
42 |
43 | class Authenticator:
44 | def __init__(self):
45 | '''Construct an authenticator to manage
46 | users logging in and out.'''
47 | self.users = {}
48 |
49 | def add_user(self, username, password):
50 | if username in self.users:
51 | raise UsernameAlreadyExists(username)
52 | if len(password) < 6:
53 | raise PasswordTooShort(username)
54 | self.users[username] = User(username, password)
55 |
56 | def login(self, username, password):
57 | try:
58 | user = self.users[username]
59 | except KeyError:
60 | raise InvalidUsername(username)
61 |
62 | if not user.check_password(password):
63 | raise InvalidPassword(username, user)
64 |
65 | user.is_logged_in = True
66 | return True
67 |
68 | def is_logged_in(self, username):
69 | if username in self.users:
70 | return self.users[username].is_logged_in
71 | return False
72 |
73 | authenticator = Authenticator()
74 |
--------------------------------------------------------------------------------
/Chapter7/mailing_list.py:
--------------------------------------------------------------------------------
1 | import smtplib
2 | from email.mime.text import MIMEText
3 | from collections import defaultdict
4 |
5 |
6 | def send_email(subject, message, from_addr, *to_addrs,
7 | host="localhost", port=1025, headers=None):
8 |
9 | headers = {} if headers is None else headers
10 |
11 | email = MIMEText(message)
12 | email['Subject'] = subject
13 | email['From'] = from_addr
14 | for header, value in headers.items():
15 | email[header] = value
16 |
17 | sender = smtplib.SMTP(host, port)
18 | for addr in to_addrs:
19 | del email['To']
20 | email['To'] = addr
21 | sender.sendmail(from_addr, addr, email.as_string())
22 | sender.quit()
23 |
24 |
25 | class MailingList:
26 | '''Manage groups of e-mail addresses for sending e-mails.'''
27 |
28 | def __init__(self, data_file):
29 | self.data_file = data_file
30 | self.email_map = defaultdict(set)
31 |
32 | def add_to_group(self, email, group):
33 | self.email_map[email].add(group)
34 |
35 | def emails_in_groups(self, *groups):
36 | groups = set(groups)
37 | emails = set()
38 | for e, g in self.email_map.items():
39 | if g & groups:
40 | emails.add(e)
41 | return emails
42 |
43 | def send_mailing(self, subject, message, from_addr,
44 | *groups, headers=None):
45 | emails = self.emails_in_groups(*groups)
46 | send_email(subject, message, from_addr,
47 | *emails, headers=headers)
48 |
49 | def save(self):
50 | with open(self.data_file, 'w') as file:
51 | for email, groups in self.email_map.items():
52 | file.write(
53 | '{} {}\n'.format(email, ','.join(groups))
54 | )
55 |
56 | def load(self):
57 | self.email_map = defaultdict(set)
58 | try:
59 | with open(self.data_file) as file:
60 | for line in file:
61 | email, groups = line.strip().split(' ')
62 | groups = set(groups.split(','))
63 | self.email_map[email] = groups
64 | except IOError:
65 | pass
66 |
67 | def __enter__(self):
68 | self.load()
69 | return self
70 |
71 | def __exit__(self, type, value, tb):
72 | self.save()
73 |
74 |
--------------------------------------------------------------------------------
/Chapter8/8_29_template_processer_complete.py:
--------------------------------------------------------------------------------
1 | import re
2 | import sys
3 | import json
4 | from pathlib import Path
5 |
6 | DIRECTIVE_RE = re.compile(
7 | r'/\*\*\s*(include|variable|loopover|endloop|loopvar)'
8 | r'\s*([^ *]*)\s*\*\*/')
9 |
10 |
11 | class TemplateEngine:
12 | def __init__(self, infilename, outfilename, contextfilename):
13 | self.template = open(infilename).read()
14 | self.working_dir = Path(infilename).absolute().parent
15 | self.pos = 0
16 | self.outfile = open(outfilename, 'w')
17 | with open(contextfilename) as contextfile:
18 | self.context = json.load(contextfile)
19 |
20 | def process(self):
21 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
22 | while match:
23 | self.outfile.write(self.template[self.pos:match.start()])
24 | directive, argument = match.groups()
25 | method_name = 'process_{}'.format(directive)
26 | getattr(self, method_name)(match, argument)
27 | match = DIRECTIVE_RE.search(self.template, pos=self.pos)
28 | self.outfile.write(self.template[self.pos:])
29 |
30 | def process_include(self, match, argument):
31 | with (self.working_dir / argument).open() as includefile:
32 | self.outfile.write(includefile.read())
33 | self.pos = match.end()
34 |
35 | def process_variable(self, match, argument):
36 | self.outfile.write(self.context.get(argument, ''))
37 | self.pos = match.end()
38 |
39 | def process_loopover(self, match, argument):
40 | self.loop_index = 0
41 | self.loop_list = self.context.get(argument, [])
42 | self.pos = self.loop_pos = match.end()
43 |
44 | def process_loopvar(self, match, argument):
45 | self.outfile.write(self.loop_list[self.loop_index])
46 | self.pos = match.end()
47 |
48 | def process_endloop(self, match, argument):
49 | self.loop_index += 1
50 | if self.loop_index >= len(self.loop_list):
51 | self.pos = match.end()
52 | del self.loop_index
53 | del self.loop_list
54 | del self.loop_pos
55 | else:
56 | self.pos = self.loop_pos
57 |
58 | if __name__ == '__main__':
59 | infilename, outfilename, contextfilename = sys.argv[1:]
60 | engine = TemplateEngine(infilename, outfilename, contextfilename)
61 | engine.process()
62 |
--------------------------------------------------------------------------------
/Chapter13/case_study/compress_bmp.py:
--------------------------------------------------------------------------------
1 | import sys
2 | from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
3 |
4 | from PIL import Image
5 | from bitarray import bitarray
6 | from pathlib import Path
7 |
8 | def compress_row(row):
9 | compressed = bytearray()
10 | chunks = split_bits(row, 127)
11 | for chunk in chunks:
12 | compressed.extend(compress_chunk(chunk))
13 | return compressed
14 |
15 | def compress_chunk(chunk):
16 | compressed = bytearray()
17 | count = 1
18 | last = chunk[0]
19 | for bit in chunk[1:]:
20 | if bit != last:
21 | compressed.append(count | (128 * last))
22 | count = 0
23 | last = bit
24 | count += 1
25 | compressed.append(count | (128 * last))
26 | return compressed
27 |
28 |
29 | def split_bits(bits, width):
30 | for i in range(0, len(bits), width):
31 | yield bits[i:i+width]
32 |
33 | def compress_in_executor(executor, bits, width):
34 | row_compressors = []
35 | for row in split_bits(bits, width):
36 | compressor = executor.submit(compress_row, row)
37 | row_compressors.append(compressor)
38 |
39 | compressed = bytearray()
40 | for compressor in row_compressors:
41 | compressed.extend(compressor.result())
42 | return compressed
43 |
44 | def compress_image(in_filename, out_filename, executor=None):
45 | executor = executor if executor else ThreadPoolExecutor(4)
46 | with Image.open(in_filename) as image:
47 | bits = bitarray(image.convert('1').getdata())
48 | width, height = image.size
49 |
50 | compressed = compress_in_executor(executor, bits, width)
51 |
52 | with open(out_filename, 'wb') as file:
53 | file.write(width.to_bytes(2, 'little'))
54 | file.write(height.to_bytes(2, 'little'))
55 | file.write(compressed)
56 |
57 | def compress_dir(in_dir, out_dir):
58 | if not out_dir.exists():
59 | out_dir.mkdir()
60 |
61 | executor = ThreadPoolExecutor(4)
62 | for file in (f for f in in_dir.iterdir() if f.suffix == '.bmp'):
63 | out_file = (out_dir / file.name).with_suffix('.rle')
64 | executor.submit(compress_image, str(file), str(out_file))
65 |
66 | def single_image_main():
67 | in_filename, out_filename = sys.argv[1:3]
68 | executor = ThreadPoolExecutor(4)
69 | #executor = ProcessPoolExecutor()
70 | compress_image(in_filename, out_filename, executor)
71 |
72 | def dir_images_main():
73 | in_dir, out_dir = (Path(p) for p in sys.argv[1:3])
74 | compress_dir(in_dir, out_dir)
75 |
76 | if __name__ == '__main__':
77 | dir_images_main()
78 | #single_image_main()
79 |
--------------------------------------------------------------------------------
/Chapter4/4_20_test_auth.py:
--------------------------------------------------------------------------------
1 | import auth
2 |
3 | # Set up a test user and permission
4 | auth.authenticator.add_user("joe", "joepassword")
5 | auth.authorizor.add_permission("test program")
6 | auth.authorizor.add_permission("change program")
7 | auth.authorizor.permit_user("test program", "joe")
8 |
9 | class Editor:
10 | def __init__(self):
11 | self.username = None
12 | self.menu_map = {
13 | "login": self.login,
14 | "test": self.test,
15 | "change": self.change,
16 | "quit": self.quit
17 | }
18 |
19 | def login(self):
20 | logged_in = False
21 | while not logged_in:
22 | username = input("username: ")
23 | password = input("password: ")
24 | try:
25 | logged_in = auth.authenticator.login(
26 | username, password)
27 | except auth.InvalidUsername:
28 | print("Sorry, that username does not exist")
29 | except auth.InvalidPassword:
30 | print("Sorry, incorrect password")
31 | else:
32 | self.username = username
33 |
34 | def is_permitted(self, permission):
35 | try:
36 | auth.authorizor.check_permission(
37 | permission, self.username)
38 | except auth.NotLoggedInError as e:
39 | print("{} is not logged in".format(e.username))
40 | return False
41 | except auth.NotPermittedError as e:
42 | print("{} cannot {}".format(
43 | e.username, permission))
44 | return False
45 | else:
46 | return True
47 |
48 | def test(self):
49 | if self.is_permitted("test program"):
50 | print("Testing program now...")
51 |
52 | def change(self):
53 | if self.is_permitted("change program"):
54 | print("Changing program now...")
55 |
56 | def quit(self):
57 | raise SystemExit()
58 |
59 | def menu(self):
60 | try:
61 | answer = ""
62 | while True:
63 | print("""
64 | Please enter a command:
65 | \tlogin\tLogin
66 | \ttest\tTest the program
67 | \tchange\tChange the program
68 | \tquit\tQuit
69 | """)
70 | answer = input("enter a command: ").lower()
71 | try:
72 | func = self.menu_map[answer]
73 | except KeyError:
74 | print("{} is not a valid option".format(
75 | answer))
76 | else:
77 | func()
78 | finally:
79 | print("Thank you for testing the auth module")
80 |
81 |
82 | Editor().menu()
83 |
--------------------------------------------------------------------------------
/Chapter10/10_15_xml_states.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, tag_name, parent=None):
3 | self.parent = parent
4 | self.tag_name = tag_name
5 | self.children = []
6 | self.text=""
7 |
8 | def __str__(self):
9 | if self.text:
10 | return self.tag_name + ": " + self.text
11 | else:
12 | return self.tag_name
13 |
14 | class FirstTag:
15 | def process(self, remaining_string, parser):
16 | i_start_tag = remaining_string.find('<')
17 | i_end_tag = remaining_string.find('>')
18 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
19 | root = Node(tag_name)
20 | parser.root = parser.current_node = root
21 | parser.state = ChildNode()
22 | return remaining_string[i_end_tag+1:]
23 |
24 | class ChildNode:
25 | def process(self, remaining_string, parser):
26 | stripped = remaining_string.strip()
27 | if stripped.startswith(""):
28 | parser.state = CloseTag()
29 | elif stripped.startswith("<"):
30 | parser.state = OpenTag()
31 | else:
32 | parser.state = TextNode()
33 | return stripped
34 |
35 | class OpenTag:
36 | def process(self, remaining_string, parser):
37 | i_start_tag = remaining_string.find('<')
38 | i_end_tag = remaining_string.find('>')
39 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
40 | node = Node(tag_name, parser.current_node)
41 | parser.current_node.children.append(node)
42 | parser.current_node = node
43 | parser.state = ChildNode()
44 | return remaining_string[i_end_tag+1:]
45 |
46 | class TextNode:
47 | def process(self, remaining_string, parser):
48 | i_start_tag = remaining_string.find('<')
49 | text = remaining_string[:i_start_tag]
50 | parser.current_node.text = text
51 | parser.state = ChildNode()
52 | return remaining_string[i_start_tag:]
53 |
54 | class CloseTag:
55 | def process(self, remaining_string, parser):
56 | i_start_tag = remaining_string.find('<')
57 | i_end_tag = remaining_string.find('>')
58 | assert remaining_string[i_start_tag+1] == "/"
59 | tag_name = remaining_string[i_start_tag+2:i_end_tag]
60 | assert tag_name == parser.current_node.tag_name
61 | parser.current_node = parser.current_node.parent
62 | parser.state = ChildNode()
63 | return remaining_string[i_end_tag+1:].strip()
64 |
65 | class Parser:
66 | def __init__(self, parse_string):
67 | self.parse_string = parse_string
68 | self.root = None
69 | self.current_node = None
70 |
71 | self.state = FirstTag()
72 |
73 | def process(self, remaining_string):
74 | remaining = self.state.process(remaining_string, self)
75 | if remaining:
76 | self.process(remaining)
77 |
78 | def start(self):
79 | self.process(self.parse_string)
80 |
81 | if __name__ == "__main__":
82 | import sys
83 | with open(sys.argv[1]) as file:
84 | contents = file.read()
85 | p = Parser(contents)
86 | p.start()
87 |
88 | nodes = [p.root]
89 | while nodes:
90 | node = nodes.pop(0)
91 | print(node)
92 | nodes = node.children + nodes
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/Chapter10/10_17_xml_singletonstates.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | def __init__(self, tag_name, parent=None):
3 | self.parent = parent
4 | self.tag_name = tag_name
5 | self.children = []
6 | self.text=""
7 |
8 | def __str__(self):
9 | if self.text:
10 | return self.tag_name + ": " + self.text
11 | else:
12 | return self.tag_name
13 |
14 | class FirstTag:
15 | def process(self, remaining_string, parser):
16 | i_start_tag = remaining_string.find('<')
17 | i_end_tag = remaining_string.find('>')
18 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
19 | root = Node(tag_name)
20 | parser.root = parser.current_node = root
21 | parser.state = child_node
22 | return remaining_string[i_end_tag+1:]
23 |
24 | class ChildNode:
25 | def process(self, remaining_string, parser):
26 | stripped = remaining_string.strip()
27 | if stripped.startswith(""):
28 | parser.state = close_tag
29 | elif stripped.startswith("<"):
30 | parser.state = open_tag
31 | else:
32 | parser.state = text_node
33 | return stripped
34 |
35 | class OpenTag:
36 | def process(self, remaining_string, parser):
37 | i_start_tag = remaining_string.find('<')
38 | i_end_tag = remaining_string.find('>')
39 | tag_name = remaining_string[i_start_tag+1:i_end_tag]
40 | node = Node(tag_name, parser.current_node)
41 | parser.current_node.children.append(node)
42 | parser.current_node = node
43 | parser.state = child_node
44 | return remaining_string[i_end_tag+1:]
45 |
46 | class TextNode:
47 | def process(self, remaining_string, parser):
48 | i_start_tag = remaining_string.find('<')
49 | text = remaining_string[:i_start_tag]
50 | parser.current_node.text = text
51 | parser.state = child_node
52 | return remaining_string[i_start_tag:]
53 |
54 | class CloseTag:
55 | def process(self, remaining_string, parser):
56 | i_start_tag = remaining_string.find('<')
57 | i_end_tag = remaining_string.find('>')
58 | assert remaining_string[i_start_tag+1] == "/"
59 | tag_name = remaining_string[i_start_tag+2:i_end_tag]
60 | assert tag_name == parser.current_node.tag_name
61 | parser.current_node = parser.current_node.parent
62 | parser.state = child_node
63 | return remaining_string[i_end_tag+1:].strip()
64 |
65 | first_tag = FirstTag()
66 | child_node = ChildNode()
67 | text_node = TextNode()
68 | open_tag = OpenTag()
69 | close_tag = CloseTag()
70 |
71 | class Parser:
72 | def __init__(self, parse_string):
73 | self.parse_string = parse_string
74 | self.root = None
75 | self.current_node = None
76 |
77 | self.state = first_tag
78 |
79 | def process(self, remaining_string):
80 | remaining = self.state.process(remaining_string, self)
81 | if remaining:
82 | self.process(remaining)
83 |
84 | def start(self):
85 | self.process(self.parse_string)
86 |
87 | if __name__ == "__main__":
88 | import sys
89 | with open(sys.argv[1]) as file:
90 | contents = file.read()
91 | p = Parser(contents)
92 | p.start()
93 |
94 | nodes = [p.root]
95 | while nodes:
96 | node = nodes.pop(0)
97 | print(node)
98 | nodes = node.children + nodes
99 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # Python 3 Object-oriented Programming - Second Edition
5 |
6 |
7 |
8 | This is the code repository for [Python 3 Object-oriented Programming - Second Edition ](https://prod.packtpub.com/in/application-development/python-3-object-oriented-programming-second-edition?utm_source=github&utm_medium=repository&utm_campaign=), published by Packt.
9 |
10 | **Building robust and maintainable software with object oriented design patterns in Python**
11 |
12 | ## What is this book about?
13 |
14 | This book covers the following exciting features:
15 |
16 | * Implement objects in Python by creating classes and defining methods
17 | * Separate related objects into a taxonomy of classes and describe the properties and behaviors of those objects via the class interface
18 | * Extend class functionality using inheritance
19 | * Understand when to use object-oriented features, and more importantly when not to use them
20 | * Discover what design patterns are and why they are different in Python
21 |
22 | If you feel this book is for you, get your [copy](https://www.amazon.com/dp/1784398780) today!
23 |
24 |
26 |
27 | ## Instructions and Navigations
28 |
29 | **Following is what you need for this book:**
30 |
31 | This book specifically targets people who are new to object-oriented programming. It assumes you have basic Python skills. You'll learn object-oriented principles in depth. It is particularly useful for system administrator types who have used Python as a "glue" language and would like to improve their programming skills.
32 | If you are familiar with object-oriented programming in other languages, then this book will help you understand the idiomatic ways to apply your knowledge in the Python ecosystem
33 |
34 | With the following software and hardware list you can run all code files present in the book (Chapter 1-13).
35 | ### Software and Hardware List
36 | | Chapter | Software required | OS required |
37 | | -------- | ------------------------------------ | ----------------------------------- |
38 | | All | Python 3.4 | Windows, Mac OS X, and Linux (Any) |
39 |
40 |
41 |
42 | ## Get to Know the Author
43 | **Dusty Phillips**
44 | is a Canadian software developer and author currently living in New Brunswick. He has been active in the open source community for two decades and has been programming in Python for nearly as long. He holds a master's degree in computer science and has worked for Facebook, the United Nations, and several start-ups. He's currently researching privacy-preserving technology at beanstalk.network.
45 |
46 |
47 | ### Suggestions and Feedback
48 | [Click here](https://docs.google.com/forms/d/e/1FAIpQLSdy7dATC6QmEL81FIUuymZ0Wy9vH1jHkvpY57OiMeKGqib_Ow/viewform) if you have any feedback or suggestions.
49 |
50 |
51 | ### Download a free PDF
52 |
53 | If you have already purchased a print or Kindle version of this book, you can get a DRM-free PDF version at no cost.
Simply click on the link to claim your free PDF.
54 | https://packt.link/free-ebook/9781784398781
--------------------------------------------------------------------------------