├── Chapter01 └── ch1 │ ├── bike.py │ ├── example │ ├── core.py │ ├── run.py │ └── util │ │ ├── __init__.py │ │ ├── db.py │ │ ├── math.py │ │ └── network.py │ ├── factorial.py │ ├── files_only │ ├── core.py │ ├── db.py │ ├── math.py │ ├── network.py │ └── run.py │ ├── names.py │ ├── scopes1.py │ ├── scopes2.py │ ├── scopes3.py │ └── virtualenv.creation.txt ├── Chapter02 └── ch2 │ ├── README.md │ ├── bytearray.py │ ├── chainmap.py │ ├── defaultdict.py │ ├── dicts.py │ ├── enum.py │ ├── final_considerations.py │ ├── lists.py │ ├── namedtuple.py │ ├── numbers.py │ ├── objects.py │ ├── sequences.py │ ├── sets.py │ └── tuples.py ├── Chapter03 └── ch3 │ ├── any.py │ ├── binary.2.py │ ├── binary.py │ ├── compress.py │ ├── conditional.1.py │ ├── conditional.2.py │ ├── coupons.dict.py │ ├── coupons.py │ ├── discount.py │ ├── errorsalert.py │ ├── for.else.py │ ├── for.no.else.py │ ├── infinite.py │ ├── multiple.sequences.enumerate.py │ ├── multiple.sequences.explicit.py │ ├── multiple.sequences.implicit.py │ ├── multiple.sequences.py │ ├── multiple.sequences.while.py │ ├── multiple.sequences.zip.py │ ├── permutations.py │ ├── primes.else.py │ ├── primes.py │ ├── range.py │ ├── simple.for.2.py │ ├── simple.for.3.py │ ├── simple.for.4.py │ ├── simple.for.py │ ├── switch.js │ ├── switch.py │ ├── taxes.py │ └── ternary.py ├── Chapter04 └── ch4 │ ├── additional.unpacking.py │ ├── arguments.all.kwonly.py │ ├── arguments.all.py │ ├── arguments.default.error.py │ ├── arguments.default.py │ ├── arguments.defaults.mutable.intermediate.call.py │ ├── arguments.defaults.mutable.no.trap.py │ ├── arguments.defaults.mutable.py │ ├── arguments.keyword.only.py │ ├── arguments.keyword.py │ ├── arguments.positional.py │ ├── arguments.variable.db.py │ ├── arguments.variable.keyword.py │ ├── arguments.variable.positional.py │ ├── arguments.variable.positional.unpacking.py │ ├── data.science.example.py │ ├── docstrings.py │ ├── filter.lambda.py │ ├── filter.regular.py │ ├── func.attributes.py │ ├── func_from.py │ ├── func_import.py │ ├── key.points.argument.passing.py │ ├── key.points.assignment.py │ ├── key.points.mutable.assignment.py │ ├── key.points.mutable.py │ ├── lambda.explained.py │ ├── lib │ ├── __init__.py │ └── funcdef.py │ ├── matrix.multiplication.func.py │ ├── matrix.multiplication.nofunc.py │ ├── no.side.effects.py │ ├── primes.py │ ├── recursive.factorial.py │ ├── return.multiple.py │ ├── return.none.py │ ├── return.single.value.2.py │ ├── return.single.value.py │ ├── scoping.level.1.py │ ├── scoping.level.2.global.py │ ├── scoping.level.2.nonlocal.py │ ├── scoping.level.2.py │ ├── vat.function.py │ └── vat.py ├── Chapter05 └── ch5 │ ├── files │ ├── buffer.py │ ├── compression │ │ ├── content1.txt │ │ ├── content2.txt │ │ ├── subfolder │ │ │ ├── content3.txt │ │ │ └── content4.txt │ │ ├── tar.py │ │ └── zip.py │ ├── existence.py │ ├── fear.txt │ ├── fixed_amount.py │ ├── listing.py │ ├── manipulation.py │ ├── open_try.py │ ├── open_with.py │ ├── ops_create.py │ ├── paths.py │ ├── print_file.py │ ├── read_write.py │ ├── read_write_bin.py │ ├── tmp.py │ ├── walking.py │ └── write_not_exists.py │ ├── io_examples │ ├── reqs.py │ ├── reqs_post.py │ └── string_io.py │ ├── json_examples │ ├── json_basic.py │ ├── json_cplx.py │ ├── json_datetime.py │ └── json_tuple.py │ └── persistence │ ├── alchemy.py │ ├── alchemy_models.py │ ├── pickler.py │ └── shelf.py ├── Chapter07 ├── circularly_linked_list.py ├── doubly_linked_list.py └── singly_linked_list.py ├── Chapter08 ├── queue.py ├── queue_application.py └── stack_queue_1.py ├── Chapter09 ├── bintree_tree.py └── rpn.py ├── Chapter10 ├── hash.py ├── hashf.py └── hashtable.py ├── Chapter11 └── heap.py ├── Chapter12 ├── bin_search.py ├── interpolation_search.py ├── ordered_list_search.py └── unordered_list_search.py ├── Chapter13 ├── bubble.py ├── insert_sort.py ├── proper_quick_sort.py └── selection_sortion.py ├── Chapter14 ├── deterministic_search.py └── randomized_search.py ├── Chapter16 ├── Case Study_ Notebook │ ├── command_option.py │ ├── menu.py │ └── notebook.py ├── first_class.py ├── guarding.py ├── inner_class.py ├── name_mangling.py └── point.py ├── Chapter17 ├── Case Study_ Online Grader │ ├── example_runner.py │ ├── grader.py │ └── lessons.py ├── audio_player.py ├── contact.py ├── contact_multiple_inheritance.py ├── multiple_inheritance_bad_demo.py ├── multiple_inheritance_super.py └── odd_container.py ├── Chapter18 ├── Case Study_ Authorization │ ├── auth.py │ └── auth_driver.py ├── all_exception_syntax.py ├── even_integers.py ├── funny_division.py ├── inventory.py └── no_return.py ├── Chapter19 ├── Document.py ├── cached_webpage.py ├── colors.py ├── points.py ├── silly.py ├── zip_find_replace.py └── zip_processor.py ├── Chapter20 ├── Case Study_ Mailing Lists │ ├── addresses.db │ └── mailing_list.py ├── augmented_move.py ├── custom_sequence.py ├── default_options.py ├── enumerate_lines.py ├── event_timer.py └── passing_functions.py ├── Chapter21 ├── Case Study_ Machine Learning │ ├── check_output.py │ ├── colors.csv │ ├── machine_learn.py │ ├── manual_classify.py │ ├── output.csv │ └── second_edition_machine_learn_with_coroutines.py ├── EXAMPLE_LOG.log ├── book_authors.py ├── capital_iterator.py ├── filesystem_generator.py ├── list_comp.py ├── list_comp_read_file.py ├── log_loop.py ├── sample_log.txt ├── score_tally.py ├── warning_generators.py └── xfs_error_log.py ├── Chapter22 ├── car_sales_template.py ├── inventory_observer.py ├── log_calls_decorator.py ├── singleton_state.py ├── socket_client.py ├── socket_decorator.py ├── tiling_strategy.py ├── xml_example.xml └── xml_state_parser.py ├── Chapter23 ├── age_calculator_adapter.py ├── car_flyweight.py ├── email_facade.py ├── folder_composite.py ├── formatter_factory.py ├── pythonic_window_command.py └── window_command.py ├── Chapter24 ├── Case Study_ Vigenère cipher │ ├── test_vigenere.py │ └── vigenere_cipher.py ├── average_raises.py ├── echo_server.py ├── first_unittest.py ├── flight_status_redis.py ├── stats.py ├── stats_list_setup.py ├── test_echo_server.py ├── test_flight_status.py ├── test_pytest_cleanup.py ├── test_pytest_setups.py ├── test_pytest_skipping.py ├── test_pytest_stats.py ├── test_skipping.py ├── test_statslist_setup.py └── test_with_pytest.py ├── LICENSE └── README.md /Chapter01/ch1/bike.py: -------------------------------------------------------------------------------- 1 | # let's define the class Bike 2 | class Bike: 3 | 4 | def __init__(self, colour, frame_material): 5 | self.colour = colour 6 | self.frame_material = frame_material 7 | 8 | def brake(self): 9 | print("Braking!") 10 | 11 | # let's create a couple of instances 12 | red_bike = Bike('Red', 'Carbon fiber') 13 | blue_bike = Bike('Blue', 'Steel') 14 | 15 | # let's inspect the objects we have, instances of the Bike class. 16 | print(red_bike.colour) # prints: Red 17 | print(red_bike.frame_material) # prints: Carbon fiber 18 | print(blue_bike.colour) # prints: Blue 19 | print(blue_bike.frame_material) # prints: Steel 20 | 21 | # let's brake! 22 | red_bike.brake() # prints: Braking! 23 | -------------------------------------------------------------------------------- /Chapter01/ch1/example/core.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/example/core.py -------------------------------------------------------------------------------- /Chapter01/ch1/example/run.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/example/run.py -------------------------------------------------------------------------------- /Chapter01/ch1/example/util/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/example/util/__init__.py -------------------------------------------------------------------------------- /Chapter01/ch1/example/util/db.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/example/util/db.py -------------------------------------------------------------------------------- /Chapter01/ch1/example/util/math.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/example/util/math.py -------------------------------------------------------------------------------- /Chapter01/ch1/example/util/network.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/example/util/network.py -------------------------------------------------------------------------------- /Chapter01/ch1/factorial.py: -------------------------------------------------------------------------------- 1 | >>> from math import factorial 2 | >>> factorial(5) 3 | 120 -------------------------------------------------------------------------------- /Chapter01/ch1/files_only/core.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/files_only/core.py -------------------------------------------------------------------------------- /Chapter01/ch1/files_only/db.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/files_only/db.py -------------------------------------------------------------------------------- /Chapter01/ch1/files_only/math.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/files_only/math.py -------------------------------------------------------------------------------- /Chapter01/ch1/files_only/network.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/files_only/network.py -------------------------------------------------------------------------------- /Chapter01/ch1/files_only/run.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter01/ch1/files_only/run.py -------------------------------------------------------------------------------- /Chapter01/ch1/names.py: -------------------------------------------------------------------------------- 1 | >>> n = 3 # integer number 2 | >>> address = "221b Baker Street, NW1 6XE, London" # Sherlock Holmes' address 3 | >>> employee = { 4 | ... 'age': 45, 5 | ... 'role': 'CTO', 6 | ... 'SSN': 'AB1234567', 7 | ... } 8 | >>> # let's print them 9 | >>> n 10 | 3 11 | >>> address 12 | '221b Baker Street, NW1 6XE, London' 13 | >>> employee 14 | {'age': 45, 'role': 'CTO', 'SSN': 'AB1234567'} 15 | >>> other_name 16 | Traceback (most recent call last): 17 | File "", line 1, in 18 | NameError: name 'other_name' is not defined 19 | >>> -------------------------------------------------------------------------------- /Chapter01/ch1/scopes1.py: -------------------------------------------------------------------------------- 1 | # Local versus Global 2 | 3 | # we define a function, called local 4 | def local(): 5 | m = 7 6 | print(m) 7 | 8 | m = 5 9 | print(m) 10 | 11 | # we call, or `execute` the function local 12 | local() 13 | -------------------------------------------------------------------------------- /Chapter01/ch1/scopes2.py: -------------------------------------------------------------------------------- 1 | # Local versus Global 2 | 3 | def local(): 4 | # m doesn't belong to the scope defined by the local function 5 | # so Python will keep looking into the next enclosing scope. 6 | # m is finally found in the global scope 7 | print(m, 'printing from the local scope') 8 | 9 | m = 5 10 | print(m, 'printing from the global scope') 11 | 12 | local() 13 | -------------------------------------------------------------------------------- /Chapter01/ch1/scopes3.py: -------------------------------------------------------------------------------- 1 | # Local, Enclosing and Global 2 | 3 | 4 | def enclosing_func(): 5 | m = 13 6 | 7 | def local(): 8 | # m doesn't belong to the scope defined by the local 9 | # function so Python will keep looking into the next 10 | # enclosing scope. This time m is found in the enclosing 11 | # scope 12 | print(m, 'printing from the local scope') 13 | 14 | # calling the function local 15 | local() 16 | 17 | m = 5 18 | print(m, 'printing from the global scope') 19 | 20 | enclosing_func() 21 | -------------------------------------------------------------------------------- /Chapter01/ch1/virtualenv.creation.txt: -------------------------------------------------------------------------------- 1 | fabmp:srv fab$ # step 1 - create folder 2 | fabmp:srv fab$ mkdir learn.pp 3 | fabmp:srv fab$ cd learn.pp 4 | 5 | fabmp:learn.pp fab$ # step 2 - create virtual environment 6 | fabmp:learn.pp fab$ which python3.7 7 | /Users/fab/.pyenv/shims/python3.7 8 | fabmp:learn.pp fab$ virtualenv -p 9 | ⇢ /Users/fab/.pyenv/shims/python3.7 learnpp 10 | Running virtualenv with interpreter /Users/fab/.pyenv/shims/python3.7 11 | Using base prefix '/Users/fab/.pyenv/versions/3.7.0a3' 12 | New python executable in /Users/fab/srv/learn.pp/learnpp/bin/python3.7 13 | Also creating executable in /Users/fab/srv/learn.pp/learnpp/bin/python 14 | Installing setuptools, pip, wheel...done. 15 | 16 | fabmp:learn.pp fab$ # step 3 - activate virtual environment 17 | fabmp:learn.pp fab$ source learnpp/bin/activate 18 | 19 | (learnpp) fabmp:learn.pp fab$ # step 4 - verify which python 20 | (learnpp) fabmp:learn.pp fab$ which python 21 | /Users/fab/srv/learn.pp/learnpp/bin/python 22 | 23 | (learnpp) fabmp:learn.pp fab$ python 24 | Python 3.7.0a3 (default, Jan 27 2018, 00:46:45) 25 | [Clang 9.0.0 (clang-900.0.39.2)] on darwin 26 | Type "help", "copyright", "credits" or "license" for more information. 27 | >>> exit() 28 | 29 | (learnpp) fabmp:learn.pp fab$ # step 5 - deactivate 30 | (learnpp) fabmp:learn.pp fab$ deactivate 31 | fabmp:learn.pp fab$ 32 | -------------------------------------------------------------------------------- /Chapter02/ch2/README.md: -------------------------------------------------------------------------------- 1 | Chapter 2 data files 2 | ==================== 3 | 4 | The files in this folder are not supposed to work if run. 5 | They serve as source for the book chapters, and to provide a 6 | quick copy/paste tool for whoever would need their content. 7 | -------------------------------------------------------------------------------- /Chapter02/ch2/bytearray.py: -------------------------------------------------------------------------------- 1 | # bytearray.py 2 | 3 | 4 | >>> bytearray() # empty bytearray object 5 | bytearray(b'') 6 | >>> bytearray(10) # zero-filled instance with given length 7 | bytearray(b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00') 8 | >>> bytearray(range(5)) # bytearray from iterable of integers 9 | bytearray(b'\x00\x01\x02\x03\x04') 10 | >>> name = bytearray(b'Lina') #A - bytearray from bytes 11 | >>> name.replace(b'L', b'l') 12 | bytearray(b'lina') 13 | >>> name.endswith(b'na') 14 | True 15 | >>> name.upper() 16 | bytearray(b'LINA') 17 | >>> name.count(b'L') 18 | 1 19 | -------------------------------------------------------------------------------- /Chapter02/ch2/chainmap.py: -------------------------------------------------------------------------------- 1 | # chainmap.py 2 | 3 | 4 | >>> from collections import ChainMap 5 | >>> default_connection = {'host': 'localhost', 'port': 4567} 6 | >>> connection = {'port': 5678} 7 | >>> conn = ChainMap(connection, default_connection) # map creation 8 | >>> conn['port'] # port is found in the first dictionary 9 | 5678 10 | >>> conn['host'] # host is fetched from the second dictionary 11 | 'localhost' 12 | >>> conn.maps # we can see the mapping objects 13 | [{'port': 5678}, {'host': 'localhost', 'port': 4567}] 14 | >>> conn['host'] = 'packtpub.com' # let's add host 15 | >>> conn.maps 16 | [{'port': 5678, 'host': 'packtpub.com'}, 17 | {'host': 'localhost', 'port': 4567}] 18 | >>> del conn['port'] # let's remove the port information 19 | >>> conn.maps 20 | [{'host': 'packtpub.com'}, {'host': 'localhost', 'port': 4567}] 21 | >>> conn['port'] # now port is fetched from the second dictionary 22 | 4567 23 | >>> dict(conn) # easy to merge and convert to regular dictionary 24 | {'host': 'packtpub.com', 'port': 4567} -------------------------------------------------------------------------------- /Chapter02/ch2/defaultdict.py: -------------------------------------------------------------------------------- 1 | # defaultdict.py 2 | 3 | 4 | >>> d = {} 5 | >>> d['age'] = d.get('age', 0) + 1 # age not there, we get 0 + 1 6 | >>> d 7 | {'age': 1} 8 | >>> d = {'age': 39} 9 | >>> d['age'] = d.get('age', 0) + 1 # age is there, we get 40 10 | >>> d 11 | {'age': 40} 12 | 13 | 14 | >>> from collections import defaultdict 15 | >>> dd = defaultdict(int) # int is the default type (0 the value) 16 | >>> dd['age'] += 1 # short for dd['age'] = dd['age'] + 1 17 | >>> dd 18 | defaultdict(, {'age': 1}) # 1, as expected 19 | -------------------------------------------------------------------------------- /Chapter02/ch2/dicts.py: -------------------------------------------------------------------------------- 1 | # dicts.py 2 | 3 | 4 | >>> a = dict(A=1, Z=-1) 5 | >>> b = {'A': 1, 'Z': -1} 6 | >>> c = dict(zip(['A', 'Z'], [1, -1])) 7 | >>> d = dict([('A', 1), ('Z', -1)]) 8 | >>> e = dict({'Z': -1, 'A': 1}) 9 | >>> a == b == c == d == e # are they all the same? 10 | True # They are indeed 11 | 12 | 13 | # zip 14 | >>> list(zip(['h', 'e', 'l', 'l', 'o'], [1, 2, 3, 4, 5])) 15 | [('h', 1), ('e', 2), ('l', 3), ('l', 4), ('o', 5)] 16 | >>> list(zip('hello', range(1, 6))) # equivalent, more pythonic 17 | [('h', 1), ('e', 2), ('l', 3), ('l', 4), ('o', 5)] 18 | 19 | 20 | # basic 21 | >>> d = {} 22 | >>> d['a'] = 1 # let's set a couple of (key, value) pairs 23 | >>> d['b'] = 2 24 | >>> len(d) # how many pairs? 25 | 2 26 | >>> d['a'] # what is the value of 'a'? 27 | 1 28 | >>> d # how does `d` look now? 29 | {'a': 1, 'b': 2} 30 | >>> del d['a'] # let's remove `a` 31 | >>> d 32 | {'b': 2} 33 | >>> d['c'] = 3 # let's add 'c': 3 34 | >>> 'c' in d # membership is checked against the keys 35 | True 36 | >>> 3 in d # not the values 37 | False 38 | >>> 'e' in d 39 | False 40 | >>> d.clear() # let's clean everything from this dictionary 41 | >>> d 42 | {} 43 | 44 | 45 | 46 | # views 47 | >>> d = dict(zip('hello', range(5))) 48 | >>> d 49 | {'h': 0, 'e': 1, 'l': 3, 'o': 4} 50 | >>> d.keys() 51 | dict_keys(['h', 'e', 'l', 'o']) 52 | >>> d.values() 53 | dict_values([0, 1, 3, 4]) 54 | >>> d.items() 55 | dict_items([('h', 0), ('e', 1), ('l', 3), ('o', 4)]) 56 | >>> 3 in d.values() 57 | True 58 | >>> ('o', 4) in d.items() 59 | True 60 | 61 | 62 | # other methods 63 | >>> d 64 | {'e': 1, 'h': 0, 'o': 4, 'l': 3} 65 | >>> d.popitem() # removes a random item (useful in algorithms) 66 | ('o', 4) 67 | >>> d 68 | {'h': 0, 'e': 1, 'l': 3} 69 | >>> d.pop('l') # remove item with key `l` 70 | 3 71 | >>> d.pop('not-a-key') # remove a key not in dictionary: KeyError 72 | Traceback (most recent call last): 73 | File "", line 1, in 74 | KeyError: 'not-a-key' 75 | >>> d.pop('not-a-key', 'default-value') # with a default value? 76 | 'default-value' # we get the default value 77 | >>> d.update({'another': 'value'}) # we can update dict this way 78 | >>> d.update(a=13) # or this way (like a function call) 79 | >>> d 80 | {'h': 0, 'e': 1, 'another': 'value', 'a': 13} 81 | >>> d.get('a') # same as d['a'] but if key is missing no KeyError 82 | 13 83 | >>> d.get('a', 177) # default value used if key is missing 84 | 13 85 | >>> d.get('b', 177) # like in this case 86 | 177 87 | >>> d.get('b') # key is not there, so None is returned 88 | 89 | 90 | # setdefault 91 | >>> d = {} 92 | >>> d.setdefault('a', 1) # 'a' is missing, we get default value 93 | 1 94 | >>> d 95 | {'a': 1} # also, the key/value pair ('a', 1) has now been added 96 | >>> d.setdefault('a', 5) # let's try to override the value 97 | 1 98 | >>> d 99 | {'a': 1} # no override, as expected 100 | 101 | 102 | # setdefault example 103 | >>> d = {} 104 | >>> d.setdefault('a', {}).setdefault('b', []).append(1) 105 | >>> d 106 | {'a': {'b': [1]}} 107 | -------------------------------------------------------------------------------- /Chapter02/ch2/enum.py: -------------------------------------------------------------------------------- 1 | >>> GREEN = 1 2 | >>> YELLOW = 2 3 | >>> RED = 4 4 | >>> TRAFFIC_LIGHTS = (GREEN, YELLOW, RED) 5 | >>> # or with a dict 6 | >>> traffic_lights = {'GREEN': 1, 'YELLOW': 2, 'RED': 4} 7 | 8 | 9 | 10 | # using enum 11 | >>> from enum import Enum 12 | >>> class TrafficLight(Enum): 13 | ... GREEN = 1 14 | ... YELLOW = 2 15 | ... RED = 4 16 | ... 17 | >>> TrafficLight.GREEN 18 | 19 | >>> TrafficLight.GREEN.name 20 | 'GREEN' 21 | >>> TrafficLight.GREEN.value 22 | 1 23 | >>> TrafficLight(1) 24 | 25 | >>> TrafficLight(4) 26 | 27 | -------------------------------------------------------------------------------- /Chapter02/ch2/final_considerations.py: -------------------------------------------------------------------------------- 1 | # final_considerations.py 2 | 3 | 4 | >>> a = 1000000 5 | >>> b = 1000000 6 | >>> id(a) == id(b) 7 | False 8 | 9 | 10 | >>> a = 5 11 | >>> b = 5 12 | >>> id(a) == id(b) 13 | True 14 | 15 | 16 | # how to choose data structures 17 | # example customer objects 18 | customer1 = {'id': 'abc123', 'full_name': 'Master Yoda'} 19 | customer2 = {'id': 'def456', 'full_name': 'Obi-Wan Kenobi'} 20 | customer3 = {'id': 'ghi789', 'full_name': 'Anakin Skywalker'} 21 | # collect them in a tuple 22 | customers = (customer1, customer2, customer3) 23 | # or collect them in a list 24 | customers = [customer1, customer2, customer3] 25 | # or maybe within a dictionary, they have a unique id after all 26 | customers = { 27 | 'abc123': customer1, 28 | 'def456': customer2, 29 | 'ghi789': customer3, 30 | } 31 | 32 | 33 | # negative indexing 34 | >>> a = list(range(10)) # `a` has 10 elements. Last one is 9. 35 | >>> a 36 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 37 | >>> len(a) # its length is 10 elements 38 | 10 39 | >>> a[len(a) - 1] # position of last one is len(a) - 1 40 | 9 41 | >>> a[-1] # but we don't need len(a)! Python rocks! 42 | 9 43 | >>> a[-2] # equivalent to len(a) - 2 44 | 8 45 | >>> a[-3] # equivalent to len(a) - 3 46 | 7 47 | -------------------------------------------------------------------------------- /Chapter02/ch2/lists.py: -------------------------------------------------------------------------------- 1 | # lists.py 2 | 3 | 4 | # creation 5 | >>> [] # empty list 6 | [] 7 | >>> list() # same as [] 8 | [] 9 | >>> [1, 2, 3] # as with tuples, items are comma separated 10 | [1, 2, 3] 11 | >>> [x + 5 for x in [2, 3, 4]] # Python is magic 12 | [7, 8, 9] 13 | >>> list((1, 3, 5, 7, 9)) # list from a tuple 14 | [1, 3, 5, 7, 9] 15 | >>> list('hello') # list from a string 16 | ['h', 'e', 'l', 'l', 'o'] 17 | 18 | 19 | # main methods 20 | >>> a = [1, 2, 1, 3] 21 | >>> a.append(13) # we can append anything at the end 22 | >>> a 23 | [1, 2, 1, 3, 13] 24 | >>> a.count(1) # how many `1` are there in the list? 25 | 2 26 | >>> a.extend([5, 7]) # extend the list by another (or sequence) 27 | >>> a 28 | [1, 2, 1, 3, 13, 5, 7] 29 | >>> a.index(13) # position of `13` in the list (0-based indexing) 30 | 4 31 | >>> a.insert(0, 17) # insert `17` at position 0 32 | >>> a 33 | [17, 1, 2, 1, 3, 13, 5, 7] 34 | >>> a.pop() # pop (remove and return) last element 35 | 7 36 | >>> a.pop(3) # pop element at position 3 37 | 1 38 | >>> a 39 | [17, 1, 2, 3, 13, 5] 40 | >>> a.remove(17) # remove `17` from the list 41 | >>> a 42 | [1, 2, 3, 13, 5] 43 | >>> a.reverse() # reverse the order of the elements in the list 44 | >>> a 45 | [5, 13, 3, 2, 1] 46 | >>> a.sort() # sort the list 47 | >>> a 48 | [1, 2, 3, 5, 13] 49 | >>> a.clear() # remove all elements from the list 50 | >>> a 51 | [] 52 | 53 | 54 | # extending 55 | >>> a = list('hello') # makes a list from a string 56 | >>> a 57 | ['h', 'e', 'l', 'l', 'o'] 58 | >>> a.append(100) # append 100, heterogeneous type 59 | >>> a 60 | ['h', 'e', 'l', 'l', 'o', 100] 61 | >>> a.extend((1, 2, 3)) # extend using tuple 62 | >>> a 63 | ['h', 'e', 'l', 'l', 'o', 100, 1, 2, 3] 64 | >>> a.extend('...') # extend using string 65 | >>> a 66 | ['h', 'e', 'l', 'l', 'o', 100, 1, 2, 3, '.', '.', '.'] 67 | 68 | 69 | # most common operations 70 | >>> a = [1, 3, 5, 7] 71 | >>> min(a) # minimum value in the list 72 | 1 73 | >>> max(a) # maximum value in the list 74 | 7 75 | >>> sum(a) # sum of all values in the list 76 | 16 77 | >>> len(a) # number of elements in the list 78 | 4 79 | >>> b = [6, 7, 8] 80 | >>> a + b # `+` with list means concatenation 81 | [1, 3, 5, 7, 6, 7, 8] 82 | >>> a * 2 # `*` has also a special meaning 83 | [1, 3, 5, 7, 1, 3, 5, 7] 84 | 85 | 86 | # cool sorting 87 | >>> from operator import itemgetter 88 | >>> a = [(5, 3), (1, 3), (1, 2), (2, -1), (4, 9)] 89 | >>> sorted(a) 90 | [(1, 2), (1, 3), (2, -1), (4, 9), (5, 3)] 91 | >>> sorted(a, key=itemgetter(0)) 92 | [(1, 3), (1, 2), (2, -1), (4, 9), (5, 3)] 93 | >>> sorted(a, key=itemgetter(0, 1)) 94 | [(1, 2), (1, 3), (2, -1), (4, 9), (5, 3)] 95 | >>> sorted(a, key=itemgetter(1)) 96 | [(2, -1), (1, 2), (5, 3), (1, 3), (4, 9)] 97 | >>> sorted(a, key=itemgetter(1), reverse=True) 98 | [(4, 9), (5, 3), (1, 3), (1, 2), (2, -1)] 99 | -------------------------------------------------------------------------------- /Chapter02/ch2/namedtuple.py: -------------------------------------------------------------------------------- 1 | # namedtuple.py 2 | 3 | 4 | # the problem 5 | >>> vision = (9.5, 8.8) 6 | >>> vision 7 | (9.5, 8.8) 8 | >>> vision[0] # left eye (implicit positional reference) 9 | 9.5 10 | >>> vision[1] # right eye (implicit positional reference) 11 | 8.8 12 | 13 | 14 | # the solution 15 | >>> from collections import namedtuple 16 | >>> Vision = namedtuple('Vision', ['left', 'right']) 17 | >>> vision = Vision(9.5, 8.8) 18 | >>> vision[0] 19 | 9.5 20 | >>> vision.left # same as vision[0], but explicit 21 | 9.5 22 | >>> vision.right # same as vision[1], but explicit 23 | 8.8 24 | 25 | 26 | # the change 27 | >>> Vision = namedtuple('Vision', ['left', 'combined', 'right']) 28 | >>> vision = Vision(9.5, 9.2, 8.8) 29 | >>> vision.left # still correct 30 | 9.5 31 | >>> vision.right # still correct (though now is vision[2]) 32 | 8.8 33 | >>> vision.combined # the new vision[1] 34 | 9.2 35 | -------------------------------------------------------------------------------- /Chapter02/ch2/numbers.py: -------------------------------------------------------------------------------- 1 | # numbers.py 2 | 3 | 4 | # integers 5 | >>> a = 14 6 | >>> b = 3 7 | >>> a + b # addition 8 | 17 9 | >>> a - b # subtraction 10 | 11 11 | >>> a * b # multiplication 12 | 42 13 | >>> a / b # true division 14 | 4.666666666666667 15 | >>> a // b # integer division 16 | 4 17 | >>> a % b # modulo operation (reminder of division) 18 | 2 19 | >>> a ** b # power operation 20 | 2744 21 | 22 | 23 | 24 | # integer and true division 25 | >>> 7 / 4 # true division 26 | 1.75 27 | >>> 7 // 4 # integer division, truncation returns 1 28 | 1 29 | >>> -7 / 4 # true division again, result is opposite of previous 30 | -1.75 31 | >>> -7 // 4 # integer div., result not the opposite of previous 32 | -2 33 | 34 | # modulo operator 35 | >>> 10 % 3 # remainder of the division 10 // 3 36 | 1 37 | >>> 10 % 4 # remainder of the division 10 // 4 38 | 2 39 | 40 | 41 | # truncation towards 0 42 | >>> int(1.75) 43 | 1 44 | >>> int(-1.75) 45 | -1 46 | 47 | # underscored 48 | >>> n = 1_024 49 | >>> n 50 | 1024 51 | >>> hex_n = 0x_4_0_0 # 0x400 == 1024 52 | >>> hex_n 53 | 1024 54 | 55 | 56 | # booleans 57 | >>> int(True) # True behaves like 1 58 | 1 59 | >>> int(False) # False behaves like 0 60 | 0 61 | >>> bool(1) # 1 evaluates to True in a boolean context 62 | True 63 | >>> bool(-42) # and so does every non-zero number 64 | True 65 | >>> bool(0) # 0 evaluates to False 66 | False 67 | >>> # quick peak at the operators (and, or, not) 68 | >>> not True 69 | False 70 | >>> not False 71 | True 72 | >>> True and True 73 | True 74 | >>> False or True 75 | True 76 | 77 | 78 | # int and bool 79 | >>> 1 + True 80 | 2 81 | >>> False + 42 82 | 42 83 | >>> 7 - True 84 | 6 85 | 86 | 87 | # reals 88 | >>> pi = 3.1415926536 # how many digits of PI can you remember? 89 | >>> radius = 4.5 90 | >>> area = pi * (radius ** 2) 91 | >>> area 92 | 63.617251235400005 93 | 94 | 95 | # sys.float_info 96 | >>> import sys 97 | >>> sys.float_info 98 | sys.float_info(max=1.7976931348623157e+308, max_exp=1024, max_10_exp=308, min=2.2250738585072014e-308, min_exp=-1021, min_10_exp=-307, dig=15, mant_dig=53, epsilon=2.220446049250313e-16, radix=2, rounds=1) 99 | 100 | # approximation issue 101 | >>> 0.3 - 0.1 * 3 # this should be 0!!! 102 | -5.551115123125783e-17 103 | 104 | 105 | # complex 106 | >>> c = 3.14 + 2.73j 107 | >>> c.real # real part 108 | 3.14 109 | >>> c.imag # imaginary part 110 | 2.73 111 | >>> c.conjugate() # conjugate of A + Bj is A - Bj 112 | (3.14-2.73j) 113 | >>> c * 2 # multiplication is allowed 114 | (6.28+5.46j) 115 | >>> c ** 2 # power operation as well 116 | (2.4067000000000007+17.1444j) 117 | >>> d = 1 + 1j # addition and subtraction as well 118 | >>> c - d 119 | (2.14+1.73j) 120 | 121 | 122 | # fractions 123 | >>> from fractions import Fraction 124 | >>> Fraction(10, 6) # mad hatter? 125 | Fraction(5, 3) # notice it's been simplified 126 | >>> Fraction(1, 3) + Fraction(2, 3) # 1/3 + 2/3 == 3/3 == 1/1 127 | Fraction(1, 1) 128 | >>> f = Fraction(10, 6) 129 | >>> f.numerator 130 | 5 131 | >>> f.denominator 132 | 3 133 | 134 | 135 | # decimal 136 | >>> from decimal import Decimal as D # rename for brevity 137 | >>> D(3.14) # pi, from float, so approximation issues 138 | Decimal('3.140000000000000124344978758017532527446746826171875') 139 | >>> D('3.14') # pi, from a string, so no approximation issues 140 | Decimal('3.14') 141 | >>> D(0.1) * D(3) - D(0.3) # from float, we still have the issue 142 | Decimal('2.775557561565156540423631668E-17') 143 | >>> D('0.1') * D(3) - D('0.3') # from string, all perfect 144 | Decimal('0.0') 145 | >>> D('1.4').as_integer_ratio() # 7/5 = 1.4 (isn't this cool?!) 146 | (7, 5) 147 | -------------------------------------------------------------------------------- /Chapter02/ch2/objects.py: -------------------------------------------------------------------------------- 1 | # objects.py 2 | 3 | # code block # 1 4 | >>> age = 42 5 | >>> age 6 | 42 7 | >>> age = 43 #A 8 | >>> age 9 | 43 10 | 11 | 12 | # code block # 2 13 | >>> age = 42 14 | >>> id(age) 15 | 4377553168 16 | >>> age = 43 17 | >>> id(age) 18 | 4377553200 19 | 20 | 21 | # code block # 3 22 | >>> class Person(): 23 | ... def __init__(self, age): 24 | ... self.age = age 25 | ... 26 | >>> fab = Person(age=42) 27 | >>> fab.age 28 | 42 29 | >>> id(fab) 30 | 4380878496 31 | >>> id(fab.age) 32 | 4377553168 33 | >>> fab.age = 25 # I wish! 34 | >>> id(fab) # will be the same 35 | 4380878496 36 | >>> id(fab.age) # will be different 37 | 4377552624 -------------------------------------------------------------------------------- /Chapter02/ch2/sequences.py: -------------------------------------------------------------------------------- 1 | # sequences.py 2 | 3 | 4 | # strings 5 | >>> # 4 ways to make a string 6 | >>> str1 = 'This is a string. We built it with single quotes.' 7 | >>> str2 = "This is also a string, but built with double quotes." 8 | >>> str3 = '''This is built using triple quotes, 9 | ... so it can span multiple lines.''' 10 | >>> str4 = """This too 11 | ... is a multiline one 12 | ... built with triple double-quotes.""" 13 | >>> str4 #A 14 | 'This too\nis a multiline one\nbuilt with triple double-quotes.' 15 | >>> print(str4) #B 16 | This too 17 | is a multiline one 18 | built with triple double-quotes. 19 | >>> 20 | 21 | 22 | # encode / decode 23 | >>> s = "This is üŋíc0de" # unicode string: code points 24 | >>> type(s) 25 | 26 | >>> encoded_s = s.encode('utf-8') # utf-8 encoded version of s 27 | >>> encoded_s 28 | b'This is \xc3\xbc\xc5\x8b\xc3\xadc0de' # result: bytes object 29 | >>> type(encoded_s) # another way to verify it 30 | 31 | >>> encoded_s.decode('utf-8') # let's revert to the original 32 | 'This is üŋíc0de' 33 | >>> bytes_obj = b"A bytes object" # a bytes object 34 | >>> type(bytes_obj) 35 | 36 | 37 | 38 | # length 39 | >>> len(str1) 40 | 49 41 | 42 | 43 | # indexing and slicing 44 | >>> s = "The trouble is you think you have time." 45 | >>> s[0] # indexing at position 0, which is the first char 46 | 'T' 47 | >>> s[5] # indexing at position 5, which is the sixth char 48 | 'r' 49 | >>> s[:4] # slicing, we specify only the stop position 50 | 'The ' 51 | >>> s[4:] # slicing, we specify only the start position 52 | 'trouble is you think you have time.' 53 | >>> s[2:14] # slicing, both start and stop positions 54 | 'e trouble is' 55 | >>> s[2:14:3] # slicing, start, stop and step (every 3 chars) 56 | 'erb ' 57 | >>> s[:] # quick way of making a copy 58 | 'The trouble is you think you have time.' 59 | 60 | 61 | # formatting 62 | >>> greet_old = 'Hello %s!' 63 | >>> greet_old % 'Fabrizio' 64 | 'Hello Fabrizio!' 65 | >>> greet_positional = 'Hello {}!' 66 | >>> greet_positional.format('Fabrizio') 67 | 'Hello Fabrizio!' 68 | >>> greet_positional = 'Hello {} {}!' 69 | >>> greet_positional.format('Fabrizio', 'Romano') 70 | 'Hello Fabrizio Romano!' 71 | >>> greet_positional_idx = 'This is {0}! {1} loves {0}!' 72 | >>> greet_positional_idx.format('Python', 'Fabrizio') 73 | 'This is Python! Fabrizio loves Python!' 74 | >>> greet_positional_idx.format('Coffee', 'Fab') 75 | 'This is Coffee! Fab loves Coffee!' 76 | >>> keyword = 'Hello, my name is {name} {last_name}' 77 | >>> keyword.format(name='Fabrizio', last_name='Romano') 78 | 'Hello, my name is Fabrizio Romano' 79 | 80 | 81 | # formatted string literals 82 | >>> name = 'Fab' 83 | >>> age = 42 84 | >>> f"Hello! My name is {name} and I'm {age}" 85 | "Hello! My name is Fab and I'm 42" 86 | >>> from math import pi 87 | >>> f"No arguing with {pi}, it's irrational..." 88 | "No arguing with 3.141592653589793, it's irrational..." 89 | -------------------------------------------------------------------------------- /Chapter02/ch2/sets.py: -------------------------------------------------------------------------------- 1 | # sets.py 2 | 3 | 4 | >>> small_primes = set() # empty set 5 | >>> small_primes.add(2) # adding one element at a time 6 | >>> small_primes.add(3) 7 | >>> small_primes.add(5) 8 | >>> small_primes 9 | {2, 3, 5} 10 | >>> small_primes.add(1) # Look what I've done, 1 is not a prime! 11 | >>> small_primes 12 | {1, 2, 3, 5} 13 | >>> small_primes.remove(1) # so let's remove it 14 | >>> 3 in small_primes # membership test 15 | True 16 | >>> 4 in small_primes 17 | False 18 | >>> 4 not in small_primes # negated membership test 19 | True 20 | >>> small_primes.add(3) # trying to add 3 again 21 | >>> small_primes 22 | {2, 3, 5} # no change, duplication is not allowed 23 | >>> bigger_primes = set([5, 7, 11, 13]) # faster creation 24 | >>> small_primes | bigger_primes # union operator `|` 25 | {2, 3, 5, 7, 11, 13} 26 | >>> small_primes & bigger_primes # intersection operator `&` 27 | {5} 28 | >>> small_primes - bigger_primes # difference operator `-` 29 | {2, 3} 30 | 31 | 32 | >>> small_primes = {2, 3, 5, 5, 3} 33 | >>> small_primes 34 | {2, 3, 5} 35 | 36 | 37 | 38 | 39 | # frozenset 40 | >>> small_primes = frozenset([2, 3, 5, 7]) 41 | >>> bigger_primes = frozenset([5, 7, 11]) 42 | >>> small_primes.add(11) # we cannot add to a frozenset 43 | Traceback (most recent call last): 44 | File "", line 1, in 45 | AttributeError: 'frozenset' object has no attribute 'add' 46 | >>> small_primes.remove(2) # neither we can remove 47 | Traceback (most recent call last): 48 | File "", line 1, in 49 | AttributeError: 'frozenset' object has no attribute 'remove' 50 | >>> small_primes & bigger_primes # intersect, union, etc. allowed 51 | frozenset({5, 7}) 52 | -------------------------------------------------------------------------------- /Chapter02/ch2/tuples.py: -------------------------------------------------------------------------------- 1 | # tuples.py 2 | 3 | 4 | >>> t = () # empty tuple 5 | >>> type(t) 6 | 7 | >>> one_element_tuple = (42, ) # you need the comma! 8 | >>> three_elements_tuple = (1, 3, 5) # braces are optional here 9 | >>> a, b, c = 1, 2, 3 # tuple for multiple assignment 10 | >>> a, b, c # implicit tuple to print with one instruction 11 | (1, 2, 3) 12 | >>> 3 in three_elements_tuple # membership test 13 | True 14 | 15 | 16 | # swap 17 | >>> a, b = 1, 2 18 | >>> c = a # we need three lines and a temporary var c 19 | >>> a = b 20 | >>> b = c 21 | >>> a, b # a and b have been swapped 22 | (2, 1) 23 | 24 | # swap pythonic 25 | >>> a, b = 0, 1 26 | >>> a, b = b, a # this is the Pythonic way to do it 27 | >>> a, b 28 | (1, 0) -------------------------------------------------------------------------------- /Chapter03/ch3/any.py: -------------------------------------------------------------------------------- 1 | items = [0, None, 0.0, True, 0, 7] # True and 7 evaluate to True 2 | 3 | found = False # this is called "flag" 4 | for item in items: 5 | print('scanning item', item) 6 | if item: 7 | found = True # we update the flag 8 | break 9 | 10 | if found: # we inspect the flag 11 | print('At least one item evaluates to True') 12 | else: 13 | print('All items evaluate to False') 14 | -------------------------------------------------------------------------------- /Chapter03/ch3/binary.2.py: -------------------------------------------------------------------------------- 1 | n = 39 2 | remainders = [] 3 | while n > 0: 4 | n, remainder = divmod(n, 2) 5 | remainders.insert(0, remainder) 6 | 7 | print(remainders) 8 | -------------------------------------------------------------------------------- /Chapter03/ch3/binary.py: -------------------------------------------------------------------------------- 1 | """ 2 | 6 / 2 = 3 (remainder: 0) 3 | 3 / 2 = 1 (remainder: 1) 4 | 1 / 2 = 0 (remainder: 1) 5 | List of remainders: 0, 1, 1. 6 | Inverse is 1, 1, 0, which is also the binary repres. of 6: 110 7 | """ 8 | 9 | n = 39 10 | remainders = [] 11 | while n > 0: 12 | remainder = n % 2 # remainder of division by 2 13 | remainders.insert(0, remainder) # we keep track of remainders 14 | n //= 2 # we divide n by 2 15 | 16 | print(remainders) 17 | -------------------------------------------------------------------------------- /Chapter03/ch3/compress.py: -------------------------------------------------------------------------------- 1 | from itertools import compress 2 | data = range(10) 3 | even_selector = [1, 0] * 10 4 | odd_selector = [0, 1] * 10 5 | 6 | even_numbers = list(compress(data, even_selector)) 7 | odd_numbers = list(compress(data, odd_selector)) 8 | 9 | print(odd_selector) 10 | print(list(data)) 11 | print(even_numbers) 12 | print(odd_numbers) 13 | -------------------------------------------------------------------------------- /Chapter03/ch3/conditional.1.py: -------------------------------------------------------------------------------- 1 | late = True 2 | if late: 3 | print('I need to call my manager!') 4 | -------------------------------------------------------------------------------- /Chapter03/ch3/conditional.2.py: -------------------------------------------------------------------------------- 1 | late = False 2 | if late: 3 | print('I need to call my manager!') #1 4 | else: 5 | print('no need to call my manager...') #2 6 | -------------------------------------------------------------------------------- /Chapter03/ch3/coupons.dict.py: -------------------------------------------------------------------------------- 1 | customers = [ 2 | dict(id=1, total=200, coupon_code='F20'), # F20: fixed, £20 3 | dict(id=2, total=150, coupon_code='P30'), # P30: percent, 30% 4 | dict(id=3, total=100, coupon_code='P50'), # P50: percent, 50% 5 | dict(id=4, total=110, coupon_code='F15'), # F15: fixed, £15 6 | ] 7 | discounts = { 8 | 'F20': (0.0, 20.0), # each value is (percent, fixed) 9 | 'P30': (0.3, 0.0), 10 | 'P50': (0.5, 0.0), 11 | 'F15': (0.0, 15.0), 12 | } 13 | for customer in customers: 14 | code = customer['coupon_code'] 15 | percent, fixed = discounts.get(code, (0.0, 0.0)) 16 | customer['discount'] = percent * customer['total'] + fixed 17 | 18 | for customer in customers: 19 | print(customer['id'], customer['total'], customer['discount']) 20 | -------------------------------------------------------------------------------- /Chapter03/ch3/coupons.py: -------------------------------------------------------------------------------- 1 | customers = [ 2 | dict(id=1, total=200, coupon_code='F20'), # F20: fixed, £20 3 | dict(id=2, total=150, coupon_code='P30'), # P30: percent, 30% 4 | dict(id=3, total=100, coupon_code='P50'), # P50: percent, 50% 5 | dict(id=4, total=110, coupon_code='F15'), # F15: fixed, £15 6 | ] 7 | for customer in customers: 8 | code = customer['coupon_code'] 9 | if code == 'F20': 10 | customer['discount'] = 20.0 11 | elif code == 'F15': 12 | customer['discount'] = 15.0 13 | elif code == 'P30': 14 | customer['discount'] = customer['total'] * 0.3 15 | elif code == 'P50': 16 | customer['discount'] = customer['total'] * 0.5 17 | else: 18 | customer['discount'] = 0.0 19 | 20 | for customer in customers: 21 | print(customer['id'], customer['total'], customer['discount']) 22 | -------------------------------------------------------------------------------- /Chapter03/ch3/discount.py: -------------------------------------------------------------------------------- 1 | from datetime import date, timedelta 2 | 3 | today = date.today() 4 | tomorrow = today + timedelta(days=1) # today + 1 day is tomorrow 5 | products = [ 6 | {'sku': '1', 'expiration_date': today, 'price': 100.0}, 7 | {'sku': '2', 'expiration_date': tomorrow, 'price': 50}, 8 | {'sku': '3', 'expiration_date': today, 'price': 20}, 9 | ] 10 | 11 | for product in products: 12 | if product['expiration_date'] != today: 13 | continue 14 | product['price'] *= 0.8 # equivalent to applying 20% discount 15 | print( 16 | 'Price for sku', product['sku'], 17 | 'is now', product['price']) 18 | -------------------------------------------------------------------------------- /Chapter03/ch3/errorsalert.py: -------------------------------------------------------------------------------- 1 | # The send_email function is defined here to enable you to play with 2 | # code, but of course it doesn't send an actual email. 3 | def send_email(*a): 4 | print (*a) 5 | 6 | alert_system = 'console' # other value can be 'email' 7 | error_severity = 'critical' # other values: 'medium' or 'low' 8 | error_message = 'OMG! Something terrible happened!' 9 | 10 | if alert_system == 'console': 11 | print(error_message) #1 12 | elif alert_system == 'email': 13 | if error_severity == 'critical': 14 | send_email('admin@example.com', error_message) #2 15 | elif error_severity == 'medium': 16 | send_email('support.1@example.com', error_message) #3 17 | else: 18 | send_email('support.2@example.com', error_message) #4 19 | -------------------------------------------------------------------------------- /Chapter03/ch3/for.else.py: -------------------------------------------------------------------------------- 1 | class DriverException(Exception): 2 | pass 3 | 4 | 5 | people = [('James', 17), ('Kirk', 9), ('Lars', 13), ('Robert', 8)] 6 | for person, age in people: 7 | if age >= 18: 8 | driver = (person, age) 9 | break 10 | else: 11 | raise DriverException('Driver not found.') 12 | -------------------------------------------------------------------------------- /Chapter03/ch3/for.no.else.py: -------------------------------------------------------------------------------- 1 | class DriverException(Exception): 2 | pass 3 | 4 | 5 | people = [('James', 17), ('Kirk', 9), ('Lars', 13), ('Robert', 8)] 6 | driver = None 7 | for person, age in people: 8 | if age >= 18: 9 | driver = (person, age) 10 | break 11 | 12 | if driver is None: 13 | raise DriverException('Driver not found.') 14 | -------------------------------------------------------------------------------- /Chapter03/ch3/infinite.py: -------------------------------------------------------------------------------- 1 | from itertools import count 2 | 3 | for n in count(5, 3): 4 | if n > 20: 5 | break 6 | print(n, end=', ') # instead of newline, comma and space 7 | 8 | print() 9 | -------------------------------------------------------------------------------- /Chapter03/ch3/multiple.sequences.enumerate.py: -------------------------------------------------------------------------------- 1 | people = ['Conrad', 'Deepak', 'Heinrich', 'Tom'] 2 | ages = [29, 30, 34, 36] 3 | for position, person in enumerate(people): 4 | age = ages[position] 5 | print(person, age) 6 | -------------------------------------------------------------------------------- /Chapter03/ch3/multiple.sequences.explicit.py: -------------------------------------------------------------------------------- 1 | people = ['Conrad', 'Deepak', 'Heinrich', 'Tom'] 2 | ages = [29, 30, 34, 36] 3 | nationalities = ['Poland', 'India', 'South Africa', 'England'] 4 | for person, age, nationality in zip(people, ages, nationalities): 5 | print(person, age, nationality) 6 | -------------------------------------------------------------------------------- /Chapter03/ch3/multiple.sequences.implicit.py: -------------------------------------------------------------------------------- 1 | people = ['Conrad', 'Deepak', 'Heinrich', 'Tom'] 2 | ages = [29, 30, 34, 36] 3 | nationalities = ['Poland', 'India', 'South Africa', 'England'] 4 | for data in zip(people, ages, nationalities): 5 | person, age, nationality = data 6 | print(person, age, nationality) 7 | -------------------------------------------------------------------------------- /Chapter03/ch3/multiple.sequences.py: -------------------------------------------------------------------------------- 1 | people = ['Conrad', 'Deepak', 'Heinrich', 'Tom'] 2 | ages = [29, 30, 34, 36] 3 | for position in range(len(people)): 4 | person = people[position] 5 | age = ages[position] 6 | print(person, age) 7 | -------------------------------------------------------------------------------- /Chapter03/ch3/multiple.sequences.while.py: -------------------------------------------------------------------------------- 1 | people = ['Conrad', 'Deepak', 'Heinrich', 'Tom'] 2 | ages = [29, 30, 34, 36] 3 | position = 0 4 | while position < len(people): 5 | person = people[position] 6 | age = ages[position] 7 | print(person, age) 8 | position += 1 9 | -------------------------------------------------------------------------------- /Chapter03/ch3/multiple.sequences.zip.py: -------------------------------------------------------------------------------- 1 | people = ['Conrad', 'Deepak', 'Heinrich', 'Tom'] 2 | ages = [29, 30, 34, 36] 3 | for person, age in zip(people, ages): 4 | print(person, age) 5 | -------------------------------------------------------------------------------- /Chapter03/ch3/permutations.py: -------------------------------------------------------------------------------- 1 | from itertools import permutations 2 | print(list(permutations('ABC'))) 3 | -------------------------------------------------------------------------------- /Chapter03/ch3/primes.else.py: -------------------------------------------------------------------------------- 1 | primes = [] 2 | upto = 100 3 | for n in range(2, upto + 1): 4 | for divisor in range(2, n): 5 | if n % divisor == 0: 6 | break 7 | else: 8 | primes.append(n) 9 | 10 | print(primes) 11 | -------------------------------------------------------------------------------- /Chapter03/ch3/primes.py: -------------------------------------------------------------------------------- 1 | primes = [] # this will contain the primes in the end 2 | upto = 100 # the limit, inclusive 3 | for n in range(2, upto + 1): 4 | is_prime = True # flag, new at each iteration of outer for 5 | for divisor in range(2, n): 6 | if n % divisor == 0: 7 | is_prime = False 8 | break 9 | if is_prime: # check on flag 10 | primes.append(n) 11 | 12 | print(primes) 13 | -------------------------------------------------------------------------------- /Chapter03/ch3/range.py: -------------------------------------------------------------------------------- 1 | >>> list(range(10)) # one value: from 0 to value (excluded) 2 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 3 | >>> list(range(3, 8)) # two values: from start to stop (excluded) 4 | [3, 4, 5, 6, 7] 5 | >>> list(range(-10, 10, 4)) # three values: step is added 6 | [-10, -6, -2, 2, 6] 7 | -------------------------------------------------------------------------------- /Chapter03/ch3/simple.for.2.py: -------------------------------------------------------------------------------- 1 | surnames = ['Rivest', 'Shamir', 'Adleman'] 2 | for position in range(len(surnames)): 3 | print(position, surnames[position]) 4 | # print(surnames[position][0], end='') # try swapping prints 5 | -------------------------------------------------------------------------------- /Chapter03/ch3/simple.for.3.py: -------------------------------------------------------------------------------- 1 | surnames = ['Rivest', 'Shamir', 'Adleman'] 2 | for surname in surnames: 3 | print(surname) 4 | -------------------------------------------------------------------------------- /Chapter03/ch3/simple.for.4.py: -------------------------------------------------------------------------------- 1 | surnames = ['Rivest', 'Shamir', 'Adleman'] 2 | for position, surname in enumerate(surnames): 3 | print(position, surname) 4 | -------------------------------------------------------------------------------- /Chapter03/ch3/simple.for.py: -------------------------------------------------------------------------------- 1 | for number in [0, 1, 2, 3, 4]: 2 | print(number) 3 | 4 | 5 | # equivalent using range 6 | for number in range(5): 7 | print(number) 8 | -------------------------------------------------------------------------------- /Chapter03/ch3/switch.js: -------------------------------------------------------------------------------- 1 | switch (day_number) { 2 | case 1: 3 | case 2: 4 | case 3: 5 | case 4: 6 | case 5: 7 | day = "Weekday"; 8 | break; 9 | case 6: 10 | day = "Saturday"; 11 | break; 12 | case 0: 13 | day = "Sunday"; 14 | break; 15 | default: 16 | day = ""; 17 | alert(day_number + ' is not a valid day number.') 18 | } 19 | -------------------------------------------------------------------------------- /Chapter03/ch3/switch.py: -------------------------------------------------------------------------------- 1 | # this code is not supposed to be run 2 | if 1 <= day_number <= 5: 3 | day = 'Weekday' 4 | elif day_number == 6: 5 | day = 'Saturday' 6 | elif day_number == 0: 7 | day = 'Sunday' 8 | else: 9 | day = '' 10 | raise ValueError( 11 | str(day_number) + ' is not a valid day number.') 12 | -------------------------------------------------------------------------------- /Chapter03/ch3/taxes.py: -------------------------------------------------------------------------------- 1 | income = 15000 2 | if income < 10000: 3 | tax_coefficient = 0.0 #1 4 | elif income < 30000: 5 | tax_coefficient = 0.2 #2 6 | elif income < 100000: 7 | tax_coefficient = 0.35 #3 8 | else: 9 | tax_coefficient = 0.45 #4 10 | 11 | print('I will pay:', income * tax_coefficient, 'in taxes') 12 | -------------------------------------------------------------------------------- /Chapter03/ch3/ternary.py: -------------------------------------------------------------------------------- 1 | order_total = 247 # GBP 2 | 3 | # classic if/else form 4 | if order_total > 100: 5 | discount = 25 # GBP 6 | else: 7 | discount = 0 # GBP 8 | print(order_total, discount) 9 | 10 | # ternary operator 11 | discount = 25 if order_total > 100 else 0 12 | print(order_total, discount) 13 | -------------------------------------------------------------------------------- /Chapter04/ch4/additional.unpacking.py: -------------------------------------------------------------------------------- 1 | def additional(*args, **kwargs): 2 | print(args) 3 | print(kwargs) 4 | 5 | args1 = (1, 2, 3) 6 | args2 = [4, 5] 7 | kwargs1 = dict(option1=10, option2=20) 8 | kwargs2 = {'option3': 30} 9 | 10 | additional(*args1, *args2, **kwargs1, **kwargs2) 11 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.all.kwonly.py: -------------------------------------------------------------------------------- 1 | def func_with_kwonly(a, b=42, *args, c, d=256, **kwargs): 2 | print('a, b:', a, b) 3 | print('c, d:', c, d) 4 | print('args:', args) 5 | print('kwargs:', kwargs) 6 | 7 | # both calls equivalent 8 | func_with_kwonly(3, 42, c=0, d=1, *(7, 9, 11), e='E', f='F') 9 | func_with_kwonly(3, 42, *(7, 9, 11), c=0, d=1, e='E', f='F') 10 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.all.py: -------------------------------------------------------------------------------- 1 | def func(a, b, c=7, *args, **kwargs): 2 | print('a, b, c:', a, b, c) 3 | print('args:', args) 4 | print('kwargs:', kwargs) 5 | 6 | func(1, 2, 3, *(5, 7, 9), **{'A': 'a', 'B': 'b'}) 7 | func(1, 2, 3, 5, 7, 9, A='a', B='b') # same as previous one 8 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.default.error.py: -------------------------------------------------------------------------------- 1 | def func(a, b=4, c=88): 2 | print(a, b, c) 3 | 4 | func(b=1, c=2, 42) # positional argument after keyword one 5 | 6 | """ 7 | File "arguments.default.error.py", line 4 8 | func(b=1, c=2, 42) 9 | ^ 10 | SyntaxError: non-keyword arg after keyword arg 11 | """ 12 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.default.py: -------------------------------------------------------------------------------- 1 | def func(a, b=4, c=88): 2 | print(a, b, c) 3 | 4 | func(1) # prints: 1 4 88 5 | func(b=5, a=7, c=9) # prints: 7 5 9 6 | func(42, c=9) # prints: 42 4 9 7 | func(42, 43, 44) # prints: 42, 43, 44 8 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.defaults.mutable.intermediate.call.py: -------------------------------------------------------------------------------- 1 | def func(a=[], b={}): 2 | print(a) 3 | print(b) 4 | print('#' * 12) 5 | a.append(len(a)) # this will affect a's default value 6 | b[len(a)] = len(a) # and this will affect b's one 7 | 8 | func() 9 | func(a=[1, 2, 3], b={'B': 1}) 10 | func() 11 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.defaults.mutable.no.trap.py: -------------------------------------------------------------------------------- 1 | def func(a=None): 2 | if a is None: 3 | a = [] 4 | # do whatever you want with `a` ... 5 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.defaults.mutable.py: -------------------------------------------------------------------------------- 1 | def func(a=[], b={}): 2 | print(a) 3 | print(b) 4 | print('#' * 12) 5 | a.append(len(a)) # this will affect a's default value 6 | b[len(a)] = len(a) # and this will affect b's one 7 | 8 | func() 9 | func() 10 | func() 11 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.keyword.only.py: -------------------------------------------------------------------------------- 1 | def kwo(*a, c): 2 | print(a, c) 3 | 4 | kwo(1, 2, 3, c=7) # prints: (1, 2, 3) 7 5 | kwo(c=4) # prints: () 4 6 | # kwo(1, 2) # breaks, invalid syntax, with the following error 7 | # TypeError: kwo() missing 1 required keyword-only argument: 'c' 8 | 9 | def kwo2(a, b=42, *, c): 10 | print(a, b, c) 11 | 12 | kwo2(3, b=7, c=99) # prints: 3 7 99 13 | kwo2(3, c=13) # prints: 3 42 13 14 | # kwo2(3, 23) # breaks, invalid syntax, with the following error 15 | # TypeError: kwo2() missing 1 required keyword-only argument: 'c' 16 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.keyword.py: -------------------------------------------------------------------------------- 1 | def func(a, b, c): 2 | print(a, b, c) 3 | 4 | func(a=1, c=2, b=3) # prints: 1 3 2 5 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.positional.py: -------------------------------------------------------------------------------- 1 | def func(a, b, c): 2 | print(a, b, c) 3 | 4 | func(1, 2, 3) # prints: 1 2 3 5 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.variable.db.py: -------------------------------------------------------------------------------- 1 | def connect(**options): 2 | conn_params = { 3 | 'host': options.get('host', '127.0.0.1'), 4 | 'port': options.get('port', 5432), 5 | 'user': options.get('user', ''), 6 | 'pwd': options.get('pwd', ''), 7 | } 8 | print(conn_params) 9 | # we then connect to the db (commented out) 10 | # db.connect(**conn_params) 11 | 12 | connect() 13 | connect(host='127.0.0.42', port=5433) 14 | connect(port=5431, user='fab', pwd='gandalf') 15 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.variable.keyword.py: -------------------------------------------------------------------------------- 1 | def func(**kwargs): 2 | print(kwargs) 3 | 4 | # All calls equivalent. They print: {'a': 1, 'b': 42} 5 | func(a=1, b=42) 6 | func(**{'a': 1, 'b': 42}) 7 | func(**dict(a=1, b=42)) 8 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.variable.positional.py: -------------------------------------------------------------------------------- 1 | def minimum(*n): 2 | # print(type(n)) # n is a tuple 3 | if n: # explained after the code 4 | mn = n[0] 5 | for value in n[1:]: 6 | if value < mn: 7 | mn = value 8 | print(mn) 9 | 10 | minimum(1, 3, -7, 9) # n = (1, 3, -7, 9) - prints: -7 11 | minimum() # n = () - prints: nothing 12 | -------------------------------------------------------------------------------- /Chapter04/ch4/arguments.variable.positional.unpacking.py: -------------------------------------------------------------------------------- 1 | def func(*args): 2 | print(args) 3 | 4 | values = (1, 3, -7, 9) 5 | func(values) # equivalent to: func((1, 3, -7, 9)) 6 | func(*values) # equivalent to: func(1, 3, -7, 9) 7 | -------------------------------------------------------------------------------- /Chapter04/ch4/data.science.example.py: -------------------------------------------------------------------------------- 1 | def do_report(data_source): 2 | # fetch and prepare data 3 | data = fetch_data(data_source) 4 | parsed_data = parse_data(data) 5 | filtered_data = filter_data(parsed_data) 6 | polished_data = polish_data(filtered_data) 7 | 8 | # run algorithms on data 9 | final_data = analyse(polished_data) 10 | 11 | # create and return report 12 | report = Report(final_data) 13 | return report 14 | 15 | 16 | if __name__ == "__main__": 17 | 18 | print( 19 | "Please don't call the `do_report` function. " 20 | "It's just and example." 21 | ) 22 | -------------------------------------------------------------------------------- /Chapter04/ch4/docstrings.py: -------------------------------------------------------------------------------- 1 | def square(n): 2 | """Return the square of a number n. """ 3 | return n ** 2 4 | 5 | def get_username(userid): 6 | """Return the username of a user given their id. """ 7 | return db.get(user_id=userid).username 8 | 9 | 10 | def connect(host, port, user, password): 11 | """Connect to a database. 12 | 13 | Connect to a PostgreSQL database directly, using the given 14 | parameters. 15 | 16 | :param host: The host IP. 17 | :param port: The desired port. 18 | :param user: The connection username. 19 | :param password: The connection password. 20 | :return: The connection object. 21 | """ 22 | # body of the function here... 23 | return connection 24 | -------------------------------------------------------------------------------- /Chapter04/ch4/filter.lambda.py: -------------------------------------------------------------------------------- 1 | def get_multiples_of_five(n): 2 | return list(filter(lambda k: not k % 5, range(n))) 3 | 4 | 5 | print(get_multiples_of_five(50)) 6 | -------------------------------------------------------------------------------- /Chapter04/ch4/filter.regular.py: -------------------------------------------------------------------------------- 1 | def is_multiple_of_five(n): 2 | return not n % 5 3 | 4 | 5 | def get_multiples_of_five(n): 6 | return list(filter(is_multiple_of_five, range(n))) 7 | 8 | 9 | print(get_multiples_of_five(50)) 10 | -------------------------------------------------------------------------------- /Chapter04/ch4/func.attributes.py: -------------------------------------------------------------------------------- 1 | def multiplication(a, b=1): 2 | """Return a multiplied by b. """ 3 | return a * b 4 | 5 | 6 | if __name__ == "__main__": 7 | 8 | special_attributes = [ 9 | "__doc__", "__name__", "__qualname__", "__module__", 10 | "__defaults__", "__code__", "__globals__", "__dict__", 11 | "__closure__", "__annotations__", "__kwdefaults__", 12 | ] 13 | 14 | for attribute in special_attributes: 15 | print(attribute, '->', getattr(multiplication, attribute)) 16 | -------------------------------------------------------------------------------- /Chapter04/ch4/func_from.py: -------------------------------------------------------------------------------- 1 | from lib.funcdef import square, cube 2 | 3 | print(square(10)) 4 | print(cube(10)) 5 | -------------------------------------------------------------------------------- /Chapter04/ch4/func_import.py: -------------------------------------------------------------------------------- 1 | import lib.funcdef 2 | 3 | 4 | print(lib.funcdef.square(10)) 5 | print(lib.funcdef.cube(10)) 6 | -------------------------------------------------------------------------------- /Chapter04/ch4/key.points.argument.passing.py: -------------------------------------------------------------------------------- 1 | x = 3 2 | def func(y): 3 | print(y) 4 | 5 | func(x) # prints: 3 6 | -------------------------------------------------------------------------------- /Chapter04/ch4/key.points.assignment.py: -------------------------------------------------------------------------------- 1 | x = 3 2 | def func(x): 3 | x = 7 # defining a local x, not changing the global one 4 | 5 | func(x) 6 | print(x) # prints: 3 7 | -------------------------------------------------------------------------------- /Chapter04/ch4/key.points.mutable.assignment.py: -------------------------------------------------------------------------------- 1 | x = [1, 2, 3] 2 | def func(x): 3 | x[1] = 42 # this changes the caller! 4 | x = 'something else' # this points x to a new string object 5 | 6 | func(x) 7 | print(x) # still prints: [1, 42, 3] 8 | -------------------------------------------------------------------------------- /Chapter04/ch4/key.points.mutable.py: -------------------------------------------------------------------------------- 1 | x = [1, 2, 3] 2 | def func(x): 3 | x[1] = 42 # this affects the caller! 4 | 5 | func(x) 6 | print(x) # prints: [1, 42, 3] 7 | -------------------------------------------------------------------------------- /Chapter04/ch4/lambda.explained.py: -------------------------------------------------------------------------------- 1 | """ 2 | myfunc = lambda [parameter_list]: expression 3 | 4 | def myfunc([parameter_list]): 5 | return expression 6 | """ 7 | 8 | # example 1: adder 9 | def adder(a, b): 10 | return a + b 11 | 12 | # is equivalent to: 13 | adder_lambda = lambda a, b: a + b 14 | 15 | # example 2: to uppercase 16 | def to_upper(s): 17 | return s.upper() 18 | 19 | # is equivalent to: 20 | to_upper_lambda = lambda s: s.upper() 21 | 22 | 23 | if __name__ == "__main__": 24 | 25 | print(adder(3, 4)) 26 | print(adder_lambda(3, 4)) 27 | 28 | print(to_upper("Hello")) 29 | print(to_upper_lambda("Hello")) 30 | -------------------------------------------------------------------------------- /Chapter04/ch4/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter04/ch4/lib/__init__.py -------------------------------------------------------------------------------- /Chapter04/ch4/lib/funcdef.py: -------------------------------------------------------------------------------- 1 | def square(n): 2 | return n ** 2 3 | 4 | 5 | def cube(n): 6 | return n ** 3 7 | -------------------------------------------------------------------------------- /Chapter04/ch4/matrix.multiplication.func.py: -------------------------------------------------------------------------------- 1 | # this function could also be defined in another module 2 | def matrix_mul(a, b): 3 | return [[sum(i * j for i, j in zip(r, c)) for c in zip(*b)] 4 | for r in a] 5 | 6 | 7 | a = [[1, 2], [3, 4]] 8 | b = [[5, 1], [2, 1]] 9 | 10 | c = matrix_mul(a, b) 11 | print(c) 12 | -------------------------------------------------------------------------------- /Chapter04/ch4/matrix.multiplication.nofunc.py: -------------------------------------------------------------------------------- 1 | a = [[1, 2], [3, 4]] 2 | b = [[5, 1], [2, 1]] 3 | 4 | c = [[sum(i * j for i, j in zip(r, c)) for c in zip(*b)] 5 | for r in a] 6 | print(c) 7 | -------------------------------------------------------------------------------- /Chapter04/ch4/no.side.effects.py: -------------------------------------------------------------------------------- 1 | # This file is not meant to be run 2 | >>> numbers = [4, 1, 7, 5] 3 | >>> sorted(numbers) # won't sort the original `numbers` list 4 | [1, 4, 5, 7] 5 | >>> numbers # let's verify 6 | [4, 1, 7, 5] # good, untouched 7 | >>> numbers.sort() # this will act on the list 8 | >>> numbers 9 | [1, 4, 5, 7] 10 | -------------------------------------------------------------------------------- /Chapter04/ch4/primes.py: -------------------------------------------------------------------------------- 1 | from math import sqrt, ceil 2 | 3 | 4 | def get_primes(n): 5 | """Calculate a list of primes up to n (included). """ 6 | primelist = [] 7 | for candidate in range(2, n + 1): 8 | is_prime = True 9 | root = ceil(sqrt(candidate)) # division limit 10 | for prime in primelist: # we try only the primes 11 | if prime > root: # no need to check any further 12 | break 13 | if candidate % prime == 0: 14 | is_prime = False 15 | break 16 | if is_prime: 17 | primelist.append(candidate) 18 | return primelist 19 | 20 | 21 | if __name__ == "__main__": 22 | 23 | def test(): 24 | primes = get_primes(10**3) 25 | primes2 = [ 26 | 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 27 | 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 28 | 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 29 | 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 30 | 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 31 | 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 32 | 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 33 | 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 34 | 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 35 | 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 36 | 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 37 | 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 38 | 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 39 | 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 40 | 941, 947, 953, 967, 971, 977, 983, 991, 997 41 | ] 42 | return primes == primes2 43 | 44 | print(test()) 45 | 46 | print(get_primes(100)) 47 | -------------------------------------------------------------------------------- /Chapter04/ch4/recursive.factorial.py: -------------------------------------------------------------------------------- 1 | def factorial(n): 2 | if n in (0, 1): # base case 3 | return 1 4 | return factorial(n - 1) * n # recursive case 5 | 6 | 7 | print([factorial(n) for n in range(10)]) 8 | -------------------------------------------------------------------------------- /Chapter04/ch4/return.multiple.py: -------------------------------------------------------------------------------- 1 | def moddiv(a, b): 2 | return a // b, a % b 3 | 4 | 5 | def test(n=10**4): 6 | from random import choice, random, randint 7 | m = 10 ** 6 8 | for x in range(n): 9 | a = choice((randint(0, m), m * random())) 10 | b = 0 11 | while not b: 12 | b = choice((randint(1, m), m * random())) 13 | 14 | r = divmod(a, b) 15 | r2 = moddiv(a, b) 16 | if r != r2: 17 | print('Difference: ', a, b, r, r2) 18 | 19 | 20 | if __name__ == "__main__": 21 | 22 | test(10 ** 6) 23 | print('Done') 24 | 25 | print(moddiv(20, 7)) # prints (2, 6) 26 | -------------------------------------------------------------------------------- /Chapter04/ch4/return.none.py: -------------------------------------------------------------------------------- 1 | def func(): 2 | pass 3 | 4 | func() # the return of this call won't be collected. It's lost. 5 | a = func() # the return of this one instead is collected into `a` 6 | print(a) # prints: None 7 | -------------------------------------------------------------------------------- /Chapter04/ch4/return.single.value.2.py: -------------------------------------------------------------------------------- 1 | from functools import reduce 2 | from operator import mul 3 | 4 | 5 | def factorial(n): 6 | return reduce(mul, range(1, n + 1), 1) 7 | 8 | 9 | f5 = factorial(5) # f5 = 120 10 | print(f5) 11 | print([factorial(k) for k in range(10)]) 12 | -------------------------------------------------------------------------------- /Chapter04/ch4/return.single.value.py: -------------------------------------------------------------------------------- 1 | def factorial(n): 2 | if n in (0, 1): 3 | return 1 4 | result = n 5 | for k in range(2, n): 6 | result *= k 7 | return result 8 | 9 | f5 = factorial(5) # f5 = 120 10 | print(f5) 11 | -------------------------------------------------------------------------------- /Chapter04/ch4/scoping.level.1.py: -------------------------------------------------------------------------------- 1 | def my_function(): 2 | test = 1 # this is defined in the local scope of the function 3 | print('my_function:', test) 4 | 5 | 6 | test = 0 # this is defined in the global scope 7 | my_function() 8 | print('global:', test) 9 | -------------------------------------------------------------------------------- /Chapter04/ch4/scoping.level.2.global.py: -------------------------------------------------------------------------------- 1 | def outer(): 2 | test = 1 # outer scope 3 | 4 | def inner(): 5 | global test 6 | test = 2 # global scope 7 | print('inner:', test) 8 | 9 | inner() 10 | print('outer:', test) 11 | 12 | 13 | test = 0 # global scope 14 | outer() 15 | print('global:', test) 16 | -------------------------------------------------------------------------------- /Chapter04/ch4/scoping.level.2.nonlocal.py: -------------------------------------------------------------------------------- 1 | def outer(): 2 | test = 1 # outer scope 3 | 4 | def inner(): 5 | nonlocal test 6 | test = 2 # nearest enclosing scope (which is 'outer') 7 | print('inner:', test) 8 | 9 | inner() 10 | print('outer:', test) 11 | 12 | 13 | test = 0 # global scope 14 | outer() 15 | print('global:', test) 16 | -------------------------------------------------------------------------------- /Chapter04/ch4/scoping.level.2.py: -------------------------------------------------------------------------------- 1 | def outer(): 2 | test = 1 # outer scope 3 | 4 | def inner(): 5 | test = 2 # inner scope 6 | print('inner:', test) 7 | 8 | inner() 9 | print('outer:', test) 10 | 11 | 12 | test = 0 # global scope 13 | outer() 14 | print('global:', test) 15 | -------------------------------------------------------------------------------- /Chapter04/ch4/vat.function.py: -------------------------------------------------------------------------------- 1 | def calculate_price_with_vat(price, vat): 2 | return price * (100 + vat) / 100 3 | 4 | 5 | if __name__ == "__main__": 6 | print(calculate_price_with_vat(100, 20)) 7 | -------------------------------------------------------------------------------- /Chapter04/ch4/vat.py: -------------------------------------------------------------------------------- 1 | price = 100 # GBP, no VAT 2 | final_price1 = price * 1.2 3 | final_price2 = price + price / 5.0 4 | final_price3 = price * (100 + 20) / 100.0 5 | final_price4 = price + price * 0.2 6 | 7 | 8 | if __name__ == "__main__": 9 | for var, value in sorted(vars().items()): 10 | if 'price' in var: 11 | print(var, value) 12 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/buffer.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | buff = bytearray(os.path.getsize('fear.txt')) 5 | 6 | 7 | with open('fear.txt', 'rb') as f: 8 | f.readinto(buff) 9 | 10 | half = len(buff) // 2 11 | buff[:half] = buff[:half].upper() 12 | buff[half:] = buff[half:].lower() 13 | 14 | with open('fear_mod.txt', 'wb') as fw: 15 | fw.write(buff) 16 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/compression/content1.txt: -------------------------------------------------------------------------------- 1 | This is content1.txt -------------------------------------------------------------------------------- /Chapter05/ch5/files/compression/content2.txt: -------------------------------------------------------------------------------- 1 | This is content2.txt -------------------------------------------------------------------------------- /Chapter05/ch5/files/compression/subfolder/content3.txt: -------------------------------------------------------------------------------- 1 | This is subfolder/content3.txt -------------------------------------------------------------------------------- /Chapter05/ch5/files/compression/subfolder/content4.txt: -------------------------------------------------------------------------------- 1 | This is subfolder/content4.txt -------------------------------------------------------------------------------- /Chapter05/ch5/files/compression/tar.py: -------------------------------------------------------------------------------- 1 | import tarfile 2 | 3 | with tarfile.open('example.tar.gz', 'w:gz') as tar: 4 | tar.add('content1.txt') 5 | tar.add('content2.txt') 6 | tar.add('subfolder/content3.txt') 7 | tar.add('subfolder/content4.txt') 8 | 9 | with tarfile.open('example.tar.gz', 'r:gz') as tar: 10 | tar.extractall('extract_tar') 11 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/compression/zip.py: -------------------------------------------------------------------------------- 1 | from zipfile import ZipFile 2 | 3 | 4 | with ZipFile('example.zip', 'w') as zp: 5 | zp.write('content1.txt') 6 | zp.write('content2.txt') 7 | zp.write('subfolder/content3.txt') 8 | zp.write('subfolder/content4.txt') 9 | 10 | 11 | with ZipFile('example.zip') as zp: 12 | zp.extract('content1.txt', 'extract_zip') 13 | zp.extract('subfolder/content3.txt', 'extract_zip') 14 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/existence.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | filename = 'fear.txt' 5 | path = os.path.dirname(os.path.abspath(filename)) 6 | 7 | 8 | print(os.path.isfile(filename)) # True 9 | print(os.path.isdir(path)) # True 10 | print(path) # /Users/fab/srv/lpp/ch7/files 11 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/fear.txt: -------------------------------------------------------------------------------- 1 | An excerpt from Fear - By Thich Nhat Hanh 2 | 3 | The Present Is Free from Fear 4 | 5 | When we are not fully present, we are not really living. We’re not really there, either for our loved ones or for ourselves. If we’re not there, then where are we? We are running, running, running, even during our sleep. We run because we’re trying to escape from our fear. 6 | 7 | We cannot enjoy life if we spend our time and energy worrying about what happened yesterday and what will happen tomorrow. If we’re afraid all the time, we miss out on the wonderful fact that we’re alive and can be happy right now. In everyday life, we tend to believe that happiness is only possible in the future. We’re always looking for the “right” conditions that we don’t yet have to make us happy. We ignore what is happening right in front of us. We look for something that will make us feel more solid, more safe, more secure. But we’re afraid all the time of what the future will bring—afraid we’ll lose our jobs, our possessions, the people around us whom we love. So we wait and hope for that magical moment—always sometime in the future—when everything will be as we want it to be. We forget that life is available only in the present moment. The Buddha said, “It is possible to live happily in the present moment. It is the only moment we have.” 8 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/fixed_amount.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | from string import ascii_lowercase 3 | 4 | 5 | size = 60 # try different sizes 6 | 7 | 8 | with open('fear.txt') as stream: 9 | while True: 10 | data = stream.read(size) 11 | if data.strip(): 12 | print('===>', data, '<===') 13 | else: 14 | break 15 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/listing.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | with os.scandir('.') as it: 5 | for entry in it: 6 | print( 7 | entry.name, entry.path, 8 | 'File' if entry.is_file() else 'Folder' 9 | ) 10 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/manipulation.py: -------------------------------------------------------------------------------- 1 | from collections import Counter 2 | from string import ascii_letters 3 | 4 | 5 | chars = ascii_letters + ' ' 6 | 7 | 8 | def sanitize(s, chars): 9 | return ''.join(c for c in s if c in chars) 10 | 11 | 12 | def reverse(s): 13 | return s[::-1] 14 | 15 | 16 | with open('fear.txt') as stream: 17 | lines = [line.rstrip() for line in stream] 18 | 19 | 20 | # let's write the mirrored version of the file 21 | with open('raef.txt', 'w') as stream: 22 | stream.write('\n'.join(reverse(line) for line in lines)) 23 | 24 | 25 | # now we can calculate some statistics 26 | lines = [sanitize(line, chars) for line in lines] 27 | whole = ' '.join(lines) 28 | 29 | 30 | # we perform comparisons on the lowercased version of `whole` 31 | cnt = Counter(whole.lower().split()) 32 | 33 | # we can print the N most common words 34 | print(cnt.most_common(3)) 35 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/open_try.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | fh = open('fear.txt', 'rt') # r: read, t: text 4 | 5 | for line in fh.readlines(): 6 | print(line.strip()) # remove whitespace and print 7 | 8 | fh.close() 9 | 10 | 11 | # secured by try/finally 12 | try: 13 | fh = open('fear.txt', 'rt') 14 | 15 | for line in fh.readlines(): 16 | print(line.strip()) 17 | 18 | finally: 19 | fh.close() 20 | 21 | 22 | # equivalent to: 23 | try: 24 | fh = open('fear.txt') # rt is default 25 | 26 | for line in fh: # we can iterate directly on fh 27 | print(line.strip()) 28 | 29 | finally: 30 | fh.close() 31 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/open_with.py: -------------------------------------------------------------------------------- 1 | 2 | with open('fear.txt') as fh: 3 | for line in fh: 4 | print(line.strip()) 5 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/ops_create.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import os 3 | 4 | 5 | BASE_PATH = 'ops_example' # this will be our base path 6 | 7 | # let's perform an initial cleanup just in case 8 | if os.path.exists(BASE_PATH) and os.path.isdir(BASE_PATH): 9 | shutil.rmtree(BASE_PATH) 10 | 11 | 12 | os.mkdir(BASE_PATH) 13 | 14 | path_b = os.path.join(BASE_PATH, 'A', 'B') 15 | path_c = os.path.join(BASE_PATH, 'A', 'C') 16 | path_d = os.path.join(BASE_PATH, 'A', 'D') 17 | 18 | 19 | os.makedirs(path_b) 20 | os.makedirs(path_c) 21 | 22 | 23 | # we add three files in `ops_example/A/B` 24 | for filename in ('ex1.txt', 'ex2.txt', 'ex3.txt'): 25 | with open(os.path.join(path_b, filename), 'w') as stream: 26 | stream.write(f'Some content here in {filename}\n') 27 | 28 | 29 | shutil.move(path_b, path_d) 30 | 31 | 32 | # we can also rename files 33 | shutil.move( 34 | os.path.join(path_d, 'ex1.txt'), 35 | os.path.join(path_d, 'ex1d.txt') 36 | ) 37 | 38 | # now call $ tree ops_example 39 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/paths.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | filename = 'fear.txt' 5 | path = os.path.abspath(filename) 6 | 7 | 8 | print(path) 9 | 10 | print(os.path.basename(path)) 11 | print(os.path.dirname(path)) 12 | print(os.path.splitext(path)) 13 | print(os.path.split(path)) 14 | 15 | readme_path = os.path.join( 16 | os.path.dirname(path), '..', '..', 'README.rst') 17 | 18 | print(readme_path) 19 | print(os.path.normpath(readme_path)) 20 | 21 | 22 | 23 | """ 24 | /Users/fab/srv/lpp/ch7/files/fear.txt # path 25 | fear.txt # basename 26 | /Users/fab/srv/lpp/ch7/files # dirname 27 | ('/Users/fab/srv/lpp/ch7/files/fear', '.txt') # splitext 28 | ('/Users/fab/srv/lpp/ch7/files', 'fear.txt') # split 29 | /Users/fab/srv/lpp/ch7/files/../../README.rst # readme_path 30 | /Users/fab/srv/lpp/README.rst # normalized 31 | """ 32 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/print_file.py: -------------------------------------------------------------------------------- 1 | 2 | with open('print_example.txt', 'w') as fw: 3 | print('Hey I am printing into a file!!!', file=fw) 4 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/read_write.py: -------------------------------------------------------------------------------- 1 | 2 | with open('fear.txt') as f: 3 | lines = [line.rstrip() for line in f] 4 | 5 | 6 | with open('fear_copy.txt', 'w') as fw: # w - write 7 | fw.write('\n'.join(lines)) 8 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/read_write_bin.py: -------------------------------------------------------------------------------- 1 | 2 | with open('example.bin', 'wb') as fw: 3 | fw.write(b'This is binary data...') 4 | 5 | 6 | with open('example.bin', 'rb') as f: 7 | print(f.read()) # prints: b'This is binary data...' 8 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/tmp.py: -------------------------------------------------------------------------------- 1 | import os 2 | from tempfile import NamedTemporaryFile, TemporaryDirectory 3 | 4 | 5 | with TemporaryDirectory(dir='.') as td: 6 | print('Temp directory:', td) 7 | with NamedTemporaryFile(dir=td) as t: 8 | name = t.name 9 | print(os.path.abspath(name)) 10 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/walking.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | for root, dirs, files in os.walk('.'): 5 | abs_root = os.path.abspath(root) 6 | print(abs_root) 7 | 8 | if dirs: 9 | print('Directories:') 10 | for dir_ in dirs: 11 | print(dir_) 12 | print() 13 | 14 | if files: 15 | print('Files:') 16 | for filename in files: 17 | print(filename) 18 | print() 19 | -------------------------------------------------------------------------------- /Chapter05/ch5/files/write_not_exists.py: -------------------------------------------------------------------------------- 1 | 2 | with open('write_x.txt', 'x') as fw: 3 | fw.write('Writing line 1') # this succeeds 4 | 5 | 6 | with open('write_x.txt', 'x') as fw: 7 | fw.write('Writing line 2') # this fails 8 | -------------------------------------------------------------------------------- /Chapter05/ch5/io_examples/reqs.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | urls = { 5 | 'get': 'https://httpbin.org/get?title=learn+python+programming', 6 | 'headers': 'https://httpbin.org/headers', 7 | 'ip': 'https://httpbin.org/ip', 8 | 'now': 'https://now.httpbin.org/', 9 | 'user-agent': 'https://httpbin.org/user-agent', 10 | 'UUID': 'https://httpbin.org/uuid', 11 | } 12 | 13 | 14 | def get_content(title, url): 15 | resp = requests.get(url) 16 | print(f'Response for {title}') 17 | print(resp.json()) 18 | 19 | 20 | for title, url in urls.items(): 21 | get_content(title, url) 22 | print('-' * 40) 23 | -------------------------------------------------------------------------------- /Chapter05/ch5/io_examples/reqs_post.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | 4 | url = 'https://httpbin.org/post' 5 | data = dict(title='Learn Python Programming') 6 | 7 | 8 | resp = requests.post(url, data=data) 9 | print('Response for POST') 10 | print(resp.json()) 11 | -------------------------------------------------------------------------------- /Chapter05/ch5/io_examples/string_io.py: -------------------------------------------------------------------------------- 1 | import io 2 | 3 | 4 | stream = io.StringIO() 5 | stream.write('Learning Python Programming.\n') 6 | print('Become a Python ninja!', file=stream) 7 | 8 | contents = stream.getvalue() 9 | print(contents) 10 | 11 | stream.close() 12 | 13 | 14 | # better alternative, using a context manager 15 | with io.StringIO() as stream: 16 | stream.write('Learning Python Programming.\n') 17 | print('Become a Python ninja!', file=stream) 18 | 19 | contents = stream.getvalue() 20 | print(contents) 21 | -------------------------------------------------------------------------------- /Chapter05/ch5/json_examples/json_basic.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import json 3 | 4 | 5 | data = { 6 | 'big_number': 2 ** 3141, 7 | 'max_float': sys.float_info.max, 8 | 'a_list': [2, 3, 5, 7], 9 | } 10 | 11 | 12 | json_data = json.dumps(data) 13 | data_out = json.loads(json_data) 14 | 15 | assert data == data_out # json and back, data matches 16 | 17 | 18 | # let's see how passing indent affects dumps. 19 | 20 | info = { 21 | 'full_name': 'Sherlock Holmes', 22 | 'address': { 23 | 'street': '221B Baker St', 24 | 'zip': 'NW1 6XE', 25 | 'city': 'London', 26 | 'country': 'UK', 27 | } 28 | } 29 | 30 | 31 | print(json.dumps(info, indent=2, sort_keys=True)) 32 | -------------------------------------------------------------------------------- /Chapter05/ch5/json_examples/json_cplx.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class ComplexEncoder(json.JSONEncoder): 5 | def default(self, obj): 6 | if isinstance(obj, complex): 7 | return { 8 | '_meta': '_complex', 9 | 'num': [obj.real, obj.imag], 10 | } 11 | return json.JSONEncoder.default(self, obj) 12 | 13 | 14 | data = { 15 | 'an_int': 42, 16 | 'a_float': 3.14159265, 17 | 'a_complex': 3 + 4j, 18 | } 19 | 20 | json_data = json.dumps(data, cls=ComplexEncoder) 21 | 22 | print(json_data) 23 | 24 | 25 | def object_hook(obj): 26 | try: 27 | if obj['_meta'] == '_complex': 28 | return complex(*obj['num']) 29 | except (KeyError, TypeError): 30 | return obj 31 | 32 | 33 | data_out = json.loads(json_data, object_hook=object_hook) 34 | print(data_out) 35 | -------------------------------------------------------------------------------- /Chapter05/ch5/json_examples/json_datetime.py: -------------------------------------------------------------------------------- 1 | # exercise: do the same for date 2 | import json 3 | from datetime import datetime, timedelta, timezone 4 | 5 | 6 | now = datetime.now() 7 | now_tz = datetime.now(tz=timezone(timedelta(hours=1))) 8 | 9 | 10 | class DatetimeEncoder(json.JSONEncoder): 11 | def default(self, obj): 12 | if isinstance(obj, datetime): 13 | try: 14 | off = obj.utcoffset().seconds 15 | except AttributeError: 16 | off = None 17 | 18 | return { 19 | '_meta': '_datetime', 20 | 'data': obj.timetuple()[:6] + (obj.microsecond, ), 21 | 'utcoffset': off, 22 | } 23 | return json.JSONEncoder.default(self, obj) 24 | 25 | 26 | data = { 27 | 'an_int': 42, 28 | 'a_float': 3.14159265, 29 | 'a_datetime': now, 30 | 'a_datetime_tz': now_tz, 31 | } 32 | 33 | json_data = json.dumps(data, cls=DatetimeEncoder) 34 | 35 | print(json_data) 36 | 37 | 38 | def object_hook(obj): 39 | try: 40 | if obj['_meta'] == '_datetime': 41 | if obj['utcoffset'] is None: 42 | tz = None 43 | else: 44 | tz = timezone(timedelta(seconds=obj['utcoffset'])) 45 | return datetime(*obj['data'], tzinfo=tz) 46 | except (KeyError, TypeError): 47 | return obj 48 | 49 | 50 | data_out = json.loads(json_data, object_hook=object_hook) 51 | from pprint import pprint 52 | pprint(data_out, indent=2) 53 | print(data_out) 54 | 55 | assert data_out['a_datetime'] == data['a_datetime'] 56 | assert data_out['a_datetime_tz'] == data['a_datetime_tz'] 57 | -------------------------------------------------------------------------------- /Chapter05/ch5/json_examples/json_tuple.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | data_in = { 5 | 'a_tuple': (1, 2, 3, 4, 5), 6 | } 7 | 8 | 9 | json_data = json.dumps(data_in) 10 | print(json_data) # {"a_tuple": [1, 2, 3, 4, 5]} 11 | data_out = json.loads(json_data) 12 | print(data_out) # {'a_tuple': [1, 2, 3, 4, 5]} 13 | -------------------------------------------------------------------------------- /Chapter05/ch5/persistence/alchemy.py: -------------------------------------------------------------------------------- 1 | from alchemy_models import Person, Address, engine 2 | from sqlalchemy.orm import sessionmaker 3 | 4 | 5 | Session = sessionmaker(bind=engine) 6 | session = Session() 7 | 8 | 9 | # Create a couple of people 10 | anakin = Person(name='Anakin Skywalker', age=32) 11 | obi1 = Person(name='Obi-Wan Kenobi', age=40) 12 | 13 | # Add email addresses for both of them 14 | obi1.addresses = [ 15 | Address(email='obi1@example.com'), 16 | Address(email='wanwan@example.com'), 17 | ] 18 | 19 | # another way: we can simply append 20 | anakin.addresses.append(Address(email='ani@example.com')) 21 | anakin.addresses.append(Address(email='evil.dart@example.com')) 22 | anakin.addresses.append(Address(email='vader@example.com')) 23 | 24 | # Add people to the session. This adds addresses too. 25 | session.add(anakin) 26 | session.add(obi1) 27 | session.commit() 28 | 29 | 30 | # Query and display both 31 | obi1 = session.query(Person).filter( 32 | Person.name.like('Obi%') 33 | ).first() 34 | print(obi1, obi1.addresses) 35 | 36 | anakin = session.query(Person).filter( 37 | Person.name=='Anakin Skywalker' 38 | ).first() 39 | print(anakin, anakin.addresses) 40 | 41 | # capture anakin.id 42 | anakin_id = anakin.id 43 | 44 | # then remove the var 45 | del anakin 46 | 47 | 48 | def display_info(): 49 | # get all addresses first 50 | addresses = session.query(Address).all() 51 | 52 | # display results 53 | for address in addresses: 54 | print(f'{address.person.name} <{address.email}>') 55 | 56 | # display how many objects we have in total 57 | print('people: {}, addresses: {}'.format( 58 | session.query(Person).count(), 59 | session.query(Address).count()) 60 | ) 61 | 62 | 63 | display_info() 64 | 65 | # Fetch anakin directly by its id 66 | anakin = session.query(Person).get(anakin_id) 67 | 68 | # Delete anakin 69 | session.delete(anakin) 70 | session.commit() 71 | 72 | 73 | # let's do it again and see the changes 74 | display_info() 75 | -------------------------------------------------------------------------------- /Chapter05/ch5/persistence/alchemy_models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | from sqlalchemy import ( 3 | Column, Integer, String, ForeignKey, create_engine) 4 | from sqlalchemy.orm import relationship 5 | 6 | 7 | # swap these lines to work with an actual DB file 8 | # engine = create_engine('sqlite:///example.db') 9 | engine = create_engine('sqlite:///:memory:') 10 | 11 | 12 | Base = declarative_base() 13 | 14 | 15 | class Person(Base): 16 | __tablename__ = 'person' 17 | 18 | id = Column(Integer, primary_key=True) 19 | name = Column(String) 20 | age = Column(Integer) 21 | 22 | addresses = relationship( 23 | 'Address', 24 | back_populates='person', 25 | order_by='Address.email', 26 | cascade='all, delete-orphan' 27 | ) 28 | 29 | def __repr__(self): 30 | return f'{self.name}(id={self.id})' 31 | 32 | 33 | class Address(Base): 34 | __tablename__ = 'address' 35 | 36 | id = Column(Integer, primary_key=True) 37 | email = Column(String) 38 | person_id = Column(ForeignKey('person.id')) 39 | person = relationship('Person', back_populates='addresses') 40 | 41 | def __str__(self): 42 | return self.email 43 | __repr__ = __str__ 44 | 45 | 46 | Base.metadata.create_all(engine) 47 | -------------------------------------------------------------------------------- /Chapter05/ch5/persistence/pickler.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | from dataclasses import dataclass 3 | 4 | 5 | @dataclass 6 | class Person: 7 | first_name: str 8 | last_name: str 9 | id: int 10 | 11 | def greet(self): 12 | print(f'Hi, I am {self.first_name} {self.last_name}' 13 | f' and my ID is {self.id}' 14 | ) 15 | 16 | 17 | people = [ 18 | Person('Obi-Wan', 'Kenobi', 123), 19 | Person('Anakin', 'Skywalker', 456), 20 | ] 21 | 22 | 23 | # save data in binary format to a file 24 | with open('data.pickle', 'wb') as stream: 25 | pickle.dump(people, stream) 26 | 27 | 28 | # load data from a file 29 | with open('data.pickle', 'rb') as stream: 30 | peeps = pickle.load(stream) 31 | 32 | 33 | for person in peeps: 34 | person.greet() 35 | -------------------------------------------------------------------------------- /Chapter05/ch5/persistence/shelf.py: -------------------------------------------------------------------------------- 1 | import shelve 2 | 3 | 4 | class Person: 5 | def __init__(self, name, id): 6 | self.name = name 7 | self.id = id 8 | 9 | 10 | with shelve.open('shelf1.shelve') as db: 11 | db['obi1'] = Person('Obi-Wan', 123) 12 | db['ani'] = Person('Anakin', 456) 13 | db['a_list'] = [2, 3, 5] 14 | db['delete_me'] = 'we will have to delete this one...' 15 | 16 | print(list(db.keys())) # ['ani', 'a_list', 'delete_me', 'obi1'] 17 | 18 | del db['delete_me'] # gone! 19 | 20 | print(list(db.keys())) # ['ani', 'a_list', 'obi1'] 21 | 22 | print('delete_me' in db) # False 23 | print('ani' in db) # True 24 | 25 | a_list = db['a_list'] 26 | a_list.append(7) 27 | db['a_list'] = a_list 28 | 29 | print(db['a_list']) # [2, 3, 5, 7] 30 | 31 | 32 | # this way allows writeback: 33 | # working with lists is easier, but consumes more memory and 34 | # closing the file takes longer. 35 | with shelve.open('shelf2.shelve', writeback=True) as db: 36 | db['a_list'] = [11, 13, 17] 37 | db['a_list'].append(19) # in-place append! 38 | 39 | print(db['a_list']) # [11, 13, 17, 19] 40 | -------------------------------------------------------------------------------- /Chapter07/circularly_linked_list.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | def __init__(self, data=None): 3 | self.data = data 4 | self.next = None 5 | 6 | class CircularList: 7 | def __init__(self, data=None): 8 | self.head = None 9 | self.tail = None 10 | self.size = 0 11 | 12 | def clear(self): 13 | self.tail = None 14 | self.head = None 15 | 16 | def append(self, data): 17 | node = Node(data) 18 | if self.head: 19 | self.head.next = node 20 | self.head = node 21 | else: 22 | self.head = node 23 | self.tail = node 24 | self.head.next = self.tail 25 | self.size += 1 26 | 27 | def delete(self, data): 28 | current = self.tail 29 | prev = self.tail 30 | while prev == current or prev != self.head: 31 | if current.data == data: 32 | if current == self.tail: 33 | self.tail = current.next 34 | self.head.next = self.tail 35 | else: 36 | prev.next = current.next 37 | self.size -= 1 38 | return 39 | prev = current 40 | current = current.next 41 | 42 | def iter(self): 43 | current = self.tail 44 | while current: 45 | val = current.data 46 | current = current.next 47 | yield val 48 | 49 | words = CircularList() 50 | words.append('eggs') 51 | words.append('ham') 52 | words.append('spam') 53 | 54 | counter = 0 55 | for word in words.iter(): 56 | print(word) 57 | counter += 1 58 | if counter > 1000: 59 | break 60 | 61 | import sys 62 | sys.exit() 63 | 64 | l.append('foo') 65 | l.append('bar') 66 | l.append('bim') 67 | l.append('baz') 68 | l.append('quux') 69 | l.append('duux') 70 | 71 | counter = 0 72 | for item in l.iter(): 73 | print(item) 74 | counter += 1 75 | if counter > 1000: 76 | break 77 | 78 | print("Done iterating. Now we try to delete something that isn't there.") 79 | l.delete('socks') 80 | print('back to iterating') 81 | counter = 0 82 | for item in l.iter(): 83 | print(item) 84 | counter += 1 85 | if counter > 1000: 86 | break 87 | 88 | print('Let us delete something that is there.') 89 | l.delete('foo') 90 | print('back to iterating') 91 | counter = 0 92 | for item in l.iter(): 93 | print(item) 94 | counter += 1 95 | if counter > 1000: 96 | break 97 | -------------------------------------------------------------------------------- /Chapter07/singly_linked_list.py: -------------------------------------------------------------------------------- 1 | class Node: 2 | """ A singly-linked node. """ 3 | def __init__(self, data=None): 4 | self.data = data 5 | self.next = None 6 | 7 | class SinglyLinkedList: 8 | """ A singly-linked list. """ 9 | def __init__(self): 10 | """ Create an empty list. """ 11 | self.tail = None 12 | self.head = None 13 | self.count = 0 14 | 15 | def iter(self): 16 | """ Iterate through the list. """ 17 | current = self.tail 18 | while current: 19 | val = current.data 20 | current = current.next 21 | yield val 22 | 23 | def append(self, data): 24 | """ Append an item to the list """ 25 | node = Node(data) 26 | if self.head: 27 | self.head.next = node 28 | self.head = node 29 | else: 30 | self.tail = node 31 | self.head = node 32 | self.count += 1 33 | 34 | def delete(self, data): 35 | """ Delete a node from the list """ 36 | current = self.tail 37 | prev = self.tail 38 | while current: 39 | if current.data == data: 40 | if current == self.tail: 41 | self.tail = current.next 42 | else: 43 | prev.next = current.next 44 | self.count -= 1 45 | return 46 | prev = current 47 | current = current.next 48 | 49 | def search(self, data): 50 | """ Search through the list. Return True if data is found, otherwise 51 | False. """ 52 | for node in self.iter(): 53 | if data == node: 54 | return True 55 | return False 56 | 57 | def __getitem__(self, index): 58 | if index > self.count - 1: 59 | raise Exception("Index out of range.") 60 | current = self.tail 61 | for n in range(index): 62 | current = current.next 63 | return current.data 64 | 65 | def __setitem__(self, index, value): 66 | if index > self.count - 1: 67 | raise Exception("Index out of range.") 68 | current = self.tail 69 | for n in range(index): 70 | current = current.next 71 | current.data = value 72 | 73 | words = SinglyLinkedList() 74 | words.append('foo') 75 | words.append('bar') 76 | words.append('bim') 77 | words.append('baz') 78 | words.append('quux') 79 | 80 | print("access by index") 81 | print("here is a node: {}".format(words[1])) 82 | 83 | print("modify by index") 84 | words[4] = "Quux" 85 | print("Modified node by index: {}".format(words[4])) 86 | 87 | print("This list has {} elements.".format(words.count)) 88 | for word in words.iter(): 89 | print("Got this data: {}".format(word)) 90 | 91 | if words.search('foo'): 92 | print("Found foo in the list.") 93 | if words.search('amiga'): 94 | print("Found amiga in the list.") 95 | 96 | print("Now we try to delete an item") 97 | words.delete('bim') 98 | print("List now has {} elements".format(words.count)) 99 | for word in words.iter(): 100 | print("data: {}".format(word)) 101 | 102 | print("delete the first item in the list") 103 | words.delete('foo') 104 | print("size: {}".format(words.count)) 105 | for word in words.iter(): 106 | print("data: {}".format(word)) 107 | 108 | print("delete the last item in the list") 109 | words.delete('quux') 110 | print("size: {}".format(words.count)) 111 | for word in words.iter(): 112 | print("data: {}".format(word)) 113 | -------------------------------------------------------------------------------- /Chapter08/queue.py: -------------------------------------------------------------------------------- 1 | 2 | class ListQueue: 3 | 4 | def __init__(self): 5 | self.items = [] 6 | self.size = 0 7 | 8 | def empty(self): 9 | return self.items == [] 10 | 11 | def enqueue(self, data): 12 | self.items.insert(0, data) 13 | self.size += 1 14 | 15 | def dequeue(self): 16 | data = self.items.pop() 17 | self.size -= 1 18 | return data 19 | 20 | def size(self): 21 | return self.size 22 | 23 | 24 | 25 | list_queue = ListQueue() 26 | 27 | import time 28 | start_time = time.time() 29 | for i in range(100000): 30 | #print(i) 31 | list_queue.enqueue(i) 32 | for i in range(100000): 33 | list_queue.dequeue() 34 | print("--- %s seconds ---" % (time.time() - start_time)) 35 | 36 | 37 | """ 38 | import time 39 | start_time = time.time() 40 | for i in range(1000): 41 | for j in range(100): 42 | array_queue.enqueue(i) 43 | for k in range(10): 44 | array_queue.dequeue() 45 | print("--- %s seconds ---" % (time.time() - start_time)) 46 | """ 47 | -------------------------------------------------------------------------------- /Chapter08/queue_application.py: -------------------------------------------------------------------------------- 1 | from random import randint 2 | 3 | 4 | 5 | class Node(object): 6 | """ A Doubly-linked lists' node. """ 7 | def __init__(self, data=None, next=None, prev=None): 8 | self.data = data 9 | self.next = next 10 | self.prev = prev 11 | 12 | 13 | class Queue(object): 14 | """ A doubly-linked list. """ 15 | def __init__(self): 16 | self.head = None 17 | self.tail = None 18 | self.count = 0 19 | 20 | def enqueue(self, data): 21 | """ Append an item to the list. """ 22 | 23 | new_node = Node(data, None, None) 24 | if self.head is None: 25 | self.head = new_node 26 | self.tail = self.head 27 | else: 28 | new_node.prev = self.tail 29 | self.tail.next = new_node 30 | self.tail = new_node 31 | 32 | self.count += 1 33 | 34 | def dequeue(self): 35 | """ Remove elements from the front of the list""" 36 | current = self.head 37 | if self.count == 1: 38 | self.count -= 1 39 | self.head = None 40 | self.tail = None 41 | elif self.count > 1: 42 | self.head = self.head.next 43 | self.head.prev = None 44 | self.count -= 1 45 | return current 46 | 47 | 48 | 49 | queue = Queue() 50 | 51 | import time 52 | start_time = time.time() 53 | for i in range(100000): 54 | queue.enqueue(i) 55 | for i in range(100000): 56 | queue.dequeue() 57 | print("--- %s seconds ---" % (time.time() - start_time)) 58 | 59 | 60 | class Track: 61 | 62 | def __init__(self, title=None): 63 | self.title = title 64 | self.length = randint(5, 10) 65 | 66 | 67 | 68 | track1 = Track("white whistle") 69 | track2 = Track("butter butter") 70 | print(track1.length) 71 | print(track2.length) 72 | 73 | import time 74 | class MediaPlayerQueue(Queue): 75 | 76 | def __init__(self): 77 | super(MediaPlayerQueue, self).__init__() 78 | 79 | def add_track(self, track): 80 | self.enqueue(track) 81 | 82 | def play(self): 83 | while self.count > 0: 84 | current_track_node = self.dequeue() 85 | print("Now playing {}".format(current_track_node.data.title)) 86 | time.sleep(current_track_node.data.length) 87 | 88 | track1 = Track("white whistle") 89 | track2 = Track("butter butter") 90 | track3 = Track("Oh black star") 91 | track4 = Track("Watch that chicken") 92 | track5 = Track("Don't go") 93 | media_player = MediaPlayerQueue() 94 | media_player.add_track(track1) 95 | media_player.add_track(track2) 96 | media_player.add_track(track3) 97 | media_player.add_track(track4) 98 | media_player.add_track(track5) 99 | media_player.play() 100 | 101 | -------------------------------------------------------------------------------- /Chapter08/stack_queue_1.py: -------------------------------------------------------------------------------- 1 | class Queue: 2 | def __init__(self): 3 | self.inbound_stack = [] 4 | self.outbound_stack = [] 5 | 6 | def dequeue(self): 7 | if not self.outbound_stack: 8 | while self.inbound_stack: 9 | self.outbound_stack.append(self.inbound_stack.pop()) 10 | return self.outbound_stack.pop() 11 | 12 | def enqueue(self, data): 13 | self.inbound_stack.append(data) 14 | 15 | 16 | queue = Queue() 17 | queue.enqueue(5) 18 | queue.enqueue(6) 19 | queue.enqueue(7) 20 | print(queue.inbound_stack) 21 | queue.dequeue() 22 | print(queue.inbound_stack) 23 | print(queue.outbound_stack) 24 | queue.dequeue() 25 | print(queue.outbound_stack) 26 | 27 | """ 28 | import time 29 | start_time = time.time() 30 | for i in range(100000): 31 | #print i 32 | array_queue.enqueue(i) 33 | for i in range(100000): 34 | #print i 35 | array_queue.dequeue() 36 | print("--- %s seconds ---" % (time.time() - start_time)) 37 | 38 | 39 | 40 | import time 41 | start_time = time.time() 42 | for i in range(10000): 43 | for j in range(100): 44 | array_queue.push(i) 45 | for k in range(10): 46 | array_queue.pop() 47 | print("--- %s seconds ---" % (time.time() - start_time)) 48 | """ -------------------------------------------------------------------------------- /Chapter09/bintree_tree.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | 3 | class Node: 4 | def __init__(self, data): 5 | self.data = data 6 | self.right_child = None 7 | self.left_child = None 8 | 9 | class Tree: 10 | def __init__(self): 11 | self.root_node = None 12 | 13 | def insert(self, data): 14 | node = Node(data) 15 | if self.root_node is None: 16 | self.root_node = node 17 | else: 18 | current = self.root_node 19 | parent = None 20 | while True: 21 | parent = current 22 | if node.data < parent.data: 23 | current = current.left_child 24 | if current is None: 25 | parent.left_child = node 26 | return 27 | else: 28 | current = current.right_child 29 | if current is None: 30 | parent.right_child = node 31 | return 32 | 33 | def search(self, data): 34 | current = self.root_node 35 | while True: 36 | if current is None: 37 | return None 38 | elif current.data is data: 39 | return data 40 | elif current.data > data: 41 | current = current.left_child 42 | else: 43 | current = current.right_child 44 | 45 | n1 = Node("root node") 46 | n2 = Node("left child node") 47 | n3 = Node("right child node") 48 | n4 = Node("left grandchild node") 49 | 50 | n1.left_child = n2 51 | n1.right_child = n3 52 | n2.left_child = n4 53 | 54 | current = n1 55 | while current: 56 | print(current.data) 57 | current = current.left_child 58 | 59 | tree = Tree() 60 | tree.insert(5) 61 | tree.insert(2) 62 | tree.insert(7) 63 | tree.insert(9) 64 | tree.insert(1) 65 | 66 | for i in range(1, 10): 67 | found = tree.search(i) 68 | print("{}: {}".format(i, found)) 69 | 70 | -------------------------------------------------------------------------------- /Chapter09/rpn.py: -------------------------------------------------------------------------------- 1 | from stack import Stack 2 | 3 | class TreeNode: 4 | def __init__(self, data=None): 5 | self.data = data 6 | self.right = None 7 | self.left = None 8 | 9 | def calc(node): 10 | if node.data is "+": 11 | return calc(node.left) + calc(node.right) 12 | elif node.data is "-": 13 | return calc(node.left) - calc(node.right) 14 | elif node.data is "*": 15 | return calc(node.left) * calc(node.right) 16 | elif node.data is "/": 17 | return calc(node.left) / calc(node.right) 18 | else: 19 | return node.data 20 | 21 | expr = "4 5 + 5 3 - *".split() 22 | stack = Stack() 23 | 24 | for term in expr: 25 | if term in "+-*/": 26 | node = TreeNode(term) 27 | node.right = stack.pop() 28 | node.left = stack.pop() 29 | else: 30 | node = TreeNode(int(term)) 31 | stack.push(node) 32 | 33 | root = stack.pop() 34 | result = calc(root) 35 | print(result) 36 | -------------------------------------------------------------------------------- /Chapter10/hash.py: -------------------------------------------------------------------------------- 1 | def hash(data): 2 | counter = 1 3 | sum = 0 4 | for d in data: 5 | sum += counter * ord(d) 6 | return sum % 256 7 | 8 | items = ['foo', 'bar', 'bim', 'baz', 'quux', 'duux', 'gnn'] 9 | for item in items: 10 | print("{}: {}".format(item, hash(item))) 11 | 12 | -------------------------------------------------------------------------------- /Chapter10/hashf.py: -------------------------------------------------------------------------------- 1 | def myhash(s): 2 | mult = 1 3 | hv = 0 4 | for ch in s: 5 | hv += mult * ord(ch) 6 | mult += 1 7 | return hv 8 | 9 | for item in ('hello world', 'world hello', 'gello xorld', 'ad', 'ga'): 10 | print("{}: {}".format(item, myhash(item))) 11 | 12 | #print(myhash('hello world')) 13 | #print(myhash('world hello')) 14 | #print(myhash('gello 15 | -------------------------------------------------------------------------------- /Chapter10/hashtable.py: -------------------------------------------------------------------------------- 1 | class HashItem: 2 | def __init__(self, key, value): 3 | self.key = key 4 | self.value = value 5 | 6 | class HashTable: 7 | def __init__(self): 8 | self.size = 256 9 | self.slots = [None for i in range(self.size)] 10 | self.count = 0 11 | 12 | def _hash(self, key): 13 | mult = 1 14 | hv = 0 15 | for ch in key: 16 | hv += mult * ord(ch) 17 | mult += 1 18 | return hv % self.size 19 | 20 | def put(self, key, value): 21 | item = HashItem(key, value) 22 | h = self._hash(key) 23 | 24 | while self.slots[h] is not None: 25 | if self.slots[h].key is key: 26 | break 27 | h = (h + 1) % self.size 28 | if self.slots[h] is None: 29 | self.count += 1 30 | self.slots[h] = item 31 | 32 | def get(self, key): 33 | h = self._hash(key) 34 | while self.slots[h] is not None: 35 | if self.slots[h].key is key: 36 | return self.slots[h].value 37 | h = (h+ 1) % self.size 38 | return None 39 | 40 | def __setitem__(self, key, value): 41 | self.put(key, value) 42 | 43 | def __getitem__(self, key): 44 | return self.get(key) 45 | 46 | ht = HashTable() 47 | ht["good"] = "eggs" 48 | ht["better"] = "ham" 49 | ht["best"] = "spam" 50 | ht["ad"] = "do not" 51 | ht["ga"] = "collide" 52 | 53 | for key in ("good", "better", "best", "worst", "ad", "ga"): 54 | v = ht[key] 55 | print(v) 56 | 57 | print("The number of elements is: {}".format(ht.count)) 58 | -------------------------------------------------------------------------------- /Chapter11/heap.py: -------------------------------------------------------------------------------- 1 | class Heap: 2 | def __init__(self): 3 | self.heap = [0] 4 | self.size = 0 5 | 6 | def float(self, k): 7 | while k // 2 > 0: 8 | if self.heap[k] < self.heap[k//2]: 9 | self.heap[k], self.heap[k//2] = self.heap[k//2], self.heap[k] 10 | k //= 2 11 | 12 | def insert(self, item): 13 | self.heap.append(item) 14 | self.size += 1 15 | self.float(self.size) 16 | 17 | def sink(self, k): 18 | while k * 2 <= self.size: 19 | mc = self.minchild(k) 20 | if self.heap[k] > self.heap[mc]: 21 | self.heap[k], self.heap[mc] = self.heap[mc], self.heap[k] 22 | k = mc 23 | 24 | def minchild(self, k): 25 | if k * 2 + 1 > self.size: 26 | return k * 2 27 | elif self.heap[k*2] < self.heap[k*2+1]: 28 | return k * 2 29 | else: 30 | return k * 2 + 1 31 | 32 | def pop(self): 33 | item = self.heap[1] 34 | self.heap[1] = self.heap[self.size] 35 | self.size -= 1 36 | self.heap.pop() 37 | self.sink(1) 38 | return item 39 | 40 | h = Heap() 41 | for i in (4, 8, 7, 2, 9, 10, 5, 1, 3, 6): 42 | h.insert(i) 43 | 44 | print(h.heap) 45 | 46 | for i in range(10): 47 | n = h.pop() 48 | print(n) 49 | print(h.heap) 50 | -------------------------------------------------------------------------------- /Chapter12/bin_search.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def binary_search(ordered_list, term): 4 | 5 | size_of_list = len(ordered_list) - 1 6 | 7 | index_of_first_element = 0 8 | index_of_last_element = size_of_list 9 | 10 | while index_of_first_element <= index_of_last_element: 11 | mid_point = (index_of_first_element + index_of_last_element)/2 12 | 13 | if ordered_list[mid_point] == term: 14 | return mid_point 15 | 16 | if term > ordered_list[mid_point]: 17 | index_of_first_element = mid_point + 1 18 | else: 19 | index_of_last_element = mid_point - 1 20 | 21 | 22 | store = [] 23 | for i in range(1000000): 24 | store.append(i) 25 | 26 | import time 27 | start_time = time.time() 28 | binary_search(store, 999333333) 29 | print("--- %s seconds ---" % (time.time() - start_time)) 30 | 31 | import time 32 | start_time = time.time() 33 | binary_search(store, 933) 34 | print("--- %s Found: seconds ---" % (time.time() - start_time)) 35 | 36 | 37 | """ 38 | import time 39 | start_time = time.time() 40 | print(search(store, 999)) 41 | print("--- %s seconds ---" % (time.time() - start_time)) 42 | 43 | 44 | """ 45 | 46 | store = [] 47 | for i in range(1000000): 48 | store.append(i) 49 | 50 | import time 51 | start_time = time.time() 52 | results = "Found at {}".format(binary_search(store, 999333333)) 53 | print("--- %s seconds ---: %s" % ((time.time() - start_time), results)) 54 | 55 | store = [] 56 | for i in range(1000000): 57 | store.append(i) 58 | 59 | 60 | 61 | start_time = time.time() 62 | results = "Found at {}".format(binary_search(store, 993)) 63 | print("--- %s seconds --- %s" % ((time.time() - start_time),results)) 64 | 65 | store = [] 66 | for i in range(1000000): 67 | store.append(i) 68 | 69 | 70 | start_time = time.time() 71 | results = "Found at {}".format(binary_search(store, -4999999999995)) 72 | print("--- %s seconds --- %s" % ((time.time() - start_time),results)) 73 | -------------------------------------------------------------------------------- /Chapter12/interpolation_search.py: -------------------------------------------------------------------------------- 1 | 2 | def nearest_mid(input_list, lower_bound_index, upper_bound_index, search_value): 3 | return lower_bound_index + (( upper_bound_index - lower_bound_index)/ (input_list[upper_bound_index] - input_list[lower_bound_index])) * (search_value - input_list[lower_bound_index]) 4 | 5 | def interpolation_search(ordered_list, term): 6 | 7 | size_of_list = len(ordered_list) - 1 8 | 9 | index_of_first_element = 0 10 | index_of_last_element = size_of_list 11 | 12 | while index_of_first_element <= index_of_last_element: 13 | mid_point = nearest_mid(ordered_list, index_of_first_element, index_of_last_element, term) 14 | 15 | if mid_point > index_of_last_element or mid_point < index_of_first_element: 16 | return None 17 | 18 | if ordered_list[mid_point] == term: 19 | return mid_point 20 | 21 | if term > ordered_list[mid_point]: 22 | index_of_first_element = mid_point + 1 23 | else: 24 | index_of_last_element = mid_point - 1 25 | 26 | 27 | store = [] 28 | for i in range(1000000): 29 | store.append(i) 30 | 31 | import time 32 | start_time = time.time() 33 | results = "Found at {}".format(interpolation_search(store, 999333333)) 34 | print("--- %s seconds ---: %s" % ((time.time() - start_time), results)) 35 | 36 | store = [] 37 | for i in range(1000000): 38 | store.append(i) 39 | 40 | 41 | 42 | start_time = time.time() 43 | results = "Found at {}".format(interpolation_search(store, 993)) 44 | print("--- %s seconds --- %s" % ((time.time() - start_time),results)) 45 | 46 | store = [] 47 | for i in range(1000000): 48 | store.append(i) 49 | 50 | 51 | start_time = time.time() 52 | results = "Found at {}".format(interpolation_search(store, -4999999999995)) 53 | print("--- %s seconds --- %s" % ((time.time() - start_time),results)) 54 | -------------------------------------------------------------------------------- /Chapter12/ordered_list_search.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def search(ordered_list, term): 4 | ordered_list_size = len(ordered_list) 5 | for i in range(ordered_list_size): 6 | if term == ordered_list[i]: 7 | return i 8 | elif ordered_list[i] > term: 9 | return None 10 | 11 | return None 12 | 13 | 14 | 15 | scores = [2, 3, 4, 6, 7] 16 | 17 | search_term = 5 18 | position = search(scores, search_term) 19 | 20 | if position is None: 21 | print("{} not found".format(search_term)) 22 | else: 23 | print("{} found at position {}".format(search_term, position)) 24 | 25 | 26 | search_term = 2 27 | position = search(scores, search_term) 28 | if position is None: 29 | print("{} not found".format(search_term)) 30 | else: 31 | print("{} found at position {}".format(search_term, position)) -------------------------------------------------------------------------------- /Chapter12/unordered_list_search.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | def search(unordered_list, term): 4 | unordered_list_size = len(unordered_list) 5 | for i in range(unordered_list_size): 6 | if term == unordered_list[i]: 7 | return i 8 | 9 | return None 10 | 11 | 12 | 13 | scores = [60, 1, 88, 10, 11, 600] 14 | 15 | search_term = 65 16 | position = search(scores, search_term) 17 | 18 | if position is None: 19 | print("{} not found".format(search_term)) 20 | else: 21 | print("{} found at position {}".format(search_term, position)) 22 | 23 | 24 | search_term = 600 25 | position = search(scores, search_term) 26 | if position is None: 27 | print("{} not found".format(search_term)) 28 | else: 29 | print("{} found at position {}".format(search_term, position)) -------------------------------------------------------------------------------- /Chapter13/bubble.py: -------------------------------------------------------------------------------- 1 | 2 | unordered_list = [5, 2] 3 | i = 0 4 | first_element = unordered_list[0] 5 | second_element = unordered_list[1] 6 | 7 | 8 | 9 | temp = unordered_list[0] 10 | unordered_list[0] = unordered_list[1] 11 | unordered_list[1] = temp 12 | 13 | print(unordered_list) 14 | """################################################################""" 15 | 16 | def bubble_sort(unordered_list): 17 | iteration_number = len(unordered_list)-1 18 | for i in range(iteration_number): 19 | for j in range(iteration_number): 20 | if unordered_list[j] > unordered_list[j+1]: 21 | temp = unordered_list[j] 22 | unordered_list[j] = unordered_list[j+1] 23 | unordered_list[j+1] = temp 24 | 25 | 26 | 27 | 28 | my_list = [4,3,2,1] 29 | bubble_sort(my_list) 30 | print(my_list) 31 | 32 | my_list = [1,2,3,4] 33 | bubble_sort(my_list) 34 | print(my_list) -------------------------------------------------------------------------------- /Chapter16/Case Study_ Notebook/command_option.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktPublishing/Getting-Started-with-Python/f40be119a306f1f147ec3b225b90f9de318f5ee2/Chapter16/Case Study_ Notebook/command_option.py -------------------------------------------------------------------------------- /Chapter16/Case Study_ Notebook/menu.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from notebook import Notebook 3 | 4 | 5 | class Menu: 6 | """Display a menu and respond to choices when run.""" 7 | 8 | def __init__(self): 9 | self.notebook = Notebook() 10 | self.choices = { 11 | "1": self.show_notes, 12 | "2": self.search_notes, 13 | "3": self.add_note, 14 | "4": self.modify_note, 15 | "5": self.quit, 16 | } 17 | 18 | def display_menu(self): 19 | print( 20 | """ 21 | Notebook Menu 22 | 23 | 1. Show all Notes 24 | 2. Search Notes 25 | 3. Add Note 26 | 4. Modify Note 27 | 5. Quit 28 | """ 29 | ) 30 | 31 | def run(self): 32 | """Display the menu and respond to choices.""" 33 | while True: 34 | self.display_menu() 35 | choice = input("Enter an option: ") 36 | action = self.choices.get(choice) 37 | if action: 38 | action() 39 | else: 40 | print("{0} is not a valid choice".format(choice)) 41 | 42 | def show_notes(self, notes=None): 43 | if not notes: 44 | notes = self.notebook.notes 45 | for note in notes: 46 | print("{0}: {1}\n{2}".format(note.id, note.tags, note.memo)) 47 | 48 | def search_notes(self): 49 | filter = input("Search for: ") 50 | notes = self.notebook.search(filter) 51 | self.show_notes(notes) 52 | 53 | def add_note(self): 54 | memo = input("Enter a memo: ") 55 | self.notebook.new_note(memo) 56 | print("Your note has been added.") 57 | 58 | def modify_note(self): 59 | id = input("Enter a note id: ") 60 | memo = input("Enter a memo: ") 61 | tags = input("Enter tags: ") 62 | if memo: 63 | self.notebook.modify_memo(id, memo) 64 | if tags: 65 | self.notebook.modify_tags(id, tags) 66 | 67 | def quit(self): 68 | print("Thank you for using your notebook today.") 69 | sys.exit(0) 70 | 71 | 72 | if __name__ == "__main__": 73 | Menu().run() 74 | -------------------------------------------------------------------------------- /Chapter16/Case Study_ Notebook/notebook.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | # Store the next available id for all new notes 4 | last_id = 0 5 | 6 | 7 | class Note: 8 | """Represent a note in the notebook. Match against a 9 | string in searches and store tags for each note.""" 10 | 11 | def __init__(self, memo, tags=""): 12 | """initialize a note with memo and optional 13 | space-separated tags. Automatically set the note's 14 | creation date and a unique id.""" 15 | self.memo = memo 16 | self.tags = tags 17 | self.creation_date = datetime.date.today() 18 | global last_id 19 | last_id += 1 20 | self.id = last_id 21 | 22 | def match(self, filter): 23 | """Determine if this note matches the filter 24 | text. Return True if it matches, False otherwise. 25 | 26 | Search is case sensitive and matches both text and 27 | tags.""" 28 | return filter in self.memo or filter in self.tags 29 | 30 | 31 | class Notebook: 32 | """Represent a collection of notes that can be tagged, 33 | modified, and searched.""" 34 | 35 | def __init__(self): 36 | """Initialize a notebook with an empty list.""" 37 | self.notes = [] 38 | 39 | def new_note(self, memo, tags=""): 40 | """Create a new note and add it to the list.""" 41 | self.notes.append(Note(memo, tags)) 42 | 43 | def _find_note(self, note_id): 44 | """Locate the note with the given id.""" 45 | for note in self.notes: 46 | if str(note.id) == str(note_id): 47 | return note 48 | return None 49 | 50 | def modify_memo(self, note_id, memo): 51 | """Find the note with the given id and change its 52 | memo to the given value.""" 53 | note = self._find_note(note_id) 54 | if note: 55 | note.memo = memo 56 | return True 57 | return False 58 | 59 | def modify_tags(self, note_id, tags): 60 | """Find the note with the given id and change its 61 | tags to the given value.""" 62 | note = self._find_note(note_id) 63 | if note: 64 | note.tags = tags 65 | return True 66 | return False 67 | 68 | def search(self, filter): 69 | """Find all notes that match the given filter 70 | string.""" 71 | return [note for note in self.notes if note.match(filter)] 72 | -------------------------------------------------------------------------------- /Chapter16/first_class.py: -------------------------------------------------------------------------------- 1 | class MyFirstClass: 2 | pass -------------------------------------------------------------------------------- /Chapter16/guarding.py: -------------------------------------------------------------------------------- 1 | class UsefulClass: 2 | """This class might be useful to other modules.""" 3 | 4 | pass 5 | 6 | 7 | def main(): 8 | """Creates a useful class and does something with it for our module.""" 9 | useful = UsefulClass() 10 | print(useful) 11 | 12 | 13 | if __name__ == "__main__": 14 | main() 15 | 16 | -------------------------------------------------------------------------------- /Chapter16/inner_class.py: -------------------------------------------------------------------------------- 1 | def format_string(string, formatter=None): 2 | """Format a string using the formatter object, which 3 | is expected to have a format() method that accepts 4 | a string.""" 5 | 6 | class DefaultFormatter: 7 | """Format a string in title case.""" 8 | 9 | def format(self, string): 10 | return str(string).title() 11 | 12 | if not formatter: 13 | formatter = DefaultFormatter() 14 | 15 | return formatter.format(string) 16 | 17 | 18 | hello_string = "hello world, how are you today?" 19 | print(" input: " + hello_string) 20 | print("output: " + format_string(hello_string)) 21 | -------------------------------------------------------------------------------- /Chapter16/name_mangling.py: -------------------------------------------------------------------------------- 1 | class SecretString: 2 | """A not-at-all secure way to store a secret string.""" 3 | 4 | def __init__(self, plain_string, pass_phrase): 5 | self.__plain_string = plain_string 6 | self.__pass_phrase = pass_phrase 7 | 8 | def decrypt(self, pass_phrase): 9 | """Only show the string if the pass_phrase is correct.""" 10 | if pass_phrase == self.__pass_phrase: 11 | return self.__plain_string 12 | else: 13 | return "" 14 | -------------------------------------------------------------------------------- /Chapter16/point.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | 4 | class Point: 5 | "Represents a point in two-dimensional geometric coordinates" 6 | 7 | def __init__(self, x=0, y=0): 8 | """Initialize the position of a new point. The x and y 9 | coordinates can be specified. If they are not, the 10 | point defaults to the origin.""" 11 | self.move(x, y) 12 | 13 | def move(self, x, y): 14 | "Move the point to a new location in 2D space." 15 | self.x = x 16 | self.y = y 17 | 18 | def reset(self): 19 | "Reset the point back to the geometric origin: 0, 0" 20 | self.move(0, 0) 21 | 22 | def calculate_distance(self, other_point): 23 | """Calculate the distance from this point to a second 24 | point passed as a parameter. 25 | 26 | This function uses the Pythagorean Theorem to calculate 27 | the distance between the two points. The distance is 28 | returned as a float.""" 29 | 30 | return math.sqrt( 31 | (self.x - other_point.x) ** 2 32 | + (self.y - other_point.y) ** 2 33 | ) 34 | 35 | 36 | # how to use it: 37 | point1 = Point() 38 | point2 = Point() 39 | 40 | point1.reset() 41 | point2.move(5, 0) 42 | print(point2.calculate_distance(point1)) 43 | assert point2.calculate_distance(point1) == point1.calculate_distance( 44 | point2 45 | ) 46 | point1.move(3, 4) 47 | print(point1.calculate_distance(point2)) 48 | print(point1.calculate_distance(point1)) 49 | -------------------------------------------------------------------------------- /Chapter17/Case Study_ Online Grader/example_runner.py: -------------------------------------------------------------------------------- 1 | from grader import Grader 2 | from lessons import IntroToPython, Statistics 3 | 4 | grader = Grader() 5 | itp_id = grader.register(IntroToPython) 6 | stat_id = grader.register(Statistics) 7 | 8 | grader.start_assignment("Tammy", itp_id) 9 | print("Tammy's Lesson:", grader.get_lesson("Tammy")) 10 | print( 11 | "Tammy's check:", 12 | grader.check_assignment("Tammy", "a = 1 ; b = 'hello'"), 13 | ) 14 | print( 15 | "Tammy's other check:", 16 | grader.check_assignment("Tammy", "a = 1\nb = 'hello'"), 17 | ) 18 | 19 | print(grader.assignment_summary("Tammy")) 20 | 21 | grader.start_assignment("Tammy", stat_id) 22 | print("Tammy's Lesson:", grader.get_lesson("Tammy")) 23 | print("Tammy's check:", grader.check_assignment("Tammy", "avg=5.25")) 24 | print( 25 | "Tammy's other check:", 26 | grader.check_assignment( 27 | "Tammy", "avg = statistics.mean([1, 5, 18, -3])" 28 | ), 29 | ) 30 | 31 | print(grader.assignment_summary("Tammy")) 32 | -------------------------------------------------------------------------------- /Chapter17/Case Study_ Online Grader/grader.py: -------------------------------------------------------------------------------- 1 | import abc 2 | import uuid 3 | 4 | 5 | class Grader: 6 | def __init__(self): 7 | self.student_graders = {} 8 | self.assignment_classes = {} 9 | 10 | def register(self, assignment_class): 11 | if not issubclass(assignment_class, Assignment): 12 | raise RuntimeError( 13 | "Your class does not have the right methods" 14 | ) 15 | 16 | id = uuid.uuid4() 17 | self.assignment_classes[id] = assignment_class 18 | return id 19 | 20 | def start_assignment(self, student, id): 21 | self.student_graders[student] = AssignmentGrader( 22 | student, self.assignment_classes[id] 23 | ) 24 | 25 | def get_lesson(self, student): 26 | assignment = self.student_graders[student] 27 | return assignment.lesson() 28 | 29 | def check_assignment(self, student, code): 30 | assignment = self.student_graders[student] 31 | return assignment.check(code) 32 | 33 | def assignment_summary(self, student): 34 | grader = self.student_graders[student] 35 | return f""" 36 | {student}'s attempts at {grader.assignment.__class__.__name__}: 37 | 38 | attempts: {grader.attempts} 39 | correct: {grader.correct_attempts} 40 | 41 | passed: {grader.correct_attempts > 0} 42 | """ 43 | 44 | 45 | class Assignment(metaclass=abc.ABCMeta): 46 | @abc.abstractmethod 47 | def lesson(self, student): 48 | pass 49 | 50 | @abc.abstractmethod 51 | def check(self, code): 52 | pass 53 | 54 | @classmethod 55 | def __subclasshook__(cls, C): 56 | if cls is Assignment: 57 | attrs = set(dir(C)) 58 | if set(cls.__abstractmethods__) <= attrs: 59 | return True 60 | 61 | return NotImplemented 62 | 63 | 64 | class AssignmentGrader: 65 | def __init__(self, student, AssignmentClass): 66 | self.assignment = AssignmentClass() 67 | self.assignment.student = student 68 | self.attempts = 0 69 | self.correct_attempts = 0 70 | 71 | def check(self, code): 72 | self.attempts += 1 73 | result = self.assignment.check(code) 74 | if result: 75 | self.correct_attempts += 1 76 | 77 | return result 78 | 79 | def lesson(self): 80 | return self.assignment.lesson() 81 | -------------------------------------------------------------------------------- /Chapter17/Case Study_ Online Grader/lessons.py: -------------------------------------------------------------------------------- 1 | from grader import Assignment 2 | 3 | 4 | class IntroToPython: 5 | def lesson(self): 6 | return f""" 7 | Hello {self.student}. define two variables, 8 | an integer named a with value 1 9 | and a string named b with value 'hello' 10 | 11 | """ 12 | 13 | def check(self, code): 14 | return code == "a = 1\nb = 'hello'" 15 | 16 | 17 | class Statistics(Assignment): 18 | def lesson(self): 19 | return ( 20 | "Good work so far, " 21 | + self.student 22 | + ". Now calculate the average of the numbers " 23 | + " 1, 5, 18, -3 and assign to a variable named 'avg'" 24 | ) 25 | 26 | def check(self, code): 27 | import statistics 28 | 29 | code = "import statistics\n" + code 30 | 31 | local_vars = {} 32 | global_vars = {} 33 | exec(code, global_vars, local_vars) 34 | 35 | return local_vars.get("avg") == statistics.mean([1, 5, 18, -3]) 36 | 37 | 38 | # Some code to test that it's doing the right thing 39 | print( 40 | "IntroToPython is Assignment subclass:", 41 | issubclass(IntroToPython, Assignment), 42 | ) 43 | print( 44 | "Statistics is Assignment subclass:", 45 | issubclass(Statistics, Assignment), 46 | ) 47 | 48 | -------------------------------------------------------------------------------- /Chapter17/audio_player.py: -------------------------------------------------------------------------------- 1 | import abc 2 | 3 | 4 | class AudioFile: 5 | def __init__(self, filename): 6 | if not filename.endswith(self.ext): 7 | raise Exception("Invalid file format") 8 | 9 | self.filename = filename 10 | 11 | 12 | class MP3File(AudioFile): 13 | ext = "mp3" 14 | 15 | def play(self): 16 | print("playing {} as mp3".format(self.filename)) 17 | 18 | 19 | class WavFile(AudioFile): 20 | ext = "wav" 21 | 22 | def play(self): 23 | print("playing {} as wav".format(self.filename)) 24 | 25 | 26 | class OggFile(AudioFile): 27 | ext = "ogg" 28 | 29 | def play(self): 30 | print("playing {} as ogg".format(self.filename)) 31 | 32 | 33 | class FlacFile: 34 | def __init__(self, filename): 35 | if not filename.endswith(".flac"): 36 | raise Exception("Invalid file format") 37 | 38 | self.filename = filename 39 | 40 | def play(self): 41 | print("playing {} as flac".format(self.filename)) 42 | 43 | 44 | class MediaLoader(metaclass=abc.ABCMeta): 45 | @abc.abstractmethod 46 | def play(self): 47 | pass 48 | 49 | @abc.abstractproperty 50 | def ext(self): 51 | pass 52 | 53 | @classmethod 54 | def __subclasshook__(cls, C): 55 | if cls is MediaLoader: 56 | attrs = set(dir(C)) 57 | if set(cls.__abstractmethods__) <= attrs: 58 | return True 59 | 60 | return NotImplemented 61 | -------------------------------------------------------------------------------- /Chapter17/contact.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 | 12 | class Contact: 13 | all_contacts = ContactList() 14 | 15 | def __init__(self, name, email): 16 | self.name = name 17 | self.email = email 18 | Contact.all_contacts.append(self) 19 | 20 | 21 | class Supplier(Contact): 22 | def order(self, order): 23 | print( 24 | "If this were a real system we would send " 25 | "'{}' order to '{}'".format(order, self.name) 26 | ) 27 | 28 | 29 | class Friend(Contact): 30 | def __init__(self, name, email, phone): 31 | self.name = name 32 | self.email = email 33 | self.phone = phone 34 | 35 | 36 | class MailSender: 37 | def send_mail(self, message): 38 | print("Sending mail to " + self.email) 39 | # Add e-mail logic here 40 | 41 | 42 | class EmailableContact(Contact, MailSender): 43 | pass 44 | -------------------------------------------------------------------------------- /Chapter17/contact_multiple_inheritance.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 | 11 | class AddressHolder: 12 | def __init__(self, street="", city="", state="", code="", **kwargs): 13 | super().__init__(**kwargs) 14 | self.street = street 15 | self.city = city 16 | self.state = state 17 | self.code = code 18 | 19 | 20 | class Friend(Contact, AddressHolder): 21 | def __init__(self, phone="", **kwargs): 22 | super().__init__(**kwargs) 23 | self.phone = phone 24 | -------------------------------------------------------------------------------- /Chapter17/multiple_inheritance_bad_demo.py: -------------------------------------------------------------------------------- 1 | class BaseClass: 2 | num_base_calls = 0 3 | 4 | def call_me(self): 5 | print("Calling method on Base Class") 6 | self.num_base_calls += 1 7 | 8 | 9 | class LeftSubclass(BaseClass): 10 | num_left_calls = 0 11 | 12 | def call_me(self): 13 | BaseClass.call_me(self) 14 | print("Calling method on Left Subclass") 15 | self.num_left_calls += 1 16 | 17 | 18 | class RightSubclass(BaseClass): 19 | num_right_calls = 0 20 | 21 | def call_me(self): 22 | BaseClass.call_me(self) 23 | print("Calling method on Right Subclass") 24 | self.num_right_calls += 1 25 | 26 | 27 | class Subclass(LeftSubclass, RightSubclass): 28 | num_sub_calls = 0 29 | 30 | def call_me(self): 31 | LeftSubclass.call_me(self) 32 | RightSubclass.call_me(self) 33 | print("Calling method on Subclass") 34 | self.num_sub_calls += 1 35 | -------------------------------------------------------------------------------- /Chapter17/multiple_inheritance_super.py: -------------------------------------------------------------------------------- 1 | class BaseClass: 2 | num_base_calls = 0 3 | 4 | def call_me(self): 5 | print("Calling method on Base Class") 6 | self.num_base_calls += 1 7 | 8 | 9 | class LeftSubclass(BaseClass): 10 | num_left_calls = 0 11 | 12 | def call_me(self): 13 | super().call_me() 14 | print("Calling method on Left Subclass") 15 | self.num_left_calls += 1 16 | 17 | 18 | class RightSubclass(BaseClass): 19 | num_right_calls = 0 20 | 21 | def call_me(self): 22 | super().call_me() 23 | print("Calling method on Right Subclass") 24 | self.num_right_calls += 1 25 | 26 | 27 | class Subclass(LeftSubclass, RightSubclass): 28 | num_sub_calls = 0 29 | 30 | def call_me(self): 31 | super().call_me() 32 | print("Calling method on Subclass") 33 | self.num_sub_calls += 1 34 | -------------------------------------------------------------------------------- /Chapter17/odd_container.py: -------------------------------------------------------------------------------- 1 | class OddContainer: 2 | def __contains__(self, x): 3 | if not isinstance(x, int) or not x % 2: 4 | return False 5 | return True 6 | -------------------------------------------------------------------------------- /Chapter18/Case Study_ Authorization/auth_driver.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 | 10 | class Editor: 11 | def __init__(self): 12 | self.username = None 13 | self.menu_map = { 14 | "login": self.login, 15 | "test": self.test, 16 | "change": self.change, 17 | "quit": self.quit, 18 | } 19 | 20 | def login(self): 21 | logged_in = False 22 | while not logged_in: 23 | username = input("username: ") 24 | password = input("password: ") 25 | try: 26 | logged_in = auth.authenticator.login(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(permission, self.username) 37 | except auth.NotLoggedInError as e: 38 | print("{} is not logged in".format(e.username)) 39 | return False 40 | except auth.NotPermittedError as e: 41 | print("{} cannot {}".format(e.username, permission)) 42 | return False 43 | else: 44 | return True 45 | 46 | def test(self): 47 | if self.is_permitted("test program"): 48 | print("Testing program now...") 49 | 50 | def change(self): 51 | if self.is_permitted("change program"): 52 | print("Changing program now...") 53 | 54 | def quit(self): 55 | raise SystemExit() 56 | 57 | def menu(self): 58 | try: 59 | answer = "" 60 | while True: 61 | print( 62 | """ 63 | Please enter a command: 64 | \tlogin\tLogin 65 | \ttest\tTest the program 66 | \tchange\tChange the program 67 | \tquit\tQuit 68 | """ 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(answer)) 75 | else: 76 | func() 77 | finally: 78 | print("Thank you for testing the auth module") 79 | 80 | 81 | Editor().menu() 82 | -------------------------------------------------------------------------------- /Chapter18/all_exception_syntax.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | some_exceptions = [ValueError, TypeError, IndexError, None] 4 | 5 | try: 6 | choice = random.choice(some_exceptions) 7 | print("raising {}".format(choice)) 8 | if choice: 9 | raise choice("An error") 10 | except ValueError: 11 | print("Caught a ValueError") 12 | except TypeError: 13 | print("Caught a TypeError") 14 | except Exception as e: 15 | print("Caught some other error: %s" % (e.__class__.__name__)) 16 | else: 17 | print("This code called if there is no exception") 18 | finally: 19 | print("This cleanup code is always called") 20 | -------------------------------------------------------------------------------- /Chapter18/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 | -------------------------------------------------------------------------------- /Chapter18/funny_division.py: -------------------------------------------------------------------------------- 1 | def funny_division(divider): 2 | try: 3 | return 100 / divider 4 | except ZeroDivisionError: 5 | return "Zero is not a good idea!" 6 | 7 | 8 | print(funny_division(0)) 9 | print(funny_division(50.0)) 10 | print(funny_division("hello")) 11 | 12 | 13 | def funny_division2(divider): 14 | try: 15 | if divider == 13: 16 | raise ValueError("13 is an unlucky number") 17 | return 100 / divider 18 | except (ZeroDivisionError, TypeError): 19 | return "Enter a number other than zero" 20 | 21 | 22 | for val in (0, "hello", 50.0, 13): 23 | 24 | print("Testing {}:".format(val), end=" ") 25 | print(funny_division2(val)) 26 | 27 | 28 | def funny_division3(divider): 29 | try: 30 | if divider == 13: 31 | raise ValueError("13 is an unlucky number") 32 | return 100 / divider 33 | except ZeroDivisionError: 34 | return "Enter a number other than zero" 35 | except TypeError: 36 | return "Enter a numerical value" 37 | except ValueError: 38 | print("No, No, not 13!") 39 | raise 40 | -------------------------------------------------------------------------------- /Chapter18/inventory.py: -------------------------------------------------------------------------------- 1 | class OutOfStock(Exception): 2 | pass 3 | 4 | 5 | class InvalidItemType(Exception): 6 | pass 7 | 8 | 9 | class Inventory: 10 | def lock(self, item_type): 11 | """Select the type of item that is going to 12 | be manipulated. This method will lock the 13 | item so nobody else can manipulate the 14 | inventory until it's returned. This prevents 15 | selling the same item to two different 16 | customers.""" 17 | pass 18 | 19 | def unlock(self, item_type): 20 | """Release the given type so that other 21 | customers can access it.""" 22 | pass 23 | 24 | def purchase(self, item_type): 25 | """If the item is not locked, raise an 26 | exception. If the item_type does not exist, 27 | raise an exception. If the item is currently 28 | out of stock, raise an exception. If the item 29 | is available, subtract one item and return 30 | the number of items left.""" 31 | pass 32 | 33 | 34 | item_type = "widget" 35 | inv = Inventory() 36 | inv.lock(item_type) 37 | try: 38 | num_left = inv.purchase(item_type) 39 | except InvalidItemType: 40 | print("Sorry, we don't sell {}".format(item_type)) 41 | except OutOfStock: 42 | print("Sorry, that item is out of stock.") 43 | else: 44 | print( 45 | "Purchase complete. There are " 46 | "{} {}s left".format(num_left, item_type) 47 | ) 48 | finally: 49 | inv.unlock(item_type) 50 | -------------------------------------------------------------------------------- /Chapter18/no_return.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 | 8 | def call_exceptor(): 9 | print("call_exceptor starts here...") 10 | no_return() 11 | print("an exception was raised...") 12 | print("...so these lines don't run") 13 | 14 | 15 | try: 16 | no_return() 17 | except: 18 | print("I caught an exception") 19 | print("executed after the exception") 20 | -------------------------------------------------------------------------------- /Chapter19/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, character) 11 | self.cursor.forward() 12 | 13 | def delete(self): 14 | del self.characters[self.cursor.position] 15 | 16 | def save(self): 17 | with open(self.filename, "w") as f: 18 | f.write("".join(self.characters)) 19 | 20 | @property 21 | def string(self): 22 | return "".join((str(c) for c in self.characters)) 23 | 24 | 25 | class Cursor: 26 | def __init__(self, document): 27 | self.document = document 28 | self.position = 0 29 | 30 | def forward(self): 31 | self.position += 1 32 | 33 | def back(self): 34 | self.position -= 1 35 | 36 | def home(self): 37 | while self.document.characters[self.position - 1].character != "\n": 38 | self.position -= 1 39 | if self.position == 0: 40 | # Got to beginning of file before newline 41 | break 42 | 43 | def end(self): 44 | while ( 45 | self.position < len(self.document.characters) 46 | and self.document.characters[self.position].character != "\n" 47 | ): 48 | self.position += 1 49 | 50 | 51 | class Character: 52 | def __init__(self, character, bold=False, italic=False, underline=False): 53 | assert len(character) == 1 54 | self.character = character 55 | self.bold = bold 56 | self.italic = italic 57 | self.underline = underline 58 | 59 | def __str__(self): 60 | bold = "*" if self.bold else "" 61 | italic = "/" if self.italic else "" 62 | underline = "_" if self.underline else "" 63 | return bold + italic + underline + self.character 64 | -------------------------------------------------------------------------------- /Chapter19/cached_webpage.py: -------------------------------------------------------------------------------- 1 | from urllib.request import urlopen 2 | 3 | 4 | class WebPage: 5 | def __init__(self, url): 6 | self.url = url 7 | self._content = None 8 | 9 | @property 10 | def content(self): 11 | if not self._content: 12 | print("Retrieving New Page...") 13 | self._content = urlopen(self.url).read() 14 | return self._content 15 | -------------------------------------------------------------------------------- /Chapter19/colors.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 | -------------------------------------------------------------------------------- /Chapter19/points.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 | 13 | 14 | square = [(1,1), (1,2), (2,2), (2,1)] 15 | perimeter(square) 16 | 17 | 18 | class Point: 19 | def __init__(self, x, y): 20 | self.x = x 21 | self.y = y 22 | 23 | def distance(self, p2): 24 | return math.sqrt((self.x-p2.x)**2 + (self.y-p2.y)**2) 25 | 26 | class Polygon: 27 | def __init__(self): 28 | self.vertices = [] 29 | 30 | def add_point(self, point): 31 | self.vertices.append((point)) 32 | 33 | def perimeter(self): 34 | perimeter = 0 35 | points = self.vertices + [self.vertices[0]] 36 | for i in range(len(self.vertices)): 37 | perimeter += points[i].distance(points[i+1]) 38 | return perimeter 39 | 40 | 41 | square = Polygon() 42 | square.add_point(Point(1,1)) 43 | square.add_point(Point(1,2)) 44 | square.add_point(Point(2,2)) 45 | square.add_point(Point(2,1)) 46 | square.perimeter() 47 | 48 | class Polygon2(Polygon): 49 | def __init__(self, points=None): 50 | points = points if points else [] 51 | self.vertices = [] 52 | for point in points: 53 | if isinstance(point, tuple): 54 | point = Point(*point) 55 | self.vertices.append(point) 56 | -------------------------------------------------------------------------------- /Chapter19/silly.py: -------------------------------------------------------------------------------- 1 | class Silly: 2 | def _get_silly(self): 3 | print("You are getting silly") 4 | return self._silly 5 | 6 | def _set_silly(self, value): 7 | print("You are making silly {}".format(value)) 8 | self._silly = value 9 | 10 | def _del_silly(self): 11 | print("Whoah, you killed silly!") 12 | del self._silly 13 | 14 | silly = property(_get_silly, _set_silly, _del_silly, "This is a silly property") 15 | 16 | 17 | class SillyDecorated: 18 | @property 19 | def silly(self): 20 | "This is a silly property" 21 | print("You are getting silly") 22 | return self._silly 23 | 24 | @silly.setter 25 | def silly(self, value): 26 | print("You are making silly {}".format(value)) 27 | self._silly = value 28 | 29 | @silly.deleter 30 | def silly(self): 31 | print("Whoah, you killed silly!") 32 | del self._silly 33 | -------------------------------------------------------------------------------- /Chapter19/zip_find_replace.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import shutil 3 | import zipfile 4 | from pathlib import Path 5 | 6 | 7 | class ZipReplace: 8 | def __init__(self, filename, search_string, replace_string): 9 | self.filename = filename 10 | self.search_string = search_string 11 | self.replace_string = replace_string 12 | self.temp_directory = Path(f"unzipped-{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(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(self.search_string, self.replace_string) 29 | with filename.open("w") as file: 30 | file.write(contents) 31 | 32 | def zip_files(self): 33 | with zipfile.ZipFile(self.filename, "w") as file: 34 | for filename in self.temp_directory.iterdir(): 35 | file.write(filename, filename.name) 36 | shutil.rmtree(self.temp_directory) 37 | 38 | 39 | if __name__ == "__main__": 40 | ZipReplace(*sys.argv[1:4]).zip_find_replace() 41 | -------------------------------------------------------------------------------- /Chapter19/zip_processor.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import shutil 3 | import zipfile 4 | from pathlib import Path 5 | from PIL import Image 6 | 7 | 8 | class ZipProcessor: 9 | def __init__(self, zipname): 10 | self.zipname = zipname 11 | self.temp_directory = Path(f"unzipped-{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(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(filename, filename.name) 27 | shutil.rmtree(self.temp_directory) 28 | 29 | 30 | class ZipReplace(ZipProcessor): 31 | def __init__(self, filename, search_string, replace_string): 32 | super().__init__(filename) 33 | self.search_string = search_string 34 | self.replace_string = replace_string 35 | 36 | def process_files(self): 37 | """perform a search and replace on all files in the 38 | temporary directory""" 39 | for filename in self.temp_directory.iterdir(): 40 | with filename.open() as file: 41 | contents = file.read() 42 | contents = contents.replace(self.search_string, self.replace_string) 43 | with filename.open("w") as file: 44 | file.write(contents) 45 | 46 | 47 | class ScaleZip(ZipProcessor): 48 | def process_files(self): 49 | """Scale each image in the directory to 640x480""" 50 | for filename in self.temp_directory.iterdir(): 51 | im = Image.open(str(filename)) 52 | scaled = im.resize((640, 480)) 53 | scaled.save(filename) 54 | 55 | 56 | if __name__ == "__main__": 57 | # ZipReplace(*sys.argv[1:4]).process_zip() 58 | ScaleZip(*sys.argv[1:4]).process_zip() 59 | -------------------------------------------------------------------------------- /Chapter20/Case Study_ Mailing Lists/addresses.db: -------------------------------------------------------------------------------- 1 | friend1@example.com friends 2 | friend2@example.com family 3 | family1@example.com family 4 | friend3@example.com friends 5 | -------------------------------------------------------------------------------- /Chapter20/Case Study_ Mailing Lists/mailing_list.py: -------------------------------------------------------------------------------- 1 | from contextlib import suppress 2 | from collections import defaultdict 3 | import smtplib 4 | from email.mime.text import MIMEText 5 | 6 | 7 | def send_email( 8 | subject, 9 | message, 10 | from_addr, 11 | *to_addrs, 12 | host="localhost", 13 | port=1025, 14 | headers=None 15 | ): 16 | headers = headers if headers else {} 17 | 18 | email = MIMEText(message) 19 | email["Subject"] = subject 20 | email["From"] = from_addr 21 | for header, value in headers.items(): 22 | email[header] = value 23 | 24 | sender = smtplib.SMTP(host, port) 25 | for addr in to_addrs: 26 | del email["To"] 27 | email["To"] = addr 28 | sender.sendmail(from_addr, addr, email.as_string()) 29 | sender.quit() 30 | 31 | 32 | class MailingList: 33 | """Manage groups of e-mail addresses for sending e-mails.""" 34 | 35 | def __init__(self, data_file): 36 | self.data_file = data_file 37 | self.email_map = defaultdict(set) 38 | 39 | def add_to_group(self, email, group): 40 | self.email_map[email].add(group) 41 | 42 | def emails_in_groups(self, *groups): 43 | groups = set(groups) 44 | emails = set() 45 | for e, g in self.email_map.items(): 46 | if g & groups: 47 | emails.add(e) 48 | return emails 49 | 50 | def send_mailing( 51 | self, subject, message, from_addr, *groups, headers=None 52 | ): 53 | emails = self.emails_in_groups(*groups) 54 | send_email( 55 | subject, message, from_addr, *emails, headers=headers 56 | ) 57 | 58 | def save(self): 59 | with open(self.data_file, "w") as file: 60 | for email, groups in self.email_map.items(): 61 | file.write("{} {}\n".format(email, ",".join(groups))) 62 | 63 | def load(self): 64 | self.email_map = defaultdict(set) 65 | with suppress(IOError): 66 | with open(self.data_file) as file: 67 | for line in file: 68 | email, groups = line.strip().split(" ") 69 | groups = set(groups.split(",")) 70 | self.email_map[email] = groups 71 | 72 | def __enter__(self): 73 | self.load() 74 | return self 75 | 76 | def __exit__(self, type, value, tb): 77 | self.save() 78 | -------------------------------------------------------------------------------- /Chapter20/augmented_move.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | import os.path 3 | 4 | 5 | def augmented_move( 6 | target_folder, *filenames, verbose=False, **specific 7 | ): 8 | """Move all filenames into the target_folder, allowing 9 | specific treatment of certain files.""" 10 | 11 | def print_verbose(message, filename): 12 | """print the message only if verbose is enabled""" 13 | if verbose: 14 | print(message.format(filename)) 15 | 16 | for filename in filenames: 17 | target_path = os.path.join(target_folder, filename) 18 | if filename in specific: 19 | if specific[filename] == "ignore": 20 | print_verbose("Ignoring {0}", filename) 21 | elif specific[filename] == "copy": 22 | print_verbose("Copying {0}", filename) 23 | shutil.copyfile(filename, target_path) 24 | else: 25 | print_verbose("Moving {0}", filename) 26 | shutil.move(filename, target_path) 27 | 28 | 29 | augmented_move( 30 | "target", "augmented_move.py", "custom_sequence.py", verbose=True 31 | ) 32 | 33 | -------------------------------------------------------------------------------- /Chapter20/custom_sequence.py: -------------------------------------------------------------------------------- 1 | normal_list = [1, 2, 3, 4, 5] 2 | 3 | 4 | class CustomSequence: 5 | def __len__(self): 6 | return 5 7 | 8 | def __getitem__(self, index): 9 | return f"x{index}" 10 | 11 | 12 | class FunkyBackwards: 13 | def __reversed__(self): 14 | return "BACKWARDS!" 15 | 16 | 17 | for seq in normal_list, CustomSequence(), FunkyBackwards(): 18 | print(f"\n{seq.__class__.__name__}: ", end="") 19 | for item in reversed(seq): 20 | print(item, end=", ") 21 | -------------------------------------------------------------------------------- /Chapter20/default_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 | 10 | def __init__(self, **kwargs): 11 | self.options = dict(Options.default_options) 12 | self.options.update(kwargs) 13 | 14 | def __getitem__(self, key): 15 | return self.options[key] 16 | -------------------------------------------------------------------------------- /Chapter20/enumerate_lines.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | filename = sys.argv[1] 4 | 5 | with open(filename) as file: 6 | for index, line in enumerate(file): 7 | print(f"{index+1}: {line}", end="") 8 | -------------------------------------------------------------------------------- /Chapter20/event_timer.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import time 3 | 4 | 5 | class TimedEvent: 6 | def __init__(self, endtime, callback): 7 | self.endtime = endtime 8 | self.callback = callback 9 | 10 | def ready(self): 11 | return self.endtime <= datetime.datetime.now() 12 | 13 | 14 | class Timer: 15 | def __init__(self): 16 | self.events = [] 17 | 18 | def call_after(self, delay, callback): 19 | end_time = datetime.datetime.now() + datetime.timedelta( 20 | seconds=delay 21 | ) 22 | 23 | self.events.append(TimedEvent(end_time, callback)) 24 | 25 | def run(self): 26 | while True: 27 | ready_events = (e for e in self.events if e.ready()) 28 | for event in ready_events: 29 | event.callback(self) 30 | self.events.remove(event) 31 | time.sleep(0.5) 32 | 33 | 34 | def format_time(message, *args): 35 | now = datetime.datetime.now() 36 | print(f"{now:%I:%M:%S}: {message}") 37 | 38 | 39 | def one(timer): 40 | format_time("Called One") 41 | 42 | 43 | def two(timer): 44 | format_time("Called Two") 45 | 46 | 47 | def three(timer): 48 | format_time("Called Three") 49 | 50 | 51 | class Repeater: 52 | def __init__(self): 53 | self.count = 0 54 | 55 | def __call__(self, timer): 56 | format_time(f"repeat {self.count}") 57 | self.count += 1 58 | timer.call_after(5, self) 59 | 60 | 61 | timer = Timer() 62 | timer.call_after(1, one) 63 | timer.call_after(2, one) 64 | timer.call_after(2, two) 65 | timer.call_after(4, two) 66 | timer.call_after(3, three) 67 | timer.call_after(6, three) 68 | repeater = Repeater() 69 | timer.call_after(5, repeater) 70 | format_time("Starting") 71 | timer.run() 72 | -------------------------------------------------------------------------------- /Chapter20/passing_functions.py: -------------------------------------------------------------------------------- 1 | def my_function(): 2 | print("The Function Was Called") 3 | 4 | 5 | my_function.description = "A silly function" 6 | 7 | 8 | def second_function(): 9 | print("The second was called") 10 | 11 | 12 | second_function.description = "A sillier function." 13 | 14 | 15 | def another_function(function): 16 | print("The description:", end=" ") 17 | print(function.description) 18 | print("The name:", end=" ") 19 | print(function.__name__) 20 | print("The class:", end=" ") 21 | print(function.__class__) 22 | print("Now I'll call the function passed in") 23 | function() 24 | 25 | 26 | another_function(my_function) 27 | another_function(second_function) 28 | -------------------------------------------------------------------------------- /Chapter21/Case Study_ Machine Learning/check_output.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | import csv 3 | 4 | 5 | class Application(tk.Frame): 6 | def __init__(self, master=None): 7 | super().__init__(master) 8 | self.grid(sticky="news") 9 | master.columnconfigure(0, weight=1) 10 | master.rowconfigure(0, weight=1) 11 | self.csv_reader = csv.reader(open("output.csv")) 12 | self.create_widgets() 13 | self.total_count = 0 14 | self.right_count = 0 15 | 16 | def next_color(self): 17 | return next(self.csv_reader) 18 | 19 | def mk_grid(self, widget, column, row, columnspan=1): 20 | widget.grid( 21 | column=column, row=row, columnspan=columnspan, sticky="news" 22 | ) 23 | 24 | def create_widgets(self): 25 | color_text, color_bg = self.next_color() 26 | self.color_box = tk.Label( 27 | self, bg=color_bg, width="30", height="15" 28 | ) 29 | self.mk_grid(self.color_box, 0, 0, 2) 30 | 31 | self.color_label = tk.Label(self, text=color_text, height="3") 32 | self.mk_grid(self.color_label, 0, 1, 2) 33 | 34 | self.no_button = tk.Button( 35 | self, command=self.count_next, text="No" 36 | ) 37 | self.mk_grid(self.no_button, 0, 2) 38 | 39 | self.yes_button = tk.Button( 40 | self, command=self.count_yes, text="Yes" 41 | ) 42 | self.mk_grid(self.yes_button, 1, 2) 43 | 44 | self.percent_accurate = tk.Label(self, height="3", text="0%") 45 | self.mk_grid(self.percent_accurate, 0, 3, 2) 46 | 47 | self.quit = tk.Button( 48 | self, text="Quit", command=root.destroy, bg="#ffaabb" 49 | ) 50 | self.mk_grid(self.quit, 0, 4, 2) 51 | 52 | def count_yes(self): 53 | self.right_count += 1 54 | self.count_next() 55 | 56 | def count_next(self): 57 | self.total_count += 1 58 | percentage = self.right_count / self.total_count 59 | self.percent_accurate["text"] = f"{percentage:.0%}" 60 | try: 61 | color_text, color_bg = self.next_color() 62 | except StopIteration: 63 | color_text = "DONE" 64 | color_bg = "#ffffff" 65 | self.color_box["text"] = "DONE" 66 | self.yes_button["state"] = tk.DISABLED 67 | self.no_button["state"] = tk.DISABLED 68 | self.color_label["text"] = color_text 69 | self.color_box["bg"] = color_bg 70 | 71 | 72 | root = tk.Tk() 73 | app = Application(master=root) 74 | app.mainloop() 75 | -------------------------------------------------------------------------------- /Chapter21/Case Study_ Machine Learning/machine_learn.py: -------------------------------------------------------------------------------- 1 | import csv 2 | from random import randint 3 | from collections import Counter 4 | 5 | dataset_filename = "colors.csv" 6 | 7 | 8 | def hex_to_rgb(hex_color): 9 | return tuple(int(hex_color[i : i + 2], 16) for i in range(1, 6, 2)) 10 | 11 | 12 | def load_colors(filename): 13 | with open(filename) as dataset_file: 14 | lines = csv.reader(dataset_file) 15 | for line in lines: 16 | label, hex_color = line 17 | yield (hex_to_rgb(hex_color), label) 18 | 19 | 20 | def generate_colors(count=100): 21 | for i in range(count): 22 | yield (randint(0, 255), randint(0, 255), randint(0, 255)) 23 | 24 | 25 | def color_distance(color1, color2): 26 | channels = zip(color1, color2) 27 | sum_distance_squared = 0 28 | for c1, c2 in channels: 29 | sum_distance_squared += (c1 - c2) ** 2 30 | return sum_distance_squared 31 | 32 | 33 | def nearest_neighbors(model_colors, target_colors, num_neighbors=5): 34 | model_colors = list(model_colors) 35 | for target in target_colors: 36 | distances = sorted( 37 | ((color_distance(c[0], target), c) for c in model_colors) 38 | ) 39 | yield target, [d[1] for d in distances[:num_neighbors]] 40 | 41 | 42 | def name_colors(model_colors, target_colors, num_neighbors=5): 43 | for target, near in nearest_neighbors( 44 | model_colors, target_colors, num_neighbors=5 45 | ): 46 | name_guess = Counter(n[1] for n in near).most_common()[0][0] 47 | yield target, name_guess 48 | 49 | 50 | def write_results(colors, filename="output.csv"): 51 | with open(filename, "w") as file: 52 | writer = csv.writer(file) 53 | for (r, g, b), name in colors: 54 | writer.writerow([name, f"#{r:02x}{g:02x}{b:02x}"]) 55 | 56 | 57 | def process_colors(dataset_filename="colors.csv"): 58 | model_colors = load_colors(dataset_filename) 59 | colors = name_colors(model_colors, generate_colors(), 5) 60 | write_results(colors) 61 | 62 | 63 | if __name__ == "__main__": 64 | process_colors() 65 | -------------------------------------------------------------------------------- /Chapter21/Case Study_ Machine Learning/manual_classify.py: -------------------------------------------------------------------------------- 1 | import random 2 | import tkinter as tk 3 | import csv 4 | 5 | 6 | class Application(tk.Frame): 7 | def __init__(self, master=None): 8 | super().__init__(master) 9 | self.grid(sticky="news") 10 | master.columnconfigure(0, weight=1) 11 | master.rowconfigure(0, weight=1) 12 | self.create_widgets() 13 | self.file = csv.writer(open("colors.csv", "a")) 14 | 15 | def create_color_button(self, label, column, row): 16 | button = tk.Button( 17 | self, command=lambda: self.click_color(label), text=label 18 | ) 19 | button.grid(column=column, row=row, sticky="news") 20 | 21 | def random_color(self): 22 | r = random.randint(0, 255) 23 | g = random.randint(0, 255) 24 | b = random.randint(0, 255) 25 | 26 | return f"#{r:02x}{g:02x}{b:02x}" 27 | 28 | def create_widgets(self): 29 | self.color_box = tk.Label( 30 | self, bg=self.random_color(), width="30", height="15" 31 | ) 32 | self.color_box.grid( 33 | column=0, columnspan=2, row=0, sticky="news" 34 | ) 35 | self.create_color_button("Red", 0, 1) 36 | self.create_color_button("Purple", 1, 1) 37 | self.create_color_button("Blue", 0, 2) 38 | self.create_color_button("Green", 1, 2) 39 | self.create_color_button("Yellow", 0, 3) 40 | self.create_color_button("Orange", 1, 3) 41 | self.create_color_button("Pink", 0, 4) 42 | self.create_color_button("Grey", 1, 4) 43 | self.quit = tk.Button( 44 | self, text="Quit", command=root.destroy, bg="#ffaabb" 45 | ) 46 | self.quit.grid(column=0, row=5, columnspan=2, sticky="news") 47 | 48 | def click_color(self, label): 49 | self.file.writerow([label, self.color_box["bg"]]) 50 | self.color_box["bg"] = self.random_color() 51 | 52 | 53 | root = tk.Tk() 54 | app = Application(master=root) 55 | app.mainloop() 56 | -------------------------------------------------------------------------------- /Chapter21/Case Study_ Machine Learning/output.csv: -------------------------------------------------------------------------------- 1 | Grey,#7c5f62 2 | Purple,#f002ce 3 | Green,#0fa210 4 | Blue,#228fdf 5 | Green,#07af68 6 | Purple,#922bc5 7 | Green,#04c871 8 | Green,#038771 9 | Purple,#bd57b5 10 | Green,#577209 11 | Blue,#114251 12 | Blue,#0b34c3 13 | Blue,#529ce8 14 | Purple,#dc38f6 15 | Orange,#5e270a 16 | Yellow,#dab40c 17 | Green,#13ec59 18 | Green,#048116 19 | Blue,#3a407c 20 | Yellow,#92aa14 21 | Grey,#352429 22 | Yellow,#e0cd7b 23 | Green,#1ab50a 24 | Purple,#6a3644 25 | Pink,#f311ae 26 | Orange,#e7780e 27 | Grey,#535d81 28 | Green,#6fdd6a 29 | Grey,#312142 30 | Green,#0ff5b4 31 | Green,#368b26 32 | Green,#1a6128 33 | Orange,#b98362 34 | Grey,#7d7349 35 | Red,#6b3a20 36 | Grey,#e1c8e7 37 | Blue,#1628c2 38 | Grey,#24110a 39 | Pink,#bb6885 40 | Blue,#2726eb 41 | Yellow,#faf18c 42 | Purple,#bf66b3 43 | Blue,#253c46 44 | Grey,#4c6c85 45 | Orange,#ff910d 46 | Grey,#afba9f 47 | Green,#c0c956 48 | Grey,#013707 49 | Green,#237161 50 | Blue,#2cc7eb 51 | Red,#f9202c 52 | Green,#57ca89 53 | Grey,#c0ede2 54 | Green,#719525 55 | Blue,#0b0e96 56 | Orange,#848006 57 | Green,#55d06c 58 | Yellow,#e5f480 59 | Purple,#591632 60 | Orange,#fb8d26 61 | Purple,#c269d7 62 | Blue,#68e9e5 63 | Green,#01e062 64 | Grey,#120026 65 | Green,#57fd7a 66 | Blue,#27b3f1 67 | Green,#53f61e 68 | Blue,#02109d 69 | Blue,#1e1c9a 70 | Red,#a30013 71 | Pink,#ed0759 72 | Purple,#551894 73 | Grey,#696847 74 | Yellow,#f7e418 75 | Orange,#e77c85 76 | Blue,#6583de 77 | Green,#6aca71 78 | Green,#025b64 79 | Green,#67cd2a 80 | Blue,#30dbee 81 | Blue,#41619f 82 | Grey,#cac7b6 83 | Purple,#843fae 84 | Purple,#c85dae 85 | Purple,#d03bd0 86 | Orange,#cdb676 87 | Pink,#f4bff3 88 | Green,#486e5c 89 | Green,#67d645 90 | Grey,#0e0900 91 | Blue,#858ef7 92 | Green,#60afb6 93 | Grey,#697952 94 | Green,#3c7c91 95 | Grey,#e4efff 96 | Pink,#775447 97 | Red,#6e4339 98 | Grey,#dac9bb 99 | Green,#58f88e 100 | Orange,#c4700b 101 | -------------------------------------------------------------------------------- /Chapter21/Case Study_ Machine Learning/second_edition_machine_learn_with_coroutines.py: -------------------------------------------------------------------------------- 1 | """ 2 | In the 2nd edition of Python 3 Object Oriented Programming, I presented the 3 | case study using coroutines. In the third edition, I switched to a generator 4 | based implementation instead. It is extremely unusual to use coroutines for 5 | this purpose, and the generator implementation is actually simpler to 6 | understand, read, and maintain. It's also substantially more object oriented. 7 | 8 | This file contains the coroutine based implementation. I am including it here 9 | for historical purposes in case anyone has an academic interest in this 10 | implementation. 11 | """ 12 | 13 | import csv 14 | from random import random 15 | import math 16 | from collections import Counter 17 | 18 | dataset_filename = "colors.csv" 19 | 20 | 21 | def load_colors(filename): 22 | with open(filename) as dataset_file: 23 | lines = csv.reader(dataset_file) 24 | for line in lines: 25 | yield tuple(float(y) for y in line[0:3]), line[3] 26 | 27 | 28 | def generate_colors(count=100): 29 | for i in range(count): 30 | yield (random(), random(), random()) 31 | 32 | 33 | def color_distance(color1, color2): 34 | channels = zip(color1, color2) 35 | sum_distance_squared = 0 36 | for c1, c2 in channels: 37 | sum_distance_squared += (c1 - c2) ** 2 38 | return math.sqrt(sum_distance_squared) 39 | 40 | 41 | def nearest_neighbors(model_colors, num_neighbors): 42 | model = list(model_colors) 43 | target = yield 44 | while True: 45 | distances = sorted( 46 | ((color_distance(c[0], target), c) for c in model) 47 | ) 48 | target = yield [d[1] for d in distances[0:num_neighbors]] 49 | 50 | 51 | def name_colors(get_neighbors): 52 | color = yield 53 | while True: 54 | near = get_neighbors.send(color) 55 | name_guess = Counter(n[1] for n in near).most_common(1)[0][0] 56 | color = yield name_guess 57 | 58 | 59 | def write_results(filename="output.csv"): 60 | with open(filename, "w") as file: 61 | writer = csv.writer(file) 62 | while True: 63 | color, name = yield 64 | writer.writerow(list(color) + [name]) 65 | 66 | 67 | def process_colors(dataset_filename="colors.csv"): 68 | model_colors = load_colors(dataset_filename) 69 | get_neighbors = nearest_neighbors(model_colors, 5) 70 | get_color_name = name_colors(get_neighbors) 71 | output = write_results() 72 | next(output) 73 | next(get_neighbors) 74 | next(get_color_name) 75 | 76 | for color in generate_colors(): 77 | name = get_color_name.send(color) 78 | output.send((color, name)) 79 | 80 | 81 | process_colors() 82 | -------------------------------------------------------------------------------- /Chapter21/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 | -------------------------------------------------------------------------------- /Chapter21/book_authors.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 | # set comprehension 15 | fantasy_authors = {b.author for b in books if b.genre == "fantasy"} 16 | # dict comprehension 17 | fantasy_titles = {b.title: b for b in books if b.genre == "fantasy"} 18 | -------------------------------------------------------------------------------- /Chapter21/capital_iterator.py: -------------------------------------------------------------------------------- 1 | class CapitalIterable: 2 | def __init__(self, string): 3 | self.string = string 4 | 5 | def __iter__(self): 6 | return CapitalIterator(self.string) 7 | 8 | 9 | class CapitalIterator: 10 | def __init__(self, string): 11 | self.words = [w.capitalize() for w in string.split()] 12 | self.index = 0 13 | 14 | def __next__(self): 15 | if self.index == len(self.words): 16 | raise StopIteration() 17 | 18 | word = self.words[self.index] 19 | self.index += 1 20 | return word 21 | 22 | def __iter__(self): 23 | return self 24 | -------------------------------------------------------------------------------- /Chapter21/filesystem_generator.py: -------------------------------------------------------------------------------- 1 | class File: 2 | def __init__(self, name): 3 | self.name = name 4 | 5 | 6 | class Folder(File): 7 | def __init__(self, name): 8 | super().__init__(name) 9 | self.children = [] 10 | 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 | -------------------------------------------------------------------------------- /Chapter21/list_comp.py: -------------------------------------------------------------------------------- 1 | input_strings = ["1", "5", "28", "131", "3"] 2 | # without comprehension 3 | output_integers = [] 4 | for num in input_strings: 5 | output_integers.append(int(num)) 6 | print(output_integers) 7 | 8 | 9 | # with comprehension 10 | output_integers = [int(num) for num in input_strings] 11 | print(output_integers) 12 | 13 | # with filter 14 | output_integers = [int(num) for num in input_strings if len(num) < 3] 15 | print(output_integers) 16 | -------------------------------------------------------------------------------- /Chapter21/list_comp_read_file.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | filename = sys.argv[1] 4 | 5 | with open(filename) as file: 6 | header = file.readline().strip().split("\t") 7 | contacts = [ 8 | dict(zip(header, line.strip().split("\t"))) for line in file 9 | ] 10 | 11 | for contact in contacts: 12 | print("email: {email} -- {last}, {first}".format(**contact)) 13 | 14 | -------------------------------------------------------------------------------- /Chapter21/log_loop.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 | -------------------------------------------------------------------------------- /Chapter21/sample_log.txt: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /Chapter21/score_tally.py: -------------------------------------------------------------------------------- 1 | def tally(): 2 | score = 0 3 | while True: 4 | increment = yield score 5 | score += increment 6 | -------------------------------------------------------------------------------- /Chapter21/warning_generators.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # generator expression 4 | inname, outname = sys.argv[1:3] 5 | 6 | with open(inname) as infile: 7 | with open(outname, "w") as outfile: 8 | warnings = ( 9 | l.replace("\tWARNING", "") for l in infile if "WARNING" in l 10 | ) 11 | for l in warnings: 12 | outfile.write(l) 13 | 14 | # normal loop 15 | with open(inname) as infile: 16 | with open(outname, "w") as outfile: 17 | for l in infile: 18 | if "WARNING" in l: 19 | outfile.write(l.replace("\tWARNING", "")) 20 | 21 | 22 | # object-oriented 23 | class WarningFilter: 24 | def __init__(self, insequence): 25 | self.insequence = insequence 26 | 27 | def __iter__(self): 28 | return self 29 | 30 | def __next__(self): 31 | l = self.insequence.readline() 32 | while l and "WARNING" not in l: 33 | l = self.insequence.readline() 34 | if not l: 35 | raise StopIteration 36 | return l.replace("\tWARNING", "") 37 | 38 | 39 | with open(inname) as infile: 40 | with open(outname, "w") as outfile: 41 | filter = WarningFilter(infile) 42 | for l in filter: 43 | outfile.write(l) 44 | 45 | 46 | # Generator with yield 47 | def warnings_filter(insequence): 48 | for l in insequence: 49 | if "WARNING" in l: 50 | yield l.replace("\tWARNING", "") 51 | 52 | 53 | with open(inname) as infile: 54 | with open(outname, "w") as outfile: 55 | filter = warnings_filter(infile) 56 | for l in filter: 57 | outfile.write(l) 58 | 59 | 60 | # Generator with yield from 61 | def warnings_filter(infilename): 62 | with open(infilename) as infile: 63 | yield from ( 64 | l.replace("\tWARNING", "") for l in infile if "WARNING" in l 65 | ) 66 | 67 | 68 | filter = warnings_filter(inname) 69 | with open(outname, "w") as outfile: 70 | for l in filter: 71 | outfile.write(l) 72 | -------------------------------------------------------------------------------- /Chapter21/xfs_error_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 | try: 19 | bus = matcher.send( 20 | "(sd \S+) {}.*".format(re.escape(device)) 21 | ) 22 | serial = matcher.send("{} \(SERIAL=([^)]*)\)".format(bus)) 23 | yield serial 24 | device = matcher.send(ERROR_RE) 25 | except StopIteration: 26 | return 27 | 28 | 29 | for serial_number in get_serials("EXAMPLE_LOG.log"): 30 | print(serial_number) 31 | -------------------------------------------------------------------------------- /Chapter22/car_sales_template.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import sqlite3 3 | 4 | conn = sqlite3.connect("sales.db") 5 | 6 | conn.execute( 7 | "CREATE TABLE Sales (salesperson text, " 8 | "amt currency, year integer, model text, new boolean)" 9 | ) 10 | conn.execute( 11 | "INSERT INTO Sales values" 12 | " ('Tim', 16000, 2010, 'Honda Fit', 'true')" 13 | ) 14 | conn.execute( 15 | "INSERT INTO Sales values" 16 | " ('Tim', 9000, 2006, 'Ford Focus', 'false')" 17 | ) 18 | conn.execute( 19 | "INSERT INTO Sales values" 20 | " ('Gayle', 8000, 2004, 'Dodge Neon', 'false')" 21 | ) 22 | conn.execute( 23 | "INSERT INTO Sales values" 24 | " ('Gayle', 28000, 2009, 'Ford Mustang', 'true')" 25 | ) 26 | conn.execute( 27 | "INSERT INTO Sales values" 28 | " ('Gayle', 50000, 2010, 'Lincoln Navigator', 'true')" 29 | ) 30 | conn.execute( 31 | "INSERT INTO Sales values" 32 | " ('Don', 20000, 2008, 'Toyota Prius', 'false')" 33 | ) 34 | conn.commit() 35 | conn.close() 36 | 37 | 38 | class QueryTemplate: 39 | def connect(self): 40 | self.conn = sqlite3.connect("sales.db") 41 | 42 | def construct_query(self): 43 | raise NotImplementedError() 44 | 45 | def do_query(self): 46 | results = self.conn.execute(self.query) 47 | self.results = results.fetchall() 48 | 49 | def format_results(self): 50 | output = [] 51 | for row in self.results: 52 | row = [str(i) for i in row] 53 | output.append(", ".join(row)) 54 | self.formatted_results = "\n".join(output) 55 | 56 | def output_results(self): 57 | raise NotImplementedError() 58 | 59 | def process_format(self): 60 | self.connect() 61 | self.construct_query() 62 | self.do_query() 63 | self.format_results() 64 | self.output_results() 65 | 66 | 67 | class NewVehiclesQuery(QueryTemplate): 68 | def construct_query(self): 69 | self.query = "select * from Sales where new='true'" 70 | 71 | def output_results(self): 72 | print(self.formatted_results) 73 | 74 | 75 | class UserGrossQuery(QueryTemplate): 76 | def construct_query(self): 77 | self.query = ( 78 | "select salesperson, sum(amt) " 79 | + " from Sales group by salesperson" 80 | ) 81 | 82 | def output_results(self): 83 | filename = "gross_sales_{0}".format( 84 | datetime.date.today().strftime("%Y%m%d") 85 | ) 86 | with open(filename, "w") as outfile: 87 | outfile.write(self.formatted_results) 88 | -------------------------------------------------------------------------------- /Chapter22/inventory_observer.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 | 14 | @product.setter 15 | def product(self, value): 16 | self._product = value 17 | self._update_observers() 18 | 19 | @property 20 | def quantity(self): 21 | return self._quantity 22 | 23 | @quantity.setter 24 | def quantity(self, value): 25 | self._quantity = value 26 | self._update_observers() 27 | 28 | def _update_observers(self): 29 | for observer in self.observers: 30 | observer() 31 | 32 | 33 | class ConsoleObserver: 34 | def __init__(self, inventory): 35 | self.inventory = inventory 36 | 37 | def __call__(self): 38 | print(self.inventory.product) 39 | print(self.inventory.quantity) 40 | -------------------------------------------------------------------------------- /Chapter22/log_calls_decorator.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | 4 | def log_calls(func): 5 | def wrapper(*args, **kwargs): 6 | now = time.time() 7 | print( 8 | "Calling {0} with {1} and {2}".format( 9 | func.__name__, args, kwargs 10 | ) 11 | ) 12 | return_value = func(*args, **kwargs) 13 | print( 14 | "Executed {0} in {1}ms".format( 15 | func.__name__, time.time() - now 16 | ) 17 | ) 18 | return return_value 19 | 20 | return wrapper 21 | 22 | 23 | def test1(a, b, c): 24 | print("\ttest1 called") 25 | 26 | 27 | def test2(a, b): 28 | print("\ttest2 called") 29 | 30 | 31 | def test3(a, b): 32 | print("\ttest3 called") 33 | time.sleep(1) 34 | 35 | 36 | test1 = log_calls(test1) 37 | test2 = log_calls(test2) 38 | test3 = log_calls(test3) 39 | 40 | test1(1, 2, 3) 41 | test2(4, b=5) 42 | test3(6, 7) 43 | 44 | -------------------------------------------------------------------------------- /Chapter22/singleton_state.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 | 15 | class FirstTag: 16 | def process(self, remaining_string, parser): 17 | i_start_tag = remaining_string.find("<") 18 | i_end_tag = remaining_string.find(">") 19 | tag_name = remaining_string[i_start_tag + 1 : i_end_tag] 20 | root = Node(tag_name) 21 | parser.root = parser.current_node = root 22 | parser.state = child_node 23 | return remaining_string[i_end_tag + 1 :] 24 | 25 | 26 | class ChildNode: 27 | def process(self, remaining_string, parser): 28 | stripped = remaining_string.strip() 29 | if stripped.startswith("") 42 | tag_name = remaining_string[i_start_tag + 1 : i_end_tag] 43 | node = Node(tag_name, parser.current_node) 44 | parser.current_node.children.append(node) 45 | parser.current_node = node 46 | parser.state = child_node 47 | return remaining_string[i_end_tag + 1 :] 48 | 49 | 50 | class TextNode: 51 | def process(self, remaining_string, parser): 52 | i_start_tag = remaining_string.find("<") 53 | text = remaining_string[:i_start_tag] 54 | parser.current_node.text = text 55 | parser.state = child_node 56 | return remaining_string[i_start_tag:] 57 | 58 | 59 | class CloseTag: 60 | def process(self, remaining_string, parser): 61 | i_start_tag = remaining_string.find("<") 62 | i_end_tag = remaining_string.find(">") 63 | assert remaining_string[i_start_tag + 1] == "/" 64 | tag_name = remaining_string[i_start_tag + 2 : i_end_tag] 65 | assert tag_name == parser.current_node.tag_name 66 | parser.current_node = parser.current_node.parent 67 | parser.state = child_node 68 | return remaining_string[i_end_tag + 1 :].strip() 69 | 70 | 71 | first_tag = FirstTag() 72 | child_node = ChildNode() 73 | text_node = TextNode() 74 | open_tag = OpenTag() 75 | close_tag = CloseTag() 76 | -------------------------------------------------------------------------------- /Chapter22/socket_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 | -------------------------------------------------------------------------------- /Chapter22/socket_decorator.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import gzip 3 | from io import BytesIO 4 | 5 | 6 | class GzipSocket: 7 | def __init__(self, socket): 8 | self.socket = socket 9 | 10 | def send(self, data): 11 | buf = BytesIO() 12 | zipfile = gzip.GzipFile(fileobj=buf, mode="w") 13 | zipfile.write(data) 14 | zipfile.close() 15 | self.socket.send(buf.getvalue()) 16 | 17 | def close(self): 18 | self.socket.close() 19 | 20 | 21 | class LogSocket: 22 | def __init__(self, socket): 23 | self.socket = socket 24 | 25 | def send(self, data): 26 | print( 27 | "Sending {0} to {1}".format( 28 | data, self.socket.getpeername()[0] 29 | ) 30 | ) 31 | self.socket.send(data) 32 | 33 | def close(self): 34 | self.socket.close() 35 | 36 | 37 | def respond(client): 38 | response = input("Enter a value: ") 39 | client.send(bytes(response, "utf8")) 40 | client.close() 41 | 42 | 43 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 44 | server.bind(("localhost", 2401)) 45 | server.listen(1) 46 | try: 47 | while True: 48 | client, addr = server.accept() 49 | # respond(client) # No decorator 50 | respond(LogSocket(client)) # One decorator 51 | finally: 52 | server.close() 53 | 54 | -------------------------------------------------------------------------------- /Chapter22/tiling_strategy.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 zip(out_img.size, in_img.size) 10 | ] 11 | for x in range(num_tiles[0]): 12 | for y in range(num_tiles[1]): 13 | out_img.paste( 14 | in_img, 15 | ( 16 | in_img.size[0] * x, 17 | in_img.size[1] * y, 18 | in_img.size[0] * (x + 1), 19 | in_img.size[1] * (y + 1), 20 | ), 21 | ) 22 | return out_img 23 | 24 | 25 | class CenteredStrategy: 26 | def make_background(self, img_file, desktop_size): 27 | in_img = Image.open(img_file) 28 | out_img = Image.new("RGB", desktop_size) 29 | left = (out_img.size[0] - in_img.size[0]) // 2 30 | top = (out_img.size[1] - in_img.size[1]) // 2 31 | out_img.paste( 32 | in_img, 33 | (left, top, left + in_img.size[0], top + in_img.size[1]), 34 | ) 35 | return out_img 36 | 37 | 38 | class ScaledStrategy: 39 | def make_background(self, img_file, desktop_size): 40 | in_img = Image.open(img_file) 41 | out_img = in_img.resize(desktop_size) 42 | return out_img 43 | -------------------------------------------------------------------------------- /Chapter22/xml_example.xml: -------------------------------------------------------------------------------- 1 | 2 | Dusty Phillips 3 | Packt Publishing 4 | Python 3 Object Oriented Programming 5 | 6 | 7 | 1 8 | Object Oriented Design 9 | 10 | 11 | 2 12 | Objects In Python 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /Chapter22/xml_state_parser.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 | 15 | class FirstTag: 16 | def process(self, remaining_string, parser): 17 | i_start_tag = remaining_string.find("<") 18 | i_end_tag = remaining_string.find(">") 19 | tag_name = remaining_string[i_start_tag + 1 : i_end_tag] 20 | root = Node(tag_name) 21 | parser.root = parser.current_node = root 22 | parser.state = ChildNode() 23 | return remaining_string[i_end_tag + 1 :] 24 | 25 | 26 | class ChildNode: 27 | def process(self, remaining_string, parser): 28 | stripped = remaining_string.strip() 29 | if stripped.startswith("") 42 | tag_name = remaining_string[i_start_tag + 1 : i_end_tag] 43 | node = Node(tag_name, parser.current_node) 44 | parser.current_node.children.append(node) 45 | parser.current_node = node 46 | parser.state = ChildNode() 47 | return remaining_string[i_end_tag + 1 :] 48 | 49 | 50 | class CloseTag: 51 | def process(self, remaining_string, parser): 52 | i_start_tag = remaining_string.find("<") 53 | i_end_tag = remaining_string.find(">") 54 | assert remaining_string[i_start_tag + 1] == "/" 55 | tag_name = remaining_string[i_start_tag + 2 : i_end_tag] 56 | assert tag_name == parser.current_node.tag_name 57 | parser.current_node = parser.current_node.parent 58 | parser.state = ChildNode() 59 | return remaining_string[i_end_tag + 1 :].strip() 60 | 61 | 62 | class TextNode: 63 | def process(self, remaining_string, parser): 64 | i_start_tag = remaining_string.find("<") 65 | text = remaining_string[:i_start_tag] 66 | parser.current_node.text = text 67 | parser.state = ChildNode() 68 | return remaining_string[i_start_tag:] 69 | 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 = FirstTag() 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 | 88 | if __name__ == "__main__": 89 | import sys 90 | 91 | with open(sys.argv[1]) as file: 92 | contents = file.read() 93 | p = Parser(contents) 94 | p.start() 95 | 96 | nodes = [p.root] 97 | while nodes: 98 | node = nodes.pop(0) 99 | print(node) 100 | nodes = node.children + nodes 101 | -------------------------------------------------------------------------------- /Chapter23/age_calculator_adapter.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | 3 | 4 | class AgeCalculator: 5 | def __init__(self, birthday): 6 | self.year, self.month, self.day = ( 7 | int(x) for x in birthday.split("-") 8 | ) 9 | 10 | def calculate_age(self, date): 11 | year, month, day = (int(x) for x in date.split("-")) 12 | age = year - self.year 13 | if (month, day) < (self.month, self.day): 14 | age -= 1 15 | return age 16 | 17 | 18 | class DateAgeAdapter: 19 | def _str_date(self, date): 20 | return date.strftime("%Y-%m-%d") 21 | 22 | def __init__(self, birthday): 23 | birthday = self._str_date(birthday) 24 | self.calculator = AgeCalculator(birthday) 25 | 26 | def get_age(self, date): 27 | date = self._str_date(date) 28 | return self.calculator.calculate_age(date) 29 | 30 | 31 | class AgeableDate(datetime.date): 32 | def split(self, char): 33 | return self.year, self.month, self.day 34 | -------------------------------------------------------------------------------- /Chapter23/car_flyweight.py: -------------------------------------------------------------------------------- 1 | import weakref 2 | 3 | 4 | class CarModel: 5 | _models = weakref.WeakValueDictionary() 6 | 7 | def __new__(cls, model_name, *args, **kwargs): 8 | model = cls._models.get(model_name) 9 | if not model: 10 | model = super().__new__(cls) 11 | cls._models[model_name] = model 12 | 13 | return model 14 | 15 | def __init__( 16 | self, 17 | model_name, 18 | air=False, 19 | tilt=False, 20 | cruise_control=False, 21 | power_locks=False, 22 | alloy_wheels=False, 23 | usb_charger=False, 24 | ): 25 | if not hasattr(self, "initted"): 26 | self.model_name = model_name 27 | self.air = air 28 | self.tilt = tilt 29 | self.cruise_control = cruise_control 30 | self.power_locks = power_locks 31 | self.alloy_wheels = alloy_wheels 32 | self.usb_charger = usb_charger 33 | self.initted = True 34 | 35 | def check_serial(self, serial_number): 36 | print( 37 | "Sorry, we are unable to check " 38 | "the serial number {0} on the {1} " 39 | "at this time".format(serial_number, self.model_name) 40 | ) 41 | 42 | 43 | class Car: 44 | def __init__(self, model, color, serial): 45 | self.model = model 46 | self.color = color 47 | self.serial = serial 48 | 49 | def check_serial(self): 50 | return self.model.check_serial(self.serial) 51 | -------------------------------------------------------------------------------- /Chapter23/email_facade.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | import imaplib 3 | 4 | 5 | class EmailFacade: 6 | def __init__(self, host, username, password): 7 | self.host = host 8 | self.username = username 9 | self.password = password 10 | 11 | def send_email(self, to_email, subject, message): 12 | if not "@" in self.username: 13 | from_email = "{0}@{1}".format(self.username, self.host) 14 | else: 15 | from_email = self.username 16 | message = ( 17 | "From: {0}\r\n" "To: {1}\r\n" "Subject: {2}\r\n\r\n{3}" 18 | ).format(from_email, to_email, subject, message) 19 | 20 | smtp = smtplib.SMTP(self.host) 21 | smtp.login(self.username, self.password) 22 | smtp.sendmail(from_email, [to_email], message) 23 | 24 | def get_inbox(self): 25 | mailbox = imaplib.IMAP4(self.host) 26 | mailbox.login( 27 | bytes(self.username, "utf8"), bytes(self.password, "utf8") 28 | ) 29 | mailbox.select() 30 | x, data = mailbox.search(None, "ALL") 31 | messages = [] 32 | for num in data[0].split(): 33 | x, message = mailbox.fetch(num, "(RFC822)") 34 | messages.append(message[0][1]) 35 | return messages 36 | -------------------------------------------------------------------------------- /Chapter23/folder_composite.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 | 15 | class Folder(Component): 16 | def __init__(self, name): 17 | super().__init__(name) 18 | self.children = {} 19 | 20 | def add_child(self, child): 21 | child.parent = self 22 | self.children[child.name] = child 23 | 24 | def copy(self, new_path): 25 | pass 26 | 27 | 28 | class File(Component): 29 | def __init__(self, name, contents): 30 | super().__init__(name) 31 | self.contents = contents 32 | 33 | def copy(self, new_path): 34 | pass 35 | 36 | 37 | root = Folder("") 38 | 39 | 40 | def get_path(path): 41 | names = path.split("/")[1:] 42 | node = root 43 | for name in names: 44 | node = node.children[name] 45 | return node 46 | -------------------------------------------------------------------------------- /Chapter23/formatter_factory.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 | 10 | class USADateFormatter: 11 | def format_date(self, y, m, d): 12 | y, m, d = (str(x) for x in (y, m, d)) 13 | y = "20" + y if len(y) == 2 else y 14 | m = "0" + m if len(m) == 1 else m 15 | d = "0" + d if len(d) == 1 else d 16 | return "{0}-{1}-{2}".format(m, d, y) 17 | 18 | 19 | class FranceCurrencyFormatter: 20 | def format_currency(self, base, cents): 21 | base, cents = (str(x) for x in (base, cents)) 22 | if len(cents) == 0: 23 | cents = "00" 24 | elif len(cents) == 1: 25 | cents = "0" + cents 26 | 27 | digits = [] 28 | for i, c in enumerate(reversed(base)): 29 | if i and not i % 3: 30 | digits.append(" ") 31 | digits.append(c) 32 | base = "".join(reversed(digits)) 33 | return "{0}€{1}".format(base, cents) 34 | 35 | 36 | class USACurrencyFormatter: 37 | def format_currency(self, base, cents): 38 | base, cents = (str(x) for x in (base, cents)) 39 | if len(cents) == 0: 40 | cents = "00" 41 | elif len(cents) == 1: 42 | cents = "0" + cents 43 | digits = [] 44 | for i, c in enumerate(reversed(base)): 45 | if i and not i % 3: 46 | digits.append(",") 47 | digits.append(c) 48 | base = "".join(reversed(digits)) 49 | return "${0}.{1}".format(base, cents) 50 | 51 | 52 | class USAFormatterFactory: 53 | def create_date_formatter(self): 54 | return USADateFormatter() 55 | 56 | def create_currency_formatter(self): 57 | return USACurrencyFormatter() 58 | 59 | 60 | class FranceFormatterFactory: 61 | def create_date_formatter(self): 62 | return FranceDateFormatter() 63 | 64 | def create_currency_formatter(self): 65 | return FranceCurrencyFormatter() 66 | 67 | 68 | country_code = "US" 69 | factory_map = {"US": USAFormatterFactory, "FR": FranceFormatterFactory} 70 | formatter_factory = factory_map.get(country_code)() 71 | -------------------------------------------------------------------------------- /Chapter23/pythonic_window_command.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class Window: 5 | def exit(self): 6 | sys.exit(0) 7 | 8 | 9 | class MenuItem: 10 | def click(self): 11 | self.command() 12 | 13 | 14 | window = Window() 15 | menu_item = MenuItem() 16 | menu_item.command = window.exit 17 | 18 | 19 | class Document: 20 | def __init__(self, filename): 21 | self.filename = filename 22 | self.contents = "This file cannot be modified" 23 | 24 | def save(self): 25 | with open(self.filename, "w") as file: 26 | file.write(self.contents) 27 | 28 | 29 | class KeyboardShortcut: 30 | def keypress(self): 31 | self.command() 32 | 33 | 34 | class SaveCommand: 35 | def __init__(self, document): 36 | self.document = document 37 | 38 | def __call__(self): 39 | self.document.save() 40 | 41 | 42 | document = Document("a_file.txt") 43 | shortcut = KeyboardShortcut() 44 | save_command = SaveCommand(document) 45 | shortcut.command = save_command 46 | -------------------------------------------------------------------------------- /Chapter23/window_command.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | class Window: 5 | def exit(self): 6 | sys.exit(0) 7 | 8 | 9 | class Document: 10 | def __init__(self, filename): 11 | self.filename = filename 12 | self.contents = "This file cannot be modified" 13 | 14 | def save(self): 15 | with open(self.filename, "w") as file: 16 | file.write(self.contents) 17 | 18 | 19 | class ToolbarButton: 20 | def __init__(self, name, iconname): 21 | self.name = name 22 | self.iconname = iconname 23 | 24 | def click(self): 25 | self.command.execute() 26 | 27 | 28 | class MenuItem: 29 | def __init__(self, menu_name, menuitem_name): 30 | self.menu = menu_name 31 | self.item = menuitem_name 32 | 33 | def click(self): 34 | self.command.execute() 35 | 36 | 37 | class KeyboardShortcut: 38 | def __init__(self, key, modifier): 39 | self.key = key 40 | self.modifier = modifier 41 | 42 | def keypress(self): 43 | self.command.execute() 44 | 45 | 46 | class SaveCommand: 47 | def __init__(self, document): 48 | self.document = document 49 | 50 | def execute(self): 51 | self.document.save() 52 | 53 | 54 | class ExitCommand: 55 | def __init__(self, window): 56 | self.window = window 57 | 58 | def execute(self): 59 | self.window.exit() 60 | 61 | 62 | window = Window() 63 | document = Document("a_document.txt") 64 | save = SaveCommand(document) 65 | exit = ExitCommand(window) 66 | 67 | save_button = ToolbarButton("save", "save.png") 68 | save_button.command = save 69 | save_keystroke = KeyboardShortcut("s", "ctrl") 70 | save_keystroke.command = save 71 | exit_menu = MenuItem("File", "Exit") 72 | exit_menu.command = exit 73 | -------------------------------------------------------------------------------- /Chapter24/Case Study_ Vigenère cipher/test_vigenere.py: -------------------------------------------------------------------------------- 1 | from vigenere_cipher import ( 2 | VigenereCipher, 3 | combine_character, 4 | separate_character, 5 | ) 6 | 7 | 8 | def test_encode(): 9 | cipher = VigenereCipher("TRAIN") 10 | encoded = cipher.encode("ENCODEDINPYTHON") 11 | assert encoded == "XECWQXUIVCRKHWA" 12 | 13 | 14 | def test_encode_character(): 15 | cipher = VigenereCipher("TRAIN") 16 | encoded = cipher.encode("E") 17 | assert encoded == "X" 18 | 19 | 20 | def test_encode_spaces(): 21 | cipher = VigenereCipher("TRAIN") 22 | encoded = cipher.encode("ENCODED IN PYTHON") 23 | assert encoded == "XECWQXUIVCRKHWA" 24 | 25 | 26 | def test_encode_lowercase(): 27 | cipher = VigenereCipher("TRain") 28 | encoded = cipher.encode("encoded in Python") 29 | assert encoded == "XECWQXUIVCRKHWA" 30 | 31 | 32 | def test_combine_character(): 33 | assert combine_character("E", "T") == "X" 34 | assert combine_character("N", "R") == "E" 35 | 36 | 37 | def test_extend_keyword(): 38 | cipher = VigenereCipher("TRAIN") 39 | extended = cipher.extend_keyword(16) 40 | assert extended == "TRAINTRAINTRAINT" 41 | extended = cipher.extend_keyword(15) 42 | assert extended == "TRAINTRAINTRAIN" 43 | 44 | 45 | def test_separate_character(): 46 | assert separate_character("X", "T") == "E" 47 | assert separate_character("E", "R") == "N" 48 | 49 | 50 | def test_decode(): 51 | cipher = VigenereCipher("TRAIN") 52 | decoded = cipher.decode("XECWQXUIVCRKHWA") 53 | assert decoded == "ENCODEDINPYTHON" 54 | -------------------------------------------------------------------------------- /Chapter24/Case Study_ Vigenère cipher/vigenere_cipher.py: -------------------------------------------------------------------------------- 1 | class VigenereCipher: 2 | def __init__(self, keyword): 3 | self.keyword = keyword 4 | 5 | def _code(self, text, combine_func): 6 | text = text.replace(" ", "").upper() 7 | combined = [] 8 | keyword = self.extend_keyword(len(text)) 9 | for p, k in zip(text, keyword): 10 | combined.append(combine_func(p, k)) 11 | return "".join(combined) 12 | 13 | def encode(self, plaintext): 14 | return self._code(plaintext, combine_character) 15 | 16 | def decode(self, ciphertext): 17 | return self._code(ciphertext, separate_character) 18 | 19 | def extend_keyword(self, number): 20 | repeats = number // len(self.keyword) + 1 21 | return (self.keyword * repeats)[:number] 22 | 23 | 24 | def combine_character(plain, keyword): 25 | plain = plain.upper() 26 | keyword = keyword.upper() 27 | plain_num = ord(plain) - ord("A") 28 | keyword_num = ord(keyword) - ord("A") 29 | return chr(ord("A") + (plain_num + keyword_num) % 26) 30 | 31 | 32 | def separate_character(cypher, keyword): 33 | cypher = cypher.upper() 34 | keyword = keyword.upper() 35 | cypher_num = ord(cypher) - ord("A") 36 | keyword_num = ord(keyword) - ord("A") 37 | return chr(ord("A") + (cypher_num - keyword_num) % 26) 38 | 39 | -------------------------------------------------------------------------------- /Chapter24/average_raises.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | def average(seq): 5 | return sum(seq) / len(seq) 6 | 7 | 8 | class TestAverage(unittest.TestCase): 9 | def test_zero(self): 10 | self.assertRaises(ZeroDivisionError, average, []) 11 | 12 | def test_with_zero(self): 13 | with self.assertRaises(ZeroDivisionError): 14 | average([]) 15 | 16 | 17 | if __name__ == "__main__": 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /Chapter24/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 | while True: 9 | client, address = s.accept() 10 | data = client.recv(1024) 11 | client.send(data) 12 | client.close() 13 | -------------------------------------------------------------------------------- /Chapter24/first_unittest.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | class CheckNumbers(unittest.TestCase): 5 | def test_int_float(self): 6 | self.assertEqual(1, 1.0) 7 | 8 | def test_str_float(self): 9 | self.assertEqual(1, "1") 10 | 11 | 12 | if __name__ == "__main__": 13 | unittest.main() 14 | -------------------------------------------------------------------------------- /Chapter24/flight_status_redis.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import redis 3 | 4 | 5 | class FlightStatusTracker: 6 | ALLOWED_STATUSES = {"CANCELLED", "DELAYED", "ON TIME"} 7 | 8 | def __init__(self): 9 | self.redis = redis.StrictRedis() 10 | 11 | def change_status(self, flight, status): 12 | status = status.upper() 13 | if status not in self.ALLOWED_STATUSES: 14 | raise ValueError("{} is not a valid status".format(status)) 15 | 16 | key = "flightno:{}".format(flight) 17 | value = "{}|{}".format( 18 | datetime.datetime.now().isoformat(), status 19 | ) 20 | self.redis.set(key, value) 21 | -------------------------------------------------------------------------------- /Chapter24/stats.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | 4 | class StatsList(list): 5 | def mean(self): 6 | return sum(self) / len(self) 7 | 8 | def median(self): 9 | if len(self) % 2: 10 | return self[int(len(self) / 2)] 11 | else: 12 | idx = int(len(self) / 2) 13 | return (self[idx] + self[idx - 1]) / 2 14 | 15 | def mode(self): 16 | freqs = defaultdict(int) 17 | for item in self: 18 | freqs[item] += 1 19 | mode_freq = max(freqs.values()) 20 | modes = [] 21 | for item, value in freqs.items(): 22 | if value == mode_freq: 23 | modes.append(item) 24 | return modes 25 | -------------------------------------------------------------------------------- /Chapter24/stats_list_setup.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | 4 | class StatsList(list): 5 | def mean(self): 6 | return sum(self) / len(self) 7 | 8 | def median(self): 9 | if len(self) % 2: 10 | return self[int(len(self) / 2)] 11 | else: 12 | idx = int(len(self) / 2) 13 | return (self[idx] + self[idx - 1]) / 2 14 | 15 | def mode(self): 16 | freqs = defaultdict(int) 17 | for item in self: 18 | freqs[item] += 1 19 | mode_freq = max(freqs.values()) 20 | modes = [] 21 | for item, value in freqs.items(): 22 | if value == mode_freq: 23 | modes.append(item) 24 | return modes 25 | -------------------------------------------------------------------------------- /Chapter24/test_echo_server.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import socket 3 | import time 4 | import pytest 5 | 6 | 7 | @pytest.fixture(scope="session") 8 | def echoserver(): 9 | print("loading server") 10 | p = subprocess.Popen(["python3", "echo_server.py"]) 11 | time.sleep(1) 12 | yield p 13 | p.terminate() 14 | 15 | 16 | @pytest.fixture 17 | def clientsocket(request): 18 | s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 19 | s.connect(("localhost", 1028)) 20 | yield s 21 | s.close() 22 | 23 | 24 | def test_echo(echoserver, clientsocket): 25 | clientsocket.send(b"abc") 26 | assert clientsocket.recv(3) == b"abc" 27 | 28 | 29 | def test_echo2(echoserver, clientsocket): 30 | clientsocket.send(b"def") 31 | assert clientsocket.recv(3) == b"def" 32 | 33 | -------------------------------------------------------------------------------- /Chapter24/test_flight_status.py: -------------------------------------------------------------------------------- 1 | from flight_status_redis import FlightStatusTracker 2 | from unittest.mock import Mock, patch 3 | import datetime 4 | import pytest 5 | 6 | 7 | @pytest.fixture 8 | def tracker(): 9 | return FlightStatusTracker() 10 | 11 | 12 | def test_mock_method(tracker): 13 | tracker.redis.set = Mock() 14 | with pytest.raises(ValueError) as ex: 15 | tracker.change_status("AC101", "lost") 16 | assert ex.value.args[0] == "LOST is not a valid status" 17 | assert tracker.redis.set.call_count == 0 18 | 19 | 20 | def test_patch(tracker): 21 | tracker.redis.set = Mock() 22 | fake_now = datetime.datetime(2015, 4, 1) 23 | with patch("datetime.datetime") as dt: 24 | dt.now.return_value = fake_now 25 | tracker.change_status("AC102", "on time") 26 | dt.now.assert_called_once_with() 27 | tracker.redis.set.assert_called_once_with( 28 | "flightno:AC102", "2015-04-01T00:00:00|ON TIME" 29 | ) 30 | 31 | -------------------------------------------------------------------------------- /Chapter24/test_pytest_cleanup.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | import tempfile 3 | import shutil 4 | import os.path 5 | 6 | 7 | @pytest.fixture 8 | def temp_dir(request): 9 | dir = tempfile.mkdtemp() 10 | print(dir) 11 | yield dir 12 | shutil.rmtree(dir) 13 | 14 | 15 | def test_osfiles(temp_dir): 16 | os.mkdir(os.path.join(temp_dir, "a")) 17 | os.mkdir(os.path.join(temp_dir, "b")) 18 | dir_contents = os.listdir(temp_dir) 19 | assert len(dir_contents) == 2 20 | assert "a" in dir_contents 21 | assert "b" in dir_contents 22 | 23 | -------------------------------------------------------------------------------- /Chapter24/test_pytest_setups.py: -------------------------------------------------------------------------------- 1 | def setup_module(module): 2 | print("setting up MODULE {0}".format(module.__name__)) 3 | 4 | 5 | def teardown_module(module): 6 | print("tearing down MODULE {0}".format(module.__name__)) 7 | 8 | 9 | def test_a_function(): 10 | print("RUNNING TEST FUNCTION") 11 | 12 | 13 | class BaseTest: 14 | def setup_class(cls): 15 | print("setting up CLASS {0}".format(cls.__name__)) 16 | 17 | def teardown_class(cls): 18 | print("tearing down CLASS {0}\n".format(cls.__name__)) 19 | 20 | def setup_method(self, method): 21 | print("setting up METHOD {0}".format(method.__name__)) 22 | 23 | def teardown_method(self, method): 24 | print("tearing down METHOD {0}".format(method.__name__)) 25 | 26 | 27 | class TestClass1(BaseTest): 28 | def test_method_1(self): 29 | print("RUNNING METHOD 1-1") 30 | 31 | def test_method_2(self): 32 | print("RUNNING METHOD 1-2") 33 | 34 | 35 | class TestClass2(BaseTest): 36 | def test_method_1(self): 37 | print("RUNNING METHOD 2-1") 38 | 39 | def test_method_2(self): 40 | print("RUNNING METHOD 2-2") 41 | -------------------------------------------------------------------------------- /Chapter24/test_pytest_skipping.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import pytest 3 | 4 | 5 | def test_simple_skip(): 6 | if sys.platform != "fakeos": 7 | pytest.skip("Test works only on fakeOS") 8 | 9 | fakeos.do_something_fake() 10 | assert fakeos.did_not_happen 11 | 12 | 13 | @pytest.mark.skipif("sys.version_info <= (3,0)") 14 | def test_python3(): 15 | assert b"hello".decode() == "hello" 16 | -------------------------------------------------------------------------------- /Chapter24/test_pytest_stats.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | from stats import StatsList 3 | 4 | 5 | @pytest.fixture 6 | def valid_stats(): 7 | return StatsList([1, 2, 2, 3, 3, 4]) 8 | 9 | 10 | def test_mean(valid_stats): 11 | assert valid_stats.mean() == 2.5 12 | 13 | 14 | def test_median(valid_stats): 15 | assert valid_stats.median() == 2.5 16 | valid_stats.append(4) 17 | assert valid_stats.median() == 3 18 | 19 | 20 | def test_mode(valid_stats): 21 | assert valid_stats.mode() == [2, 3] 22 | valid_stats.remove(2) 23 | assert valid_stats.mode() == [3] 24 | -------------------------------------------------------------------------------- /Chapter24/test_skipping.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import sys 3 | 4 | 5 | class SkipTests(unittest.TestCase): 6 | @unittest.expectedFailure 7 | def test_fails(self): 8 | self.assertEqual(False, True) 9 | 10 | @unittest.skip("Test is useless") 11 | def test_skip(self): 12 | self.assertEqual(False, True) 13 | 14 | @unittest.skipIf(sys.version_info.minor == 7, "broken on 3.7") 15 | def test_skipif(self): 16 | self.assertEqual(False, True) 17 | 18 | @unittest.skipUnless( 19 | sys.platform.startswith("linux"), "broken unless on linux" 20 | ) 21 | def test_skipunless(self): 22 | self.assertEqual(False, True) 23 | 24 | 25 | if __name__ == "__main__": 26 | unittest.main() 27 | -------------------------------------------------------------------------------- /Chapter24/test_statslist_setup.py: -------------------------------------------------------------------------------- 1 | from stats import StatsList 2 | import unittest 3 | 4 | 5 | class TestValidInputs(unittest.TestCase): 6 | def setUp(self): 7 | self.stats = StatsList([1, 2, 2, 3, 3, 4]) 8 | 9 | def test_mean(self): 10 | self.assertEqual(self.stats.mean(), 2.5) 11 | 12 | def test_median(self): 13 | self.assertEqual(self.stats.median(), 2.5) 14 | self.stats.append(4) 15 | self.assertEqual(self.stats.median(), 3) 16 | 17 | def test_mode(self): 18 | self.assertEqual(self.stats.mode(), [2, 3]) 19 | self.stats.remove(2) 20 | self.assertEqual(self.stats.mode(), [3]) 21 | 22 | 23 | if __name__ == "__main__": 24 | unittest.main() 25 | -------------------------------------------------------------------------------- /Chapter24/test_with_pytest.py: -------------------------------------------------------------------------------- 1 | def test_int_float(): 2 | assert 1 == 1.0 3 | 4 | 5 | class TestNumbers: 6 | def test_int_float(self): 7 | assert 1 == 1.0 8 | 9 | def test_int_str(self): 10 | assert 1 == "1" 11 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | # Getting Started with Python 5 | Understand key data structures and use Python in object-oriented programming 6 | 7 | This learning path helps you get comfortable in the world of Python. It starts with a thorough and practical introduction to Python. You'll quickly start writing programs in the first part of the learning path. With the power of linked lists, binary searches, and sorting algorithms, you'll easily create complex data structures, such as graphs, stacks, and queues. After understanding cooperative inheritance, you'll expertly raise, handle, and manipulate exceptions. You will effortlessly integrate the object-oriented and not-so-object-oriented aspects of Python, and create maintainable applications using higher level design patterns. Once you've covered the core topics, you’ll 8 | understand the joy of unit testing and just how easy it is to create unit tests. 9 | 10 | By the end of this learning path, you will have built components that are easy to understand, debug, and can be used across different applications. 11 | 12 | This learning path includes content from the following Packt products: 13 | * [Learn Python Programming - Second Edition by Fabrizio Romano](https://www.packtpub.com/application-development/learn-python-programming-second-edition) 14 | * [Python Data Structures and Algorithms by Benjamin Baka](https://www.packtpub.com/application-development/python-data-structures-and-algorithms) 15 | * [Python 3 Object-Oriented Programming by Dusty Phillips](https://www.packtpub.com/application-development/python-3-object-oriented-programming-third-edition) 16 | 17 | ## Who this book is for 18 | If you are relatively new to coding and want to write scripts or programs to accomplish tasks using Python, or if you are an object-oriented programmer for other languages and seeking a leg up in the world of Python, then this learning path is for you. Though not essential, it will help you to have basic knowledge of programming and OOP. 19 | 20 | ## To get the most out of this book 21 | The code in this book will require you to run Python 2.7.x or higher. Python's default interactive environment can also be used to run the snippets of code. Some of the examples in this book rely on third-party libraries that do not ship with Python. They are introduced within the book at the time they are used, so you do not need to install them in advance. 22 | ### Download a free PDF 23 | 24 | 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.
25 |

https://packt.link/free-ebook/9781838551919

--------------------------------------------------------------------------------