├── code ├── ch_00_welcome │ └── placeholder.txt ├── ch_99_conclusion │ └── placeholder.txt ├── ch_06_packages │ ├── requirements.txt │ ├── _01_beware_the_trade_imbalance_support.py │ ├── _02_what_is___main__support.py │ ├── _03_what_do_you_require.py │ ├── _02_what_is___main__.py │ └── _01_beware_the_trade_imbalance.py ├── ch_10_for_humans │ ├── demo_db.sqlite │ ├── _02_just_write_sql_support.py │ ├── _02_just_write_sql.py │ └── _01_official_http_client.py ├── ch_04_collections │ ├── slicing_db.sqlite │ ├── _05_generators_all_the_way_down.py │ ├── _01_adding_iteration.py │ ├── _02_containment.py │ ├── _07_counting_generators.py │ ├── _03_slice.py │ ├── _04_generators.py │ ├── _03_slice_support.py │ └── _06_express_yourself.py ├── ch_09_tuples │ ├── _02_short_swap_tricks.py │ ├── _01_unpack_and_move_in.py │ ├── _03_methods_with_ref_params.py │ └── _04_i_m_not_a_number_i_have_a_name.py ├── ch_02_foundations │ ├── _4_randomness.py │ ├── _06_state_your_state.py │ ├── _02_noneness.py │ ├── _07_flat_support_file.py │ ├── _05_stringification.py │ ├── _01_truthiness.py │ ├── _07_flat_is_better_than_nested.py │ └── _03_multipe_compares.py ├── ch_05_functions │ ├── _03_no_overloading.py │ ├── _05_variable_length_arguments.py │ ├── _04_i_ll_have_my_usual.py │ ├── _06_the_key_to_the_arguments.py │ ├── _02_lets_play_catch_support.py │ ├── _07_dangerous_defaults.py │ ├── _01_lambdas.py │ └── _02_lets_play_catch.py ├── ch_08_loops │ ├── _02_oh_there_are_num_loops_rn.py │ ├── _03_oh_there_are_num_loops_en.py │ ├── _04_dont_use_else_or_else.py │ └── _01_there_is_no_num_for_loop.py ├── ch_07_classes │ ├── _02_i_want_my_privacy.py │ ├── _01_built_in_one_place.py │ └── _03_what_properties_define_you.py ├── ch_03_dictionaries │ ├── _06_to_json_and_back.py │ ├── _04_get_some.py │ ├── _02_merge_ahead.py │ ├── _05_there_is_no_switch.py │ ├── _03_mem_hacking.py │ └── _01_perf.py └── ch_01_pep8 │ └── _01_pep8.py ├── tips-pdf └── 26-pythonic-code-tips-and-tricks.pdf ├── readme_resources └── pythonic-course-welcome-video.png ├── .idea ├── dictionaries │ └── screencaster.xml ├── dataSources.local.xml ├── dataSources.xml └── dataSources.ids ├── transcripts ├── 01-introduction │ ├── 3.txt │ ├── 4.txt │ ├── 6.txt │ ├── 5.txt │ ├── 2.txt │ └── 1.txt ├── 12-conclusion │ ├── 1.txt │ ├── 3.txt │ └── 4.txt ├── 10-tuples │ ├── 2.txt │ ├── 3.txt │ ├── 1.txt │ └── 4.txt ├── 06-methods │ ├── 1.txt │ ├── 6.txt │ ├── 4.txt │ ├── 7.txt │ └── 5.txt ├── 09-loops │ ├── 2.txt │ ├── 3.txt │ ├── 1.txt │ └── 4.txt ├── 04-dictionaries │ ├── 1.txt │ └── 7.txt ├── 07-modules-packages │ ├── 1.txt │ ├── 4.txt │ ├── 5.txt │ ├── 3.txt │ └── 2.txt ├── 02-PEP8 │ ├── 1.txt │ ├── 4.txt │ ├── 3.txt │ ├── 5.txt │ └── 2.txt ├── 03-Foundational-Concepts │ ├── 4.txt │ ├── 2.txt │ ├── 6.txt │ └── 7.txt ├── 05-Generators │ ├── 2.txt │ ├── 5.txt │ ├── 1.txt │ └── 7.txt ├── 11-for-humans │ ├── 1.txt │ ├── 3.txt │ └── 2.txt └── 08-classes │ ├── 3.txt │ └── 1.txt ├── LICENSE ├── .gitignore ├── venv_on_windows.md └── README.md /code/ch_00_welcome/placeholder.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code/ch_99_conclusion/placeholder.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /code/ch_06_packages/requirements.txt: -------------------------------------------------------------------------------- 1 | requests 2 | records 3 | passlib -------------------------------------------------------------------------------- /code/ch_10_for_humans/demo_db.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkpython/write-pythonic-code-demos/HEAD/code/ch_10_for_humans/demo_db.sqlite -------------------------------------------------------------------------------- /code/ch_04_collections/slicing_db.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkpython/write-pythonic-code-demos/HEAD/code/ch_04_collections/slicing_db.sqlite -------------------------------------------------------------------------------- /tips-pdf/26-pythonic-code-tips-and-tricks.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkpython/write-pythonic-code-demos/HEAD/tips-pdf/26-pythonic-code-tips-and-tricks.pdf -------------------------------------------------------------------------------- /readme_resources/pythonic-course-welcome-video.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/talkpython/write-pythonic-code-demos/HEAD/readme_resources/pythonic-course-welcome-video.png -------------------------------------------------------------------------------- /code/ch_10_for_humans/_02_just_write_sql_support.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | db_file = os.path.join(os.path.dirname(__file__), 'demo_db.sqlite') 4 | conn_str = 'sqlite:///' + db_file 5 | -------------------------------------------------------------------------------- /code/ch_06_packages/_01_beware_the_trade_imbalance_support.py: -------------------------------------------------------------------------------- 1 | def mode(*args): 2 | if not args: 3 | print("Mode set to DEFAULT") 4 | return 5 | 6 | print("Mode set to ADVANCED") 7 | -------------------------------------------------------------------------------- /code/ch_09_tuples/_02_short_swap_tricks.py: -------------------------------------------------------------------------------- 1 | x = 7 2 | y = 11 3 | 4 | print("x={}, y={}".format(x, y)) 5 | 6 | # swap: nonpythonic 7 | # temp = x 8 | # x = y 9 | # y = temp 10 | 11 | y, x = x, y 12 | 13 | print("x={}, y={}".format(x, y)) 14 | -------------------------------------------------------------------------------- /code/ch_02_foundations/_4_randomness.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | letters = "abcdefghijklmnopqrstuvwxyz1234567890" 4 | 5 | # BAD: C-style 6 | index = random.randint(0, len(letters)-1) 7 | item = letters[index] 8 | print(item) 9 | 10 | # Pythonic version 11 | item = random.choice(letters) 12 | print(item) 13 | 14 | 15 | -------------------------------------------------------------------------------- /code/ch_05_functions/_03_no_overloading.py: -------------------------------------------------------------------------------- 1 | # noinspection PyMethodMayBeStatic 2 | class Sample: 3 | def simple(self): 4 | print("simple") 5 | 6 | def simple(self, details): 7 | print("Simple with details: {}".format(details)) 8 | 9 | s = Sample() 10 | s.simple("Some details") 11 | s.simple() 12 | -------------------------------------------------------------------------------- /code/ch_06_packages/_02_what_is___main__support.py: -------------------------------------------------------------------------------- 1 | def a_method(): 2 | pass 3 | 4 | 5 | class AClass: 6 | pass 7 | 8 | 9 | var = "A Variable" 10 | 11 | print("Support library name: {}".format(__name__)) 12 | 13 | if __name__ == '__main__': 14 | age = 0 15 | while age <= 0: 16 | age = int(input("How old are you? ")) 17 | -------------------------------------------------------------------------------- /.idea/dictionaries/screencaster.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | composible 5 | github 6 | mkennedy 7 | multithreaded 8 | nonpythonic 9 | performant 10 | truthiness 11 | 12 | 13 | -------------------------------------------------------------------------------- /code/ch_08_loops/_02_oh_there_are_num_loops_rn.py: -------------------------------------------------------------------------------- 1 | # but sometimes, fairly rarely, you really need just an 2 | # increasing number of integers. 3 | 4 | for i in range(0, 10): 5 | print(i, end=', ') 6 | print() 7 | 8 | print(range(1, 7)) 9 | 10 | # think of the above as: 11 | # for i = 0; i<10; i++: 12 | # print(i, end=', ') 13 | 14 | # So, use range to model for int loops 15 | -------------------------------------------------------------------------------- /code/ch_10_for_humans/_02_just_write_sql.py: -------------------------------------------------------------------------------- 1 | import records 2 | # noinspection PyProtectedMember 3 | from ch_10_for_humans._02_just_write_sql_support import conn_str 4 | 5 | print(conn_str) 6 | 7 | db = records.Database(conn_str) 8 | rows = db.query("SELECT * FROM Measurement WHERE value > .9 " 9 | "ORDER BY value DESC") 10 | 11 | for r in rows[:5]: 12 | print(r) 13 | -------------------------------------------------------------------------------- /code/ch_06_packages/_03_what_do_you_require.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import records 3 | import passlib 4 | 5 | r = requests.get("http://google.com") 6 | print(r.status_code) 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | r = requests 38 | r = records 39 | p = passlib -------------------------------------------------------------------------------- /code/ch_08_loops/_03_oh_there_are_num_loops_en.py: -------------------------------------------------------------------------------- 1 | # More often, you need the index and the value. 2 | 3 | # How would you actually accomplish this? With range? No. 4 | # for idx = 0; idx {}".format(idx, val)) 7 | 8 | data = [1, 7, 11] 9 | 10 | for idx, value in enumerate(data): 11 | print(" {} --> {}".format(idx+1, value)) 12 | -------------------------------------------------------------------------------- /code/ch_05_functions/_05_variable_length_arguments.py: -------------------------------------------------------------------------------- 1 | def biggest(x, y): 2 | if x > y: 3 | return x 4 | else: 5 | return y 6 | 7 | 8 | print(biggest(1, 7)) 9 | 10 | 11 | # what about more than 2? 12 | def biggest(x, *args): 13 | big = x 14 | for y in args: 15 | if y > big: 16 | big = y 17 | return big 18 | 19 | 20 | print(biggest(1, 7, 42, 99, -2, 11)) 21 | -------------------------------------------------------------------------------- /transcripts/01-introduction/3.txt: -------------------------------------------------------------------------------- 1 | 0:01 We are going to write a lot of code in this course. 2 | 0:02 And you want to download it and play with it 3 | 0:04 and experiment with it and so on 4 | 0:06 so of course I am going to put this into a GitHub repository, 5 | 0:09 here you can see github.com/mikeckennedy/write-pythonic-code-demos 6 | 0:14 so I recommend that you go out there 7 | 0:16 and star this so you have it as a reference. -------------------------------------------------------------------------------- /transcripts/01-introduction/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 Are you brand new to Python? 2 | 0:02 Well this course assumes that you have some basic knowledge 3 | 0:04 on how to create functions, classes, loops, those types of things, 4 | 0:07 and we focus really on the best way to do those. 5 | 0:09 So if you are brand new to Python, 6 | 0:11 I recommend you check out my course 7 | 0:13 Python Jumpstart By Building Ten Applications 8 | 0:14 and you can find that at talkpython.fm/course -------------------------------------------------------------------------------- /code/ch_06_packages/_02_what_is___main__.py: -------------------------------------------------------------------------------- 1 | print("About to import support lib") 2 | # noinspection PyProtectedMember 3 | import ch_06_packages._02_what_is___main__support as s 4 | print("Done importing support lib") 5 | 6 | def main(): 7 | print() 8 | print("The variable value is {}.".format(s.var)) 9 | # print("The name of the executed python module is {}".format(__name__)) 10 | 11 | print("Main app name: {}".format(__name__)) 12 | 13 | if __name__ == '__main__': 14 | main() 15 | -------------------------------------------------------------------------------- /code/ch_08_loops/_04_dont_use_else_or_else.py: -------------------------------------------------------------------------------- 1 | print("Running through the while: ", end='') 2 | count = 0 3 | while count < 5: 4 | print('.', end='') 5 | count += 1 6 | else: 7 | print("In the else clause of the while loop.") 8 | print() 9 | 10 | print("Breaking out of the while: ", end='') 11 | count = 0 12 | while count < 5: 13 | print('.', end='') 14 | count += 1 15 | if count > 3: 16 | break 17 | else: 18 | print("In the else clause of the early break loop.") 19 | print() 20 | -------------------------------------------------------------------------------- /transcripts/12-conclusion/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Congratulations, you've made it to the end of the course. 2 | 0:04 You've completed over 50 examples contrasting non-Pythonic code 3 | 0:07 with idiomatic Pythonic code. 4 | 0:11 I hope you've learned a ton, 5 | 0:13 I hope you have a new appreciation for some of the idioms built into Python, 6 | 0:17 why we use them, what their advantages are 7 | 0:19 and how you can apply them to your apps. 8 | 0:21 The ones you've already built 9 | 0:22 and the ones that you are going to build in the future. -------------------------------------------------------------------------------- /code/ch_02_foundations/_06_state_your_state.py: -------------------------------------------------------------------------------- 1 | import time 2 | import sys 3 | 4 | 5 | def main(): 6 | confirm = input("Are you sure you want to format drive C: [yes, NO]? ") 7 | if not confirm or confirm.lower() != 'yes': 8 | print("Format cancelled!") 9 | sys.exit(1) 10 | 11 | for _ in range(40): 12 | time.sleep(.15) 13 | print('.', end='') 14 | sys.stdout.flush() 15 | print() 16 | print("Format completed successful. Enjoy the new hard drive space.") 17 | 18 | main() 19 | 20 | -------------------------------------------------------------------------------- /code/ch_06_packages/_01_beware_the_trade_imbalance.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os import path 3 | import statistics as stats 4 | from statistics import median, mean 5 | 6 | # noinspection PyProtectedMember 7 | from ch_06_packages._01_beware_the_trade_imbalance_support import mode 8 | 9 | print("Current version: {}".format(sys.version_info.major)) 10 | print(path.abspath('.')) 11 | print(stats.median([1, 1, 1, 5, 8, 9, 100])) 12 | print(median([1, 1, 1, 5, 8, 9, 100])) 13 | print(mean([1, 1, 1, 5, 8, 9, 100])) 14 | print(mode([42, 7, 7, 1])) 15 | -------------------------------------------------------------------------------- /code/ch_10_for_humans/_01_official_http_client.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | # from: http://www.omdbapi.com/ 4 | title_text = input("Enter a title search string: ") 5 | url = 'http://www.omdbapi.com/?y=&plot=short&r=json&s={}'.format(title_text) 6 | 7 | # process json-> Search -> Title 8 | resp = requests.get(url) 9 | if resp.status_code != 200: 10 | print("Whoa, status code unexpected! {}".format(resp.status_code)) 11 | else: 12 | data = resp.json() 13 | search = data['Search'] 14 | for m in search: 15 | print("* {}".format(m['Title'])) 16 | -------------------------------------------------------------------------------- /code/ch_05_functions/_04_i_ll_have_my_usual.py: -------------------------------------------------------------------------------- 1 | # start with no defaults... 2 | def display_greeting(name, greeting='Hello', times=1): 3 | times = max(1, times) 4 | for _ in range(0, times): 5 | print("{} {}!".format(greeting, name)) 6 | 7 | display_greeting("Jeff", 'Good morning', 3) 8 | display_greeting("Michael", "G'day", 1) 9 | 10 | display_greeting("Mark") 11 | display_greeting("Mark", "Good afternoon") 12 | display_greeting("Mark", "Good afternoon", 2) 13 | 14 | display_greeting(greeting='Yo!', name='Michael', times=4) 15 | display_greeting('Michael', times=2) 16 | 17 | -------------------------------------------------------------------------------- /code/ch_08_loops/_01_there_is_no_num_for_loop.py: -------------------------------------------------------------------------------- 1 | # There is no such concept in Python: 2 | 3 | # data = [1, 7, 11] 4 | # for i = 0; i {}".format(idx, item)) 28 | -------------------------------------------------------------------------------- /code/ch_02_foundations/_07_flat_support_file.py: -------------------------------------------------------------------------------- 1 | def check_network(): 2 | return True 3 | 4 | 5 | def check_download_url(): 6 | return True 7 | 8 | 9 | def check_access_allowed(): 10 | return True 11 | 12 | 13 | def check_dns(): 14 | return True 15 | 16 | 17 | def download_file(): 18 | if not check_network(): 19 | raise ConnectionResetError("Cannot connect to network") 20 | if not check_download_url(): 21 | raise ValueError("Invalid URL") 22 | if not check_access_allowed(): 23 | raise PermissionError("Cannot access resource (permission denied)") 24 | if not check_dns(): 25 | raise ConnectionError("No DNS") 26 | 27 | return ['c', 'o', 'o', 'l'] 28 | -------------------------------------------------------------------------------- /code/ch_05_functions/_02_lets_play_catch_support.py: -------------------------------------------------------------------------------- 1 | def check_network(): 2 | return True 3 | 4 | 5 | def check_download_url(): 6 | return True 7 | 8 | 9 | def check_access_allowed(): 10 | return True 11 | 12 | 13 | def check_dns(): 14 | return False 15 | 16 | 17 | def download_file(): 18 | if not check_network(): 19 | raise ConnectionResetError("Cannot connect to network") 20 | if not check_download_url(): 21 | raise ValueError("Invalid URL") 22 | if not check_access_allowed(): 23 | raise PermissionError("Cannot access resource (permission denied)") 24 | if not check_dns(): 25 | raise ConnectionError("No DNS") 26 | 27 | return ['c', 'o', 'o', 'l'] 28 | -------------------------------------------------------------------------------- /transcripts/12-conclusion/3.txt: -------------------------------------------------------------------------------- 1 | 0:01 So I hope that you've saved the source code from this class, 2 | 0:03 you can see at "github.com/mikeckenendy/write-pythonic-code-demos", 3 | 0:08 we have all the code you saw me write during all the videos, 4 | 0:12 so please take this moment, go over there and star that repository, 5 | 0:15 maybe fork it, maybe download it, or at least bookmark it in your browser 6 | 0:19 so that you can come back to it, 7 | 0:21 it should be here forever but you never know, right. 8 | 0:23 Also, I want to encourage you to play with these ideas, 9 | 0:26 and if having the source code to start from 10 | 0:28 helps you play with those ideas in your own code, in your own project, 11 | 0:30 then please do so. -------------------------------------------------------------------------------- /code/ch_04_collections/_05_generators_all_the_way_down.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def main(): 5 | root_dir = '/Users/mkennedy/github/talk-python/courses/jumpstart-demos/transcripts' 6 | 7 | files = get_files(root_dir) 8 | print("Found these files") 9 | for f in files: 10 | print(f) 11 | print('done') 12 | 13 | 14 | def get_files(folder): 15 | for item in os.listdir(folder): 16 | 17 | full_item = os.path.join(folder, item) 18 | # print(full_item) 19 | if os.path.isfile(full_item): 20 | yield full_item 21 | elif os.path.isdir(full_item): 22 | # for f in get_files(full_item): 23 | # yield f 24 | yield from get_files(full_item) 25 | 26 | 27 | if __name__ == '__main__': 28 | main() 29 | -------------------------------------------------------------------------------- /code/ch_04_collections/_01_adding_iteration.py: -------------------------------------------------------------------------------- 1 | class ShoppingCart: 2 | def __init__(self): 3 | self.items = [] 4 | 5 | def add_item(self, it): 6 | self.items.append(it) 7 | 8 | def __iter__(self): 9 | sorted_items = sorted(self.items, key=lambda i: -i.price) 10 | return sorted_items.__iter__() 11 | # for i in sorted_items: 12 | # yield i 13 | 14 | 15 | class CartItem: 16 | def __init__(self, name, price): 17 | self.price = price 18 | self.name = name 19 | 20 | 21 | cart = ShoppingCart() 22 | cart.add_item(CartItem("guitar", 799)) 23 | cart.add_item(CartItem("cd", 19)) 24 | cart.add_item(CartItem("iPhone", 699)) 25 | 26 | # Can we for-in the cart? 27 | # what if it was to be sorted? 28 | 29 | print("Items in your cart.") 30 | for item in cart: 31 | print(" * {} ${}".format(item.name, item.price)) 32 | -------------------------------------------------------------------------------- /code/ch_09_tuples/_03_methods_with_ref_params.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | 4 | def main(): 5 | args = list() 6 | out_params_bad(7, args) 7 | print("Return values (bad): {} & {:.2f}".format(args[0], args[1])) 8 | 9 | # can we do better? 10 | v1, v2 = out_params(7) 11 | print("Return values (good): {} & {:.2f}".format(v1, v2)) 12 | 13 | 14 | # pythonic! 15 | def out_params(base: float): 16 | r1 = base * base 17 | r2 = math.sqrt(base * base * base) 18 | 19 | return r1, r2 20 | 21 | # non-pythonic! 22 | def out_params_bad(base: float, args: list): 23 | if len(args) == 0: 24 | args.append(0) 25 | args.append(0) 26 | 27 | if len(args) != 2: 28 | raise Exception("Need to return values") 29 | 30 | args[0] = base * base 31 | args[1] = math.sqrt(base * base * base) 32 | 33 | 34 | if __name__ == '__main__': 35 | main() 36 | -------------------------------------------------------------------------------- /code/ch_04_collections/_02_containment.py: -------------------------------------------------------------------------------- 1 | nums_list = [1, 1, 2, 3, 5, 8, 13, 21, 34] 2 | nums_set = {1, 1, 2, 3, 5, 8, 13, 21, 34} 3 | nums_dict = {1: "one", 2: "two", 3: "three", 5: "five"} 4 | 5 | print("List: {}".format(nums_list)) 6 | print("Set: {}".format(nums_set)) 7 | print("Dict: {}".format(nums_dict)) 8 | 9 | n = int(input("Enter a number to test for small fibonacci: ")) 10 | 11 | # is this number in the sequences above? 12 | print("{} in list".format(n) if n in nums_list else "not in list") 13 | print("{} in set".format(n) if n in nums_set else "not in set") 14 | print("{} in dict".format(n) if n in nums_dict else "not in dict") 15 | 16 | text = "Why did the multithreaded chicken cross the street? Other side to the get." 17 | 18 | word = input("Enter word to search text for...") 19 | # if word in text: 20 | if text.find(word) >= 0: 21 | print("{} is in {}".format(word, text)) 22 | else: 23 | print("{} is NOT in {}".format(word, text)) 24 | -------------------------------------------------------------------------------- /code/ch_02_foundations/_05_stringification.py: -------------------------------------------------------------------------------- 1 | name = 'Michael' 2 | age = 43 3 | 4 | # Create the string "Hi, I'm Michael and I'm 43 years old." 5 | 6 | # crash: print("Hi, I'm " + name + " and I'm " + age + " years old.") 7 | 8 | # works, but not pythonic 9 | print("Hi, I'm " + name + " and I'm " + str(age) + " years old.") 10 | 11 | 12 | # probably pythonic 13 | print("Hi, I'm %s and I'm %d years old." % (name, age)) 14 | 15 | # pythonic 16 | print("Hi, I'm {} and I'm {} years old.".format(name, age)) 17 | print("Hi, I'm {1} years old and my name is {0}, yeah {1}.".format(name, age)) 18 | 19 | data = {'day': 'Saturday', 'office': 'Home office', 'other': 'UNUSED'} 20 | # print: On Saturday I was working in my Home office! 21 | print("On {day} I was working in my {office}!".format(**data)) 22 | 23 | # In Python 3.6 24 | print("Hi, I'm {name} and I'm {age} years old.".format(name=name, age=age)) 25 | # print(f"Hi, I'm {name} and I'm {age} years old.") 26 | 27 | -------------------------------------------------------------------------------- /code/ch_09_tuples/_04_i_m_not_a_number_i_have_a_name.py: -------------------------------------------------------------------------------- 1 | import collections 2 | 3 | Rating = collections.namedtuple("Rating", "id, x, y, rating") 4 | 5 | 6 | def main(): 7 | # for d in get_data_tricky(): 8 | # print("id={}, rating={}, position=({}, {})".format( 9 | # d[0], d[3], d[2], d[1])) 10 | 11 | for d in get_data_better(): 12 | print("id={}, rating={}, position=({}, {})".format( 13 | d.id, d.rating, d.x, d.y)) 14 | 15 | _, x, y, _ = d 16 | print(x, y) 17 | 18 | 19 | def get_data_better(): 20 | data = [ 21 | Rating(1, 19.2, 11.1, 50), 22 | Rating(2, 18.9, 12.0, 45), 23 | Rating(3, 20.1, 14.0, 55), 24 | ] 25 | 26 | return data 27 | 28 | 29 | def get_data_tricky(): 30 | data = [ 31 | (1, 19.2, 11.1, 50), 32 | (2, 18.9, 12.0, 45), 33 | (3, 20.1, 14.0, 55), 34 | ] 35 | 36 | return data 37 | 38 | 39 | if __name__ == '__main__': 40 | main() 41 | -------------------------------------------------------------------------------- /.idea/dataSources.local.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | *: 8 | 9 | 10 | 11 | 12 | *: 13 | 14 | 15 | -------------------------------------------------------------------------------- /code/ch_04_collections/_07_counting_generators.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import uuid 3 | 4 | Measurement = collections.namedtuple('Measurement', 'id x y value') 5 | 6 | measurements = [ 7 | Measurement(str(uuid.uuid4()), 1, 1, 72), 8 | Measurement(str(uuid.uuid4()), 2, 1, 40), 9 | Measurement(str(uuid.uuid4()), 3, 1, 11), 10 | Measurement(str(uuid.uuid4()), 2, 1, 90), 11 | Measurement(str(uuid.uuid4()), 2, 2, 60), 12 | Measurement(str(uuid.uuid4()), 2, 3, 73), 13 | Measurement(str(uuid.uuid4()), 3, 1, 40), 14 | Measurement(str(uuid.uuid4()), 3, 2, 44), 15 | Measurement(str(uuid.uuid4()), 3, 3, 90) 16 | ] 17 | 18 | # generator expression 19 | high_values = ( 20 | m.value 21 | for m in measurements 22 | if m.value >= 70 23 | ) 24 | 25 | # crash! no len() 26 | # print(len(high_values)) 27 | 28 | # could use a list, but expensive! 29 | # lst = list(high_values) 30 | # print(len(lst)) 31 | 32 | # pythonic counting! 33 | count = sum(1 for _ in high_values) 34 | print(count) 35 | -------------------------------------------------------------------------------- /code/ch_05_functions/_07_dangerous_defaults.py: -------------------------------------------------------------------------------- 1 | def main(): 2 | a = add_items_bad("a", 3) 3 | print(a) 4 | add_items_bad("b", 2, a) 5 | print(a) 6 | 7 | # danger revealed here 8 | d = add_items_bad("d", 4) 9 | print(d) 10 | 11 | print(id(a), id(d), id(a) == id(d)) 12 | 13 | print("Try again") 14 | a = add_items_good("a", 3) 15 | print(a) 16 | add_items_good("b", 2, a) 17 | print(a) 18 | 19 | # danger revealed here 20 | d = add_items_good("d", 4) 21 | print(d) 22 | 23 | print(id(a), id(d), id(a) == id(d)) 24 | 25 | 26 | # notice even PyCharm knows... alt-enter fixes. 27 | def add_items_bad(name, times=1, lst=[]): 28 | for _ in range(0, times): 29 | lst.append(name) 30 | return lst 31 | 32 | 33 | # Ah, better 34 | def add_items_good(name, times=1, lst=None): 35 | if lst is None: 36 | lst = [] 37 | 38 | for _ in range(0, times): 39 | lst.append(name) 40 | return lst 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /code/ch_03_dictionaries/_04_get_some.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | data = {"year": 2001, "country": "USA", "title": "Johnny 5", "duration": "119 min"} 4 | 5 | print('optimistic style') 6 | print(data['year']) 7 | # print(data['rating']) # Crash! 8 | 9 | print() 10 | print('pessimistic style') 11 | try: 12 | print(data['year']) 13 | print(data['rating']) 14 | except Exception as x: 15 | print("Oops! {}".format(x)) 16 | 17 | 18 | print() 19 | print('safety first style') 20 | if 'year' in data: 21 | print(data['year']) 22 | if 'rating' in data: 23 | print(data['rating']) 24 | else: 25 | print("Oh we didn't find a rating...") 26 | print() 27 | 28 | print('accept None instead style') 29 | print(data.get('year')) 30 | print(data.get('rating')) 31 | print() 32 | 33 | print('Explicit alternate value style') 34 | print(data.get('year', 0)) 35 | print(data.get('rating', '***')) 36 | print() 37 | 38 | data = defaultdict(lambda: "MISSING", data) 39 | print('accept default value instead style') 40 | print(data['year']) 41 | print(data['rating']) 42 | print() 43 | -------------------------------------------------------------------------------- /code/ch_02_foundations/_01_truthiness.py: -------------------------------------------------------------------------------- 1 | # ############################### 2 | # Truthiness of an element: 3 | # ############################### 4 | 5 | 6 | def print_truthiness(msg, exp): 7 | print(("TRUE" if exp else "FALSE") + " <-- " + msg) 8 | 9 | 10 | print_truthiness("Testing True", True) 11 | print_truthiness("Testing False", False) 12 | # for sequences 13 | 14 | seq = [] 15 | print_truthiness("Empty list", seq) 16 | seq.append("The cat") 17 | print_truthiness("1 item list", seq) 18 | 19 | # for objects and numbers 20 | print_truthiness("Zero", 0) 21 | print_truthiness("Eleven", 11) 22 | print_truthiness("-Eleven", -11) 23 | 24 | # for None 25 | print_truthiness("For none", None) 26 | 27 | 28 | # custom types 29 | class AClass: 30 | def __init__(self): 31 | self.data = [] 32 | 33 | def add(self, item): 34 | self.data.append(item) 35 | 36 | def __bool__(self): 37 | return True if self.data else False 38 | 39 | a = AClass() 40 | 41 | print_truthiness("Empty AClass", a) 42 | a.add("Thing") 43 | print_truthiness("nonempty AClass", a) 44 | 45 | 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Michael Kennedy 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 | -------------------------------------------------------------------------------- /transcripts/12-conclusion/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 If this dive into "what is Pythonic code", 2 | 0:03 what are the idioms and the best ways of working in Python is interesting to you, 3 | 0:07 I encourage you to check on my podcast "Talk Python To Me"; 4 | 0:10 on "Talk Python To Me", I interview many of the great developers in the Python community, 5 | 0:14 and our conversations often get around to things that touch on or hint at Pythonic ideas. 6 | 0:20 We've talked about SQLAlchemy, and I've had Mike Bayer on the show 7 | 0:23 who is the creator and maintainer of SQLALchemy, 8 | 0:25 we talked about Python For Humans, all done by Kenneth Reitz, 9 | 0:28 he was on the show talking about Requests and APIs and so on, 10 | 0:32 and that just gives you a sense of how you can dig much deeper in these topics, 11 | 0:35 so if this resonates with you, go check out the podcast. 12 | 0:39 And before you leave, I want to take the chance to say "thank you, 13 | 0:42 thank you for having the faith in me to buy my class, 14 | 0:44 thank you for taking the time and spend it with me 15 | 0:47 to make it all the way to the end." 16 | 0:48 I hope you fond it really valuable and I'd love to connect with you online or offline, 17 | 0:53 thanks again. -------------------------------------------------------------------------------- /code/ch_04_collections/_03_slice.py: -------------------------------------------------------------------------------- 1 | # noinspection PyProtectedMember 2 | from ch_04_collections._03_slice_support import session_factory, Measurement 3 | 4 | 5 | def main(): 6 | nums = fibonacci(200) 7 | 8 | print("All nums") 9 | print(nums) 10 | 11 | print("First 5 nums") 12 | # first_five = nums[0:5] 13 | first_five = nums[:5] 14 | print(first_five) 15 | 16 | print("2->7 nums") 17 | print(nums[2:8]) 18 | 19 | print("Last 3 nums (less good) with len") 20 | # print(nums[len(nums)-3:len(nums)]) 21 | print(nums[len(nums)-3:]) 22 | 23 | print("Last 3 nums (pythonic)") 24 | print(nums[-3:]) 25 | 26 | print("Top measurements from the database") 27 | session = session_factory() 28 | query = session.query(Measurement). \ 29 | filter(Measurement.value > .9). \ 30 | order_by(Measurement.value.desc()) 31 | 32 | print([m.value for m in query[:3]]) 33 | 34 | session.close() 35 | 36 | 37 | def fibonacci(limit): 38 | numbers = [] 39 | current, nxt = 0, 1 40 | 41 | while current < limit: 42 | current, nxt = nxt, nxt + current 43 | numbers.append(current) 44 | 45 | return numbers 46 | 47 | 48 | if __name__ == '__main__': 49 | main() 50 | -------------------------------------------------------------------------------- /code/ch_02_foundations/_07_flat_is_better_than_nested.py: -------------------------------------------------------------------------------- 1 | # noinspection PyProtectedMember 2 | import _07_flat_support_file as s 3 | 4 | 5 | def main(): 6 | download_nested() 7 | download_flat() 8 | 9 | 10 | def download_flat(): 11 | print("Let's try to download a file") 12 | if not s.check_download_url(): 13 | print("Bad url") 14 | return 15 | 16 | if not s.check_network(): 17 | print("No network") 18 | return 19 | 20 | if not s.check_dns(): 21 | print("No DNS") 22 | return 23 | if not s.check_access_allowed(): 24 | print("No access") 25 | return 26 | 27 | print("Sweet, we can download ...") 28 | 29 | 30 | def download_nested(): 31 | print("Let's try to download a file") 32 | if s.check_download_url(): 33 | if s.check_network(): 34 | if s.check_dns(): 35 | if s.check_access_allowed(): 36 | print("Sweet, we can download ...") 37 | else: 38 | print("No access") 39 | else: 40 | print("No DNS") 41 | else: 42 | print("No network") 43 | else: 44 | print("Bad url") 45 | 46 | 47 | if __name__ == '__main__': 48 | main() 49 | -------------------------------------------------------------------------------- /code/ch_07_classes/_03_what_properties_define_you.py: -------------------------------------------------------------------------------- 1 | class NotSoPythonicPet: 2 | def __init__(self, name, age): 3 | self.age = age 4 | self.name = name 5 | 6 | def get_name(self): 7 | return self.name 8 | 9 | def get_age(self): 10 | return self.age 11 | 12 | 13 | print("Here is my pet cow:") 14 | cow = NotSoPythonicPet("Betsy", 4) 15 | print("She is named {} and {} years old.".format(cow.get_name(), cow.get_age())) 16 | print() 17 | 18 | 19 | class PetSnake: 20 | def __init__(self, name, age): 21 | self.__age = age 22 | self.__name = name 23 | self._protected_val = 2 24 | 25 | @property 26 | def is_protected(self): 27 | return self._protected_val > 5 28 | 29 | @property 30 | def name(self): 31 | return self.__name 32 | 33 | @property 34 | def age(self): 35 | print("---> Getting current age") 36 | return self.__age 37 | 38 | @age.setter 39 | def age(self, value): 40 | print("---> Setting age to {}".format(value)) 41 | self.__age = value 42 | 43 | 44 | print("Here is my pet snake:") 45 | py = PetSnake("Slide", 6) 46 | print("She is named {} and {} years old.".format(py.name, py.age)) 47 | py.age = 7 48 | print("She is named {} and {} years old.".format(py.name, py.age)) 49 | print(py.is_protected) -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | # PyInstaller 28 | # Usually these files are written by a python script from a template 29 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 30 | *.manifest 31 | *.spec 32 | 33 | # Installer logs 34 | pip-log.txt 35 | pip-delete-this-directory.txt 36 | 37 | # Unit test / coverage reports 38 | htmlcov/ 39 | .tox/ 40 | .coverage 41 | .coverage.* 42 | .cache 43 | nosetests.xml 44 | coverage.xml 45 | *,cover 46 | .hypothesis/ 47 | 48 | # Translations 49 | *.mo 50 | *.pot 51 | 52 | # Django stuff: 53 | *.log 54 | 55 | # Sphinx documentation 56 | docs/_build/ 57 | 58 | # PyBuilder 59 | target/ 60 | 61 | #Ipython Notebook 62 | .ipynb_checkpoints 63 | .idea/encodings.xml 64 | .idea/misc.xml 65 | .idea/modules.xml 66 | .idea/pythonic-demos.iml 67 | .idea/vcs.xml 68 | .DS_Store 69 | .env 70 | .idea/workspace.xml 71 | .idea/dataSources/35b5cf00-25b0-486c-ad0e-a4924f786a9c.xml 72 | .idea/dataSources/8bb4bd1d-6254-49eb-8c86-6e28d7023c1b.xml 73 | .idea/inspectionProfiles/Project_Default.xml 74 | -------------------------------------------------------------------------------- /code/ch_05_functions/_01_lambdas.py: -------------------------------------------------------------------------------- 1 | # ############ lambda expressions ############# 2 | # Create by Michael Kennedy (@mkennedy) 3 | 4 | 5 | def main(): 6 | print(type(check_for_odd), check_for_odd) 7 | print("Find odd numbers via method:") 8 | for n in find_special_numbers(check_for_odd, 25): 9 | print(n, end=',') 10 | print() 11 | 12 | print("Find divisible by 6 via lambda:") 13 | for n in find_special_numbers(lambda i: i % 6 == 0, 25): 14 | print(n, end=',') 15 | print() 16 | 17 | print("Sorted list of words: ") 18 | list_of_words = ['CPython', 'read', 'improvements,', 'issues.', 'on', 'comprehensive', 'porting', 'potential', 19 | 'user-facing', 'of', 'other', 'for', 'smaller', 'deprecations,', 'a', 'optimizations,', 'changes,', 20 | 'including', 'and', 'Please', 'many', 'list'] 21 | 22 | # list_of_words.sort() # ? ;) 23 | list_of_words.sort(key=lambda w: w.lower()) 24 | print(list_of_words) 25 | 26 | print("Done") 27 | 28 | 29 | def find_special_numbers(special_selector, limit=10): 30 | found = [] 31 | n = 0 32 | while len(found) < limit: 33 | if special_selector(n): 34 | found.append(n) 35 | n += 1 36 | return found 37 | 38 | 39 | def check_for_odd(n): 40 | return n % 2 == 1 41 | 42 | 43 | if __name__ == '__main__': 44 | main() 45 | -------------------------------------------------------------------------------- /transcripts/01-introduction/6.txt: -------------------------------------------------------------------------------- 1 | 0:01 So you can use any editor you would like, 2 | 0:03 however, I am going to use PyCharm. 3 | 0:05 One, because I think PyCharm is the best editor for Python, 4 | 0:08 two- because PyCharm actually detects and warns you 5 | 0:12 and sometimes even automatically corrects errors 6 | 0:15 when you write code that is not Pythonic. 7 | 0:17 This has to do with naming, 8 | 0:19 this has to do with structure, all sorts of cool things. 9 | 0:22 So I am going to be using PyCharm in the videos 10 | 0:24 and I encourage you to get it as well. 11 | 0:26 You can get it at jetbrains.com/pycharm. 12 | 0:29 If we open that in our browser you can see here is the PyCharm page 13 | 0:32 and if we go to download it, you'll see there are actually two versions, 14 | 0:35 there is the PyCharm community edition which is 100% free, 15 | 0:39 and there is the PyCharm professional edition 16 | 0:41 and if you look at the place where the features are missing 17 | 0:44 from the community edition, 18 | 0:46 it's really around things like database, web and profiler information 19 | 0:49 as well as some of their Docker support. 20 | 0:52 So, for this course you should be able to use the community edition, 21 | 0:54 I love this tool, I pay for it so I am going to be using the professional edition. 22 | 0:58 It works great on OS X, on Windows and on Linux. 23 | 1:03 So whatever operating system you use, you should be able to use it. -------------------------------------------------------------------------------- /venv_on_windows.md: -------------------------------------------------------------------------------- 1 | # Working with virtual environments on Windows 2 | 3 | Unfortunately, the windows installer doesn't provide full parity with the richer setup on unix-based systems (namely macOS and Linux). 4 | 5 | Here are a few tips to help. 6 | 7 | ## Checking active versions 8 | 9 | In the course, you see me using the `which` command. Windows has an equivalent command called `where`. 10 | 11 | This has nothing to do with Python's setup of course, but often you need that command so we start here. 12 | 13 | ## Activating 14 | 15 | Once you've created a virtual environment via 16 | 17 | `python -m venv FOLDER_NAME` 18 | 19 | (Make sure this is python 3: `python -V` -> 3...) 20 | 21 | You activate it differently. It's `activate.bat` is in scripts not bin (why?): 22 | 23 | `FOLDER_NAME/scripts/activate.bat` 24 | 25 | ## Enabling `python3` and `pip3` commands 26 | 27 | As noted above, `pip3` and `python3` are commands on unix systems but not windows (why?). 28 | 29 | But you can easily create them. Just create two batch files and put them somewhere that is in your path (e.g. the same folder that contains python.exe for v3?). 30 | 31 | **pip3.bat** 32 | `pip.exe` 33 | 34 | **python3.bat** 35 | `python.exe` 36 | 37 | That will run the local python and pip or the one first in your path depending where you locate the files. 38 | 39 | This may make following along exactly with my commands easier. 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /code/ch_04_collections/_04_generators.py: -------------------------------------------------------------------------------- 1 | # ############ yield and generators ############# 2 | # Create by Michael Kennedy (@mkennedy) 3 | 4 | 5 | # Fibonacci numbers: 6 | # 1, 1, 2, 3, 5, 8, 13, 21, ... 7 | 8 | 9 | def classic_fibonacci(limit): 10 | nums = [] 11 | current, nxt = 0, 1 12 | 13 | while current < limit: 14 | current, nxt = nxt, nxt + current 15 | nums.append(current) 16 | 17 | return nums 18 | 19 | 20 | # can we do better? 21 | def generator_fibonacci(): 22 | current, nxt = 0, 1 23 | 24 | while True: 25 | current, nxt = nxt, nxt + current 26 | yield current 27 | 28 | 29 | # generator are composible: 30 | def even_generator(numbers): 31 | for n in numbers: 32 | if n % 2 == 0: 33 | yield n 34 | 35 | 36 | # consume both generators as a pipeline here 37 | def even_fib(): 38 | for n in even_generator(generator_fibonacci()): 39 | yield n 40 | 41 | if __name__ == '__main__': 42 | 43 | print("Classic") 44 | for m in classic_fibonacci(100): 45 | print(m, end=', ') 46 | print() 47 | 48 | print("generator") 49 | for m in generator_fibonacci(): 50 | print(m, end=', ') 51 | if m > 100: 52 | break 53 | print() 54 | 55 | print("composed") 56 | for m in even_fib(): 57 | print(m, end=', ') 58 | if m > 1000000: 59 | break 60 | print() 61 | -------------------------------------------------------------------------------- /code/ch_04_collections/_03_slice_support.py: -------------------------------------------------------------------------------- 1 | import os 2 | import random 3 | 4 | import sqlalchemy 5 | import sqlalchemy.orm 6 | import sqlalchemy.ext 7 | import sqlalchemy.ext.declarative 8 | 9 | SqlAlchemyBase = sqlalchemy.ext.declarative.declarative_base() 10 | 11 | 12 | class Measurement(SqlAlchemyBase): 13 | __tablename__ = 'Measurement' 14 | 15 | id = sqlalchemy.Column(sqlalchemy.Integer, 16 | primary_key=True, autoincrement=True) 17 | x = sqlalchemy.Column(sqlalchemy.Integer) 18 | y = sqlalchemy.Column(sqlalchemy.Integer) 19 | value = sqlalchemy.Column(sqlalchemy.Float) 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | # run this code only once per process assuming 1 database 33 | db_file = os.path.join( 34 | os.path.dirname(__file__), 35 | 'slicing_db.sqlite') 36 | 37 | conn_str = 'sqlite:///' + db_file 38 | engine = sqlalchemy.create_engine(conn_str, echo=True) 39 | session_factory = sqlalchemy.orm.sessionmaker(bind=engine) 40 | SqlAlchemyBase.metadata.create_all(engine) 41 | 42 | session = session_factory() 43 | 44 | count = session.query(Measurement).count() 45 | if not count: 46 | print("No data, adding test data") 47 | for _ in range(100): 48 | m = Measurement() 49 | m.value = random.random() 50 | m.x = random.randint(0, 100) 51 | m.y = random.randint(0, 100) 52 | session.add(m) 53 | session.commit() 54 | -------------------------------------------------------------------------------- /code/ch_05_functions/_02_lets_play_catch.py: -------------------------------------------------------------------------------- 1 | # noinspection PyProtectedMember 2 | import ch_05_functions._02_lets_play_catch_support as s 3 | 4 | 5 | def main(): 6 | # run_with_checks() 7 | # run_with_handling() 8 | run_with_handling_separate_errors() 9 | 10 | 11 | def run_with_checks(): 12 | if not s.check_network(): 13 | print("Cannot download, no network") 14 | return 15 | if not s.check_dns(): 16 | print("Cannot download, no dns") 17 | return 18 | if not s.check_download_url(): 19 | print("Cannot download, no url set") 20 | return 21 | 22 | data = s.download_file() 23 | print("downloaded data -> {}".format(data)) 24 | 25 | 26 | def run_with_handling(): 27 | try: 28 | data = s.download_file() 29 | print("downloaded data -> {}".format(data)) 30 | except Exception as x: 31 | print("Cannot download: {} -> {}".format(type(x), x)) 32 | 33 | 34 | def run_with_handling_separate_errors(): 35 | try: 36 | data = s.download_file() 37 | print("downloaded data -> {}".format(data)) 38 | except PermissionError: 39 | print("Cannot download, you don't have permission...") 40 | except ConnectionError as ce: 41 | print("Cannot download, problem with network: {}".format(ce)) 42 | except Exception as x: 43 | print("Cannot download: {} -> {}".format(type(x), x)) 44 | 45 | 46 | if __name__ == '__main__': 47 | print("Let's play catch: ") 48 | print() 49 | main() 50 | -------------------------------------------------------------------------------- /code/ch_03_dictionaries/_02_merge_ahead.py: -------------------------------------------------------------------------------- 1 | # ############ merging dictionaries ############# 2 | # Create by Michael Kennedy (@mkennedy) 3 | # 4 | # Overview: 5 | # Often we have multiple dictionaries and want to combine 6 | # them. For example, in Pyramid, we have separate dictionaries 7 | # that hold query string data, route data, and POST data. Merging 8 | # these makes access form data easier. That's just one example. 9 | # 10 | 11 | route = {'id': 271, 'title': 'Fast apps'} 12 | query = {'id': 1, 'render_fast': True} 13 | post = {'email': 'j@j.com', 'name': 'Jeff'} 14 | 15 | print("Individual dictionaries: ") 16 | print("route: {}".format(route)) 17 | print("query: {}".format(query)) 18 | print("post: {}".format(post)) 19 | 20 | # Non-pythonic procedural way 21 | m1 = {} 22 | for k in query: 23 | m1[k] = query[k] 24 | for k in post: 25 | m1[k] = post[k] 26 | for k in route: 27 | m1[k] = route[k] 28 | 29 | # Classic pythonic way: 30 | m2 = query.copy() 31 | m2.update(post) 32 | m2.update(route) 33 | 34 | # Via dictionary comprehensions: 35 | m3 = {k: v for d in [query, post, route] for k, v in d.items()} 36 | 37 | # Python 3.5+ pythonic way, warning crashes on Python <= 3.4: 38 | m4 = {**query, **post, **route} 39 | 40 | print(m1) 41 | print(m2) 42 | print(m3) 43 | print(m4) 44 | 45 | print("Are the same? " + 'yes' if m1 == m2 and m2 == m3 and m3 == m4 else 'no') 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | # m3 = {k: v for d in [query, post, route] for k, v in d.items()} 54 | # m4 = {**query, **post, **route} 55 | -------------------------------------------------------------------------------- /transcripts/10-tuples/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 Let's see the Pythonic way to swap two values and hint: It involves tuples. 2 | 0:05 All right, so here we have two values, "x" and "y", 3 | 0:08 you can see we'll print them out here and we'll print them out there 4 | 0:10 and our attention is to do some sort of swap thing. 5 | 0:13 Let's just run it really quickly. 6 | 0:14 All right, obviously not swapped yet, let's swap them here. 7 | 0:17 In most languages, this is sort of a 3-step process, 8 | 0:20 you'd say something like "temp = x", "x = y", "y = temp". 9 | 0:25 Now if we run this, you'll see they should be swapped, 10 | 0:27 great, 7, 11, 11, 7, but this is non-Pythonic. 11 | 0:34 And let's even teach PyCharm: "Hey, that's a word". 12 | 0:37 OK, so if we are going to do this in a Pythonic way, we are going to use tuples, 13 | 0:42 and it turns out you can do it in a beautiful concise one-liner 14 | 0:45 by temporarily creating a tuple and then unpacking 15 | 0:48 it into the same variables but in reverse, so we can say "y, x = x, y". 16 | 0:54 Remember, the comma here creates a tuple 17 | 0:56 and then the stuff in the left hand side will unpack that tuple 18 | 0:59 back into the values but it unpacks "x" into "y" and unpacks "y" into "x". 19 | 1:03 Beautiful, one line, very Pythonic, let's see if it works. 20 | 1:07 Ta da, same thing, much cleaner. 21 | 1:10 Want to swap two values in Python? Create a tuple 22 | 1:13 and unpack it back into the reversed set of variables, 23 | 1:17 so here we have "x" and "y", we say "y, x = x, y". Swapped, one line, very Pythonic. -------------------------------------------------------------------------------- /code/ch_03_dictionaries/_05_there_is_no_switch.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | def main(): 5 | d_text = input("Which direction [n,s,w,e,nw,ne,sw,se]? ") 6 | m = Moves.parse(d_text) 7 | 8 | print("You chose: {}".format(m)) 9 | 10 | squirrel = Character("Chippy") 11 | squirrel.move(m) 12 | 13 | 14 | class Moves(Enum): 15 | West = 1 16 | North = 2 17 | East = 3 18 | South = 4 19 | NorthEast = 5 20 | SouthEast = 6 21 | NorthWest = 7 22 | SouthWest = 8 23 | 24 | @staticmethod 25 | def parse(text: str): 26 | if not text: 27 | return None 28 | 29 | text = text.strip().lower() 30 | parse_dict = { 31 | 'w': Moves.West, 's': Moves.South, 'e': Moves.East, 'n': Moves.North, 32 | 'nw': Moves.NorthWest, 'ne': Moves.NorthEast, 'sw': Moves.SouthWest, 'se': Moves.SouthEast 33 | } 34 | return parse_dict.get(text) 35 | 36 | 37 | class Character: 38 | def __init__(self, name): 39 | self.name = name 40 | 41 | def move(self, direction: Moves): 42 | action_dict = { 43 | Moves.North: lambda: print("{} moves north with a special hesitation!".format(self.name)), 44 | Moves.South: lambda: print("{} is going south for winter!".format(self.name)) 45 | } 46 | 47 | action = action_dict.get( 48 | direction, 49 | lambda: print("{} moves quickly to {}".format(self.name, direction))) 50 | action() 51 | 52 | 53 | if __name__ == '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /transcripts/06-methods/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Our next category of Pythonic concepts and tips 2 | 0:03 is going to be around functions and methods. 3 | 0:06 Python is a multi-paradigm programming language, 4 | 0:09 we can write procedural code, 5 | 0:10 we could write object-oriented code, 6 | 0:12 we could write functional code and with things like decorators 7 | 0:15 we can even write aspect-oriented code. 8 | 0:17 Python's functional support is very strong, 9 | 0:20 you'll see the functions and methods are first class symbols and citizens in Python 10 | 0:25 and they are highly flexible and powerful. 11 | 0:28 When you define a function or method with a "def" keyword, 12 | 0:32 you're really instantiating a function object and you can pass that object around 13 | 0:37 just like you can pass a number or a string or an instance of a class. 14 | 0:41 You'll see that those function objects can even be changed at runtime 15 | 0:45 you could dynamically add a field to a function 16 | 0:47 just by saying "function.value=something". 17 | 0:50 Not saying that you should do that, but just considering the flexibility, right. 18 | 0:55 Functions even allow you create things called closures, 19 | 0:57 both inline functions as well as lambda expressions, 20 | 1:00 that's a very powerful programming technique often used in places 21 | 1:03 like Javascript for data hiding and encapsulation. 22 | 1:07 And finally with things like lambda expressions 23 | 1:09 we can even define small inline methods as little bits of executable code 24 | 1:14 that we can pass around in super concise and composable ways. -------------------------------------------------------------------------------- /code/ch_04_collections/_06_express_yourself.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import uuid 3 | 4 | Measurement = collections.namedtuple('Measurement', 'id x y value') 5 | 6 | measurements = [ 7 | Measurement(str(uuid.uuid4()), 1, 1, 72), 8 | Measurement(str(uuid.uuid4()), 2, 1, 40), 9 | Measurement(str(uuid.uuid4()), 3, 1, 11), 10 | Measurement(str(uuid.uuid4()), 2, 1, 90), 11 | Measurement(str(uuid.uuid4()), 2, 2, 60), 12 | Measurement(str(uuid.uuid4()), 2, 3, 73), 13 | Measurement(str(uuid.uuid4()), 3, 1, 40), 14 | Measurement(str(uuid.uuid4()), 3, 2, 90), 15 | Measurement(str(uuid.uuid4()), 3, 3, 90) 16 | ] 17 | 18 | # C-style 19 | high_measurements1 = [] 20 | for m in measurements: 21 | if m.value >= 70: 22 | high_measurements1.append(m.value) 23 | 24 | # list of high values via comprehension 25 | high_measurements2 = [ 26 | m.value 27 | for m in measurements 28 | if m.value >= 70 29 | ] 30 | 31 | # via generator expression 32 | high_m_gen = ( 33 | m.value 34 | for m in measurements 35 | if m.value >= 70 36 | ) 37 | print(high_m_gen) 38 | 39 | # process the generator to get something printable. 40 | high_measurements3 = list(high_m_gen) 41 | 42 | # high values lookup dict via comp 43 | high_m_by_id = { 44 | m.id: m.value 45 | for m in measurements 46 | if m.value >= 70 47 | } 48 | 49 | # high values distinct via set 50 | high_values_distinct = { 51 | m.value 52 | for m in measurements 53 | if m.value >= 70 54 | } 55 | 56 | print(high_measurements1) 57 | print(high_measurements2) 58 | print(high_measurements3) 59 | print(high_m_by_id) 60 | print(high_values_distinct) 61 | -------------------------------------------------------------------------------- /.idea/dataSources.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 1eba50fe-c223-4fd0-b2c8-4fb12b6c32bb 6 | true 7 | true 8 | SQLite 9 | org.sqlite.JDBC 10 | jdbc:sqlite:$PROJECT_DIR$/code/ch_04_collections/slicing_db.sqlite 11 | 12 | 13 | file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/xerial-sqlite-license.txt 14 | 15 | 16 | file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/sqlite-jdbc-3.8.11.2.jar 17 | 18 | 19 | 20 | 21 | 1eba50fe-c223-4fd0-b2c8-4fb12b6c32bb 22 | true 23 | true 24 | SQLite 25 | org.sqlite.JDBC 26 | jdbc:sqlite:$PROJECT_DIR$/code/ch_10_for_humans/demo_db.sqlite 27 | 28 | 29 | file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/xerial-sqlite-license.txt 30 | 31 | 32 | file://$APPLICATION_CONFIG_DIR$/jdbc-drivers/sqlite-jdbc-3.8.11.2.jar 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /.idea/dataSources.ids: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 |
27 |
-------------------------------------------------------------------------------- /transcripts/09-loops/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 So it turns out sometimes when you really do just want to 2 | 0:03 work through a sequence from numbers, in order: 1, 2, 3, 4, 3 | 0:06 or maybe even evens for some reason: 0, 2, 4, 6 and so on, 4 | 0:11 so in Python we actually do have a way to do this, 5 | 0:13 so over here imagine we wanted this code that went from 0 up to 10 6 | 0:17 but not including it, what do we do? Remember, 7 | 0:20 there is no "for" loop, so we just say "for i in" 8 | 0:24 and we can create this generator that goes from 0 to 10, 9 | 0:28 and we could even use the step size if we wanted to change 10 | 0:31 the increment there, instead of "+= 1" we would do something else. 11 | 0:35 All right, so this should go from 0 to 9 in the print out. 12 | 0:38 And it does, so here we can use range, 13 | 0:40 now range is considered slightly dangerous for large values here, 14 | 0:44 in Python 2, because if we look at range, 15 | 0:50 like so, it just says range 1 to 7, let's put a new line here, like so, 16 | 0:54 it says just 1 to 7, this is technically a generator, 17 | 0:57 but if we did this in Python 2 18 | 1:06 up here you can see this has just bin/python, 19 | 1:09 which is Python 2, what we'll get at the bottom is no longer a generator, it's a list, 20 | 1:13 so if you put 10 million here instead of using the generator 21 | 1:16 with the yield slowly create this, it actually generates a list of 10 million items. 22 | 1:21 All at once. And then, lets you loop over it. Not amazing, right? 23 | 1:24 but in Python 3 this is no problem, if you want to do this in Python 2, 24 | 1:28 "xrange" is your friend, now you get basically the equivalent of Python 3 "range". 25 | 1:35 Great, so if you really want to step through numbers, 26 | 1:38 just use range, that gives you something that's iterable 27 | 1:41 and you use that in a "for...in" loop. 28 | 1:42 Yeah, so in some sense there is a way to do a numerical "for" loop, 29 | 1:46 it's just not a language construct, it's an idiom. -------------------------------------------------------------------------------- /transcripts/04-dictionaries/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Now we are going to focus on a really important set of topics 2 | 0:03 revolving around dictionaries in Python. 3 | 0:06 So, the first question you might ask is like: 4 | 0:08 "Why should we focus on dictionaries in Pythonic code?" 5 | 0:11 Well, it turns out that dictionaries are everywhere in Python, 6 | 0:15 you'll see that dictionaries are the backing store for many types, 7 | 0:18 so for example when you create a new object from a custom class you've created, 8 | 0:22 every instance has its own backing store 9 | 0:25 which is a dictionary for the fields and whatnot that you add to this class. 10 | 0:30 Dictionaries are isomorphic with JSON, 11 | 0:33 so you'll see that there is basically a one to one mapping 12 | 0:35 between Python dictionaries and JSON 13 | 0:38 which is the web's most important transport type. 14 | 0:41 If we want to create a method, that allows us to use keyword arguments, 15 | 0:45 one of the ways we can do that is to have the kwargs, 16 | 0:48 **kwargs parameter and this allows us to 17 | 0:51 not just pass a set of non-keyword arguments 18 | 0:54 but actually arbitrary ones as well, 19 | 0:56 and those come through as a dictionary. 20 | 0:58 As we'll see, dictionaries add incredible performance boost 21 | 1:02 for certain types of algorithms, 22 | 1:04 and we'll look at that in the section as well. 23 | 1:06 Python, the language does not have a switch statement, 24 | 1:09 and we don't miss it too often but sometimes a switch statement 25 | 1:12 is really nice and you'll see that we can actually leverage dictionaries 26 | 1:16 to add switch statements, 27 | 1:17 switch statement-like functionality to the Python language. 28 | 1:21 If you access a database and you are not using an ORM such is SQLAlchemy, 29 | 1:25 typically the way the rows come back to you are each row comes back 30 | 1:29 either as a tuple or a dictionary, preferably as a dictionary 31 | 1:32 so you can lookup the columns by  name rather than index. 32 | 1:35 So that's just a taste of why dictionaries are so important in Python, 33 | 1:38 you'll see there is a lot of cool Pythonic techniques around working with them, 34 | 1:41 so let's jump right in. -------------------------------------------------------------------------------- /transcripts/07-modules-packages/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Python is often referred to as a programming language and ecosystem 2 | 0:04 with batteries included, and on one level 3 | 0:06 that means it has a really rich standard library. 4 | 0:08 Things like the JSON module that's built-in and so on. 5 | 0:11 It also means looking at the larger ecosystem 6 | 0:14 all the 80 000 packages on PyPi, things like that, 7 | 0:18 so in this section we are going to focus 8 | 0:20 on the Python idioms around packages, modules, 9 | 0:23 importing them, creating them, those kinds of things. 10 | 0:27 I want to give you just a little bit inspiration before we get to the technical details 11 | 0:30 so if I come over here and run Python 3, and I go type in "import", 12 | 0:35 I could say "import json" things like that, 13 | 0:37 and then I could say "json.dumps" given a dictionary and so on, 14 | 0:41 right, "json.dumps", here a is 1 and boom, we have JSON, 15 | 0:46 but we could also import all sorts of things, 16 | 0:49 we could go to PyPi install some packages and import them 17 | 0:52 for example I could "import requests", because I've installed that somewhere, 18 | 0:56 to capture this idea of how amazing packages are, 19 | 0:59 we have this concept of "import antigravity", 20 | 1:02 so what happens if I import antigravity? Well something pretty awesome, 21 | 1:05 it pulls up this xkcd about why Python is awesome 22 | 1:09 because you can import anything 23 | 1:11 so here I'll just read it to you really quick, 24 | 1:13 so we have this guy up here he is totally flying and his friend says, 25 | 1:16 "Dude, how are you flying?" 26 | 1:18 "Python, I just learned it last night everything is so simple, 27 | 1:21 hello world is just print "hello world!"" 28 | 1:23 "I don't know man, dynamic typing? White space?", 29 | 1:26 "Come on, join us, programming is fun again, it's a whole new world up here" 30 | 1:30 "But how are you flying?" 31 | 1:31 "I just typed import antigravity", "That's it?" 32 | 1:33 "Well I also sampled everything in the medicine cabinet for comparison, 33 | 1:36 but I think it's the Python". 34 | 1:38 This is the feeling you should have as you think about packages 35 | 1:41 and modules and all the stuff that you can do, 36 | 1:44 let's talk about the idioms about consuming all these amazing packages. -------------------------------------------------------------------------------- /transcripts/09-loops/3.txt: -------------------------------------------------------------------------------- 1 | 0:01 So in some sense we saw that range gives us a kind of a numerical "for" loop 2 | 0:04 and that lets us walk through numbers from some starting point 3 | 0:08 to some ending point, evenly incrementing them, continuously by the same amount. 4 | 0:12 If you actually want to get items out of a collection, 5 | 0:15 if you are going to emulate that original "for" loop 6 | 0:17 where you say "I am going to get the index 7 | 0:18 and then index into some kind of collection like a list or something", still, 8 | 0:22 don't do that, don't do that with the range, there is yet a better way. 9 | 0:26 So sometimes you want the item and you need the index 10 | 0:29 at which that item comes from, 11 | 0:31 suppose you are making like an ordered list type thing, 12 | 0:33 item number 1 is this, item number 2 is that and so on, 13 | 0:36 that might look kind of like this fake "for" loop up here, right, 14 | 0:40 so we create the index, we do the loop based on the index, 15 | 0:43 we get the value and we print out the index and value. 16 | 0:46 We can use a really cool combination of tuple unpacking 17 | 0:49 and a special type of iterable that we can work with, 18 | 0:52 so simulate this in a much more Pythonic way, 19 | 0:54 so if we wanted to write this code we could write something like "for", 20 | 0:57 let me just suspend what goes here for a minute, 21 | 0:59 "something in" we can say "enumerate" 22 | 1:01 and when you enumerate some kind of collection like in this case 23 | 1:03 we'll enumerate "data", so what actually comes out is a tuple of 2 items, 24 | 1:08 first the index and then the value for that part, 25 | 1:11 to the loop, so we can unpack that in two variables right here, 26 | 1:14 index and value and then we can do whatever we are going to do, 27 | 1:17 here we said something like this: "we'll print out the index and the value", 28 | 1:23 there you have it, if we are going to make this sort of ordered list style, 29 | 1:26 you might put a "+1" here, item 1 goes to 1, item 2 is 7, item 3 is 11. 30 | 1:31 There, that's the Pythonic way to do numerical "for" loops. 31 | 1:35 So we saw that range works for just walking through a set of numbers, 32 | 1:38 but if you actually want to get the items and the numbers, 33 | 1:41 or items and the indexes rather, use enumerate 34 | 1:43 and unpack the tuple into the indexing value and it can't be easier. -------------------------------------------------------------------------------- /transcripts/02-PEP8/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Are you ready to start looking at some Pythonic examples and writing some code? 2 | 0:04 I sure am, we are going to start by focusing on PEP 8. 3 | 0:07 And some of the very basic structured ways 4 | 0:10 in which you are supposed to write Python code, 5 | 0:12 and then we'll quickly move beyond that 6 | 0:15 to more the patterns and techniques 7 | 0:17 that we are going to talk about for the rest of class. 8 | 0:19 So one of the first questions about this idiomatic Python code, this Pythonic code, 9 | 0:25 is "Who decides what counts as Pythonic and what doesn't?" 10 | 0:28 The way one person writes code may very well be different 11 | 0:32 than the way another person likes to write code, 12 | 0:34 and this is one of the reasons that exactly, 13 | 0:37 specifically stating what is Pythonic code and what does it look like is a challenge, 14 | 0:41 but there is a couple of areas where we might take inspiration 15 | 0:43 and pull together some sources and find some consensus. 16 | 0:47 One of them is just the community. 17 | 0:49 We can look at blogs, Stack Overflow, 18 | 0:51 things like that and see what people are saying 19 | 0:54 and what they agree or disagree upon. 20 | 0:56 So, here is a question that asked what is Pythonic code 21 | 0:59 and there is some examples given. 22 | 1:01 Another area that people take inspiration from 23 | 1:04 is something called The Zen of Python, 24 | 1:06 and if you want to look at The Zen of Python, this is by Tim Peters 25 | 1:09 you can just type import this inside of the REPL 26 | 1:11 in any version of Python and you'll get this, 27 | 1:13 and it's things like "beautiful is better than ugly, 28 | 1:16 flat is better than nested, errors should never pass silently"; 29 | 1:19 again, this is not super concrete 30 | 1:22 but it does give some sort of structure about what is important and what isn't. 31 | 1:26 We also have PEP 8, Python Enhancement Proposal 8, 32 | 1:30 one of the very first official updates to the Python language 33 | 1:34 was this thing called PEP 8 which is a style guide for Python. 34 | 1:38 Mostly this talks about the actual structure of your code, 35 | 1:41 like "you should use spaces, not tabs", those types of things, 36 | 1:44 but it also has some guidance on patterns as well. 37 | 1:47 We'll look at the few of the recommendations from PEP 8 38 | 1:50 before we move on to the more design/pattern/style recommendations. -------------------------------------------------------------------------------- /transcripts/01-introduction/5.txt: -------------------------------------------------------------------------------- 1 | 0:01 Before we get to the actual code examples, 2 | 0:02 let's really quickly talk about setup, tools, versions, that sort of thing. 3 | 0:06 So, this course is built with Python 3 in mind. 4 | 0:10 If you don't have Python 3 installed, 5 | 0:12 you can get it for your operating system at python.org/downloads, 6 | 0:15 or you can "Homebrew" or "apt get install" it. 7 | 0:17 That said, most of the topics will actually be quite similar 8 | 0:21 between Python 2 and Python 3, 9 | 0:23 so we'll talk about Python 2 whenever there are significant differences. 10 | 0:26 You may be wondering why are we talking about Python 3, 11 | 0:29 when Python 2 is more widely used today in commercial applications. 12 | 0:33 Let's look at Python over time and I think you'll see why I made this decision; 13 | 0:38 if we start at the beginning, back in 1989, Guido started work on Python. 14 | 0:43 1991, Python 1 came out, 2000 Python 2, 15 | 0:47 and then, in 2008 Python 3 came out, 16 | 0:50 and this was the first major breaking change 17 | 0:52 to try to improve and clean up the language 18 | 0:55 from all the years that had preceded it. 19 | 0:57 Well, there was limited adoption because people already had large working code basis, 20 | 1:02 a lot of the PyPi packages were not updated and so on, 21 | 1:05 now if we look at today Python 3 has default as much more common 22 | 1:08 than it has been but more importantly, 23 | 1:11 if you look just four years into the future, not far at all, 24 | 1:14 you will see that Python 2 is "end of life", 25 | 1:17 there will be no more support for Python 2 in less than four years. 26 | 1:20 And that means, whoever has projects, written in Python 2 27 | 1:23 have to not just start upgrading them, 28 | 1:26 but complete the upgrades by then. 29 | 1:28 So I expect there to be quite a pick up for Python 3 30 | 1:31 and I think focusing on Python  3 is very important going forward. 31 | 1:35 Guido Van Rossum and the core developers feel this way as well, 32 | 1:39 if you look at the last three keynotes, 33 | 1:41 there has been something like this 34 | 1:43 where Guido has gotten up at PyCon and said, 35 | 1:46 "There is not going to be another version of Python 2, 36 | 1:49 going forward it's all about Python 3." 37 | 1:51 So, that's why this course itself is based on Python 3, 38 | 1:54 even though like I said, 39 | 1:55 differences are quite minor for the topics we are covering. -------------------------------------------------------------------------------- /code/ch_02_foundations/_03_multipe_compares.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | from enum import Enum 3 | 4 | 5 | def main(): 6 | d_text = input("Which direction [n,s,w,e,nw,ne,sw,se]? ") 7 | m = Moves.parse(d_text) 8 | 9 | if m is None: 10 | print("That's not a move!") 11 | return 12 | 13 | print(m) 14 | 15 | # ******** less pythonic ******** 16 | # if m == Moves.North or m == Moves.South or m == Moves.West or m == Moves.East: 17 | # print("That's a direct move.") 18 | # else: 19 | # print("That's a diagonal move.") 20 | 21 | # ******** more pythonic ******** 22 | if m in {Moves.North, Moves.South, Moves.West, Moves.East}: 23 | print("That's a direct move.") 24 | else: 25 | print("That's a diagonal move.") 26 | 27 | # direct_moves = {Moves.North, Moves.South, Moves.West, Moves.East} 28 | # t0 = datetime.datetime.now() 29 | # speed: .2 sec for multiple tests 30 | # 2.3 sec for basic slow in 31 | # .3 sec for cached direct moves in 32 | # for _ in range(0, 1000000): 33 | # # b = m == Moves.North or m == Moves.South or m == Moves.West or m == Moves.East 34 | # b = m in direct_moves 35 | # # b = m in {Moves.North, Moves.South, Moves.West, Moves.East} 36 | # t1 = datetime.datetime.now() 37 | # dt = t1 - t0 38 | # print("Time delta: {:,} sec".format(dt.total_seconds())) 39 | 40 | 41 | class Moves(Enum): 42 | West = 1 43 | North = 2 44 | East = 3 45 | South = 4 46 | NorthEast = 5 47 | SouthEast = 6 48 | NorthWest = 7 49 | SouthWest = 8 50 | 51 | @staticmethod 52 | def parse(text: str): 53 | if not text: 54 | return None 55 | 56 | text = text.strip().lower() 57 | if text == 'w': 58 | return Moves.West 59 | if text == 'e': 60 | return Moves.East 61 | if text == 's': 62 | return Moves.South 63 | if text == 'n': 64 | return Moves.North 65 | 66 | if text == 'nw': 67 | return Moves.NorthWest 68 | if text == 'sw': 69 | return Moves.SouthWest 70 | if text == 'ne': 71 | return Moves.NorthEast 72 | if text == 'se': 73 | return Moves.SouthEast 74 | 75 | return None 76 | 77 | 78 | if __name__ == '__main__': 79 | main() 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /code/ch_03_dictionaries/_03_mem_hacking.py: -------------------------------------------------------------------------------- 1 | # ############ __slots__ for improved memory usage ############# 2 | # Create by Michael Kennedy (@mkennedy) 3 | # 4 | # Overview: 5 | # Custom types store their data in individualized, dynamic dictionaries 6 | # via self.__dict__. Using __slots__ to limit available attribute names 7 | # and move the name/key storage outside the instance to a type level 8 | # can significantly improve memory usage. See EOF for perf numbers. 9 | # 10 | 11 | import collections 12 | import datetime 13 | 14 | ImmutableThingTuple = collections.namedtuple("ImmutableThingTuple", "a b c d") 15 | 16 | 17 | class MutableThing: 18 | def __init__(self, a, b, c, d): 19 | self.a = a 20 | self.b = b 21 | self.c = c 22 | self.d = d 23 | 24 | 25 | class ImmutableThing: 26 | __slots__ = ['a', 'b', 'c', 'd'] 27 | 28 | def __init__(self, a, b, c, d): 29 | self.a = a 30 | self.b = b 31 | self.c = c 32 | self.d = d 33 | 34 | 35 | print("Uncomment just 1 of these 4 loops below") 36 | print("after the program pauses on input, check the process memory") 37 | 38 | count = 1000000 39 | data = [] 40 | 41 | t0 = datetime.datetime.now() 42 | 43 | # Loop 1: Tuples 44 | print("tuple") 45 | for n in range(count): 46 | data.append((1 + n, 2 + n, 3 + n, 4 + n)) 47 | 48 | # # Loop 2: Named tuple 49 | # print("named tuple") 50 | # for n in range(count): 51 | # data.append(ImmutableThingTuple(1 + n, 2 + n, 3 + n, 4 + n)) 52 | # 53 | # # Loop 3: Standard mutable class 54 | # print("standard class") 55 | # for n in range(count): 56 | # data.append(MutableThing(1 + n, 2 + n, 3 + n, 4 + n)) 57 | # 58 | # # Loop 4: Slot based immutable class 59 | # print("slot based class") 60 | # for n in range(count): 61 | # data.append(ImmutableThing(1 + n, 2 + n, 3 + n, 4 + n)) 62 | 63 | t1 = datetime.datetime.now() 64 | 65 | input("Finished, waiting... done in {:,} s".format((t1 - t0).total_seconds())) 66 | 67 | # Sample output on OS X + Python 3 68 | # Hardware: Macbook Pro 2013 edition 69 | 70 | # straight tuple: 207 MB, 0.528455 s 71 | # named tuple: 215 MB, 1.519358 s 72 | # class (dynamic): 370 MB, 1.680248 s 73 | # slot class: 120 MB, 1.438989 s 74 | 75 | # And on Windows 10 + Python 3, same hardware (memory is "working set") 76 | # tuple: 153 MB 77 | # named: 153 MB 78 | # class: 248 MB 79 | # slots: 145 MB 80 | 81 | # Interesting real-world story of benefits of slots: 82 | # http://tech.oyster.com/save-ram-with-python-slots/ 83 | -------------------------------------------------------------------------------- /transcripts/02-PEP8/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 Next, let's look at what PEP 8 says about documentation and docstrings. 2 | 0:04 Here we have our some_method, method that I wrote, 3 | 0:08 and I added three more parameters, 4 | 0:10 if I come down here and I say some_method, 5 | 0:13 see there is sort of note help in the IntelliSense, 6 | 0:16 and if I say "look it up", PyCharm does a little work 7 | 0:19 to say what the type hints would be if it could look at the types 8 | 0:23 the way the function is written, 9 | 0:25 but it doesn't tell me really what a1, a2 or a3 mean, 10 | 0:28 or what the method itself does, 11 | 0:30 and obviously it's poorly named, so this doesn't help as well. 12 | 0:33 So what we should do is give it some docstrings, 13 | 0:35 and docstrings are just a string by itself on the first line of the method, 14 | 0:40 or module, if you're documenting the module or class, if you're documenting the class. 15 | 0:44 So we can just say triple quote 16 | 0:46 and then I hit Enter and PyCharm will actually look at the method and help us out, 17 | 0:49 so it knows it returns a value 18 | 0:51 and it knows it has three parameters called a1, a2 and a3, 19 | 0:54 so when I hit Enter, I get the sort of structured way of documenting my method 20 | 0:59 so we'll say something like "some_method returns a larger of one or two", 21 | 1:03 which is not actually what it even returns, 22 | 1:06 it's just a silly method I threw in there to talk about spaces, 23 | 1:09 let's say this is a meaningful thing, 24 | 1:11 and let's say this will be the first value will be first item to compare 25 | 1:17 I will say a2 is the second item to compare and a3 is actually 26 | 1:20 whether or not it should be reversed which again, doesn't mean anything. 27 | 1:24 We'll say 1 or 2. So if we write this we can come down here 28 | 1:27 now and I say some_method, and look at it I could hit F1 or if I was in REPL 29 | 1:31 I can type "help some_method" and I would see this- 30 | 1:36 some method returns the larger of 1 or 2, a1, first item to compare, 31 | 1:41 a2 second item to compare, a3 should reverse, 1 or 2. 32 | 1:43 All right, so that's helpful, this is the recommended way 33 | 1:46 to write these docstrings, according to PEP 8, 34 | 1:50 let me fix this, put a little space here, we'll go back and look at the picture. 35 | 1:54 So the recommendation from PEP 8 is 36 | 1:57 we should write docstrings for all public symbols, 37 | 2:00 modules, functions, classes and methods, 38 | 2:03 and they are not really necessary for non-public items, 39 | 2:06 that's an implementation detail and you can do whatever you want there. -------------------------------------------------------------------------------- /transcripts/09-loops/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Loops in Python are a little bit different than loops in other languages. 2 | 0:04 You'll see that there is much more collections philosophy underlying them. 3 | 0:07 Let's start with the basics. 4 | 0:09 There is no numerical loop in Python, 5 | 0:11 there is no "for" loop, like C++, C#, Java, Javascript, 6 | 0:16 they have this way of walking through a number and incrementing it 7 | 0:19 usually to pull items out of an array, so I imagined what would that look like in Python 8 | 0:23 if we had a numerical "for" loop, something like: "this for i = 0; i less than len(data); i++ " 9 | 0:29 we are going to go work with items. 10 | 0:30 Well, that doesn't exist. Sometimes, 11 | 0:34 people try to work their way around it using the loops 12 | 0:36 that do exist and recreating this more or less. 13 | 0:40 Look, we've done it, we've taken the initialization of the variable, moved before loop, 14 | 0:44 we have the test  and now withing the body loop we do the increment. Perfect. 15 | 0:48 No, technically that works but this is also super non-Pythonic 16 | 0:52 so let's look at some code that is. 17 | 0:55 All right, here in PyCharm you can see I kind of sketched out 18 | 0:57 what that might look like, well as you saw we can well, 19 | 1:01 first of all, this just put any ideas out of your head, this doesn't round right, obviously. 20 | 1:07 There is no "for" loop, but we can fake this idea 21 | 1:10 we can say the "i = 0" goes here, this can be a "while" loop, 22 | 1:14 we can do our test there and this increment bit, we can put that down here. 23 | 1:20 Like so, we don't have "++" but we do have a "+= 1", let's see if it works, 24 | 1:24 it should put out 1 and 7 then 11, oh but of course, it does not, let's do this. 25 | 1:33 Now it prints out the index and it prints out the value. 26 | 1:36 All right, this is not Pythonic, so we'll just make a note: "No, NOT Pythonic". 27 | 1:43 So of course, what is the proper way to loop through these items? 28 | 1:46 "for item in the collection" and let's print this out, 29 | 1:52 instead of worrying about the index, we just have the item 30 | 1:55 let's put a new line in between, there: 1, 7, 11. Perfect. 31 | 2:02 No fuss, no muss, just go. 32 | 2:05 So, there is no numerical "for" loop or faking it like this, also not Pythonic, 33 | 2:11 we'll talk about what you can do when you really need that later, 34 | 2:14 but typically, write loops like this. 35 | 2:17 OK, in a graphic, no numerical "for" loop, 36 | 2:20 instead just loop over the data, usually the goal is to get at the underlying items 37 | 2:24 and some sequence anyway. -------------------------------------------------------------------------------- /transcripts/06-methods/6.txt: -------------------------------------------------------------------------------- 1 | 0:01 Another way to add flexibility to functions is 2 | 0:03 to allow you to pass a variable number or maybe more correctly 3 | 0:06 an arbitrary number of parameters to it. 4 | 0:10 So let's look at that in this case of the silly function I wrote called biggest, 5 | 0:13 so "biggest" will take two numbers, 6 | 0:16 any really two comparables and tell you which one is bigger, 7 | 0:19 so like biggest of 1 and 7, surprise, it's 7. 8 | 0:22 Let's go look at this in code. 9 | 0:24 Here we have the same thing as on our slides, 10 | 0:26 if we run this, you can see still 7 is bigger than 1, hasn't changed. 11 | 0:31 Cool, but what if we want to have more than 1 argument, 12 | 0:34 what if we wanted to somehow, let's go in and write the code 13 | 0:39 what if we wanted to say the biggest of 1 and 7 and 42 and 99 and -1 and 11; 14 | 0:46 what if I wanted to write that code? 15 | 0:48 Well, if I try it's obviously not going to love it 16 | 0:51 because it says it took two positional parameters I was given 6, 17 | 0:54 not a good deal, so we can use convention here in Python called *args, 18 | 1:00 so star, args is the convention, * (star) is the keyword or the language feature, 19 | 1:06 that says this thing is going to accept an arbitrary number of parameters, 20 | 1:10 the x means first it means you have to supply at least one but you may supply more, 21 | 1:14 so let's first of all just print out what is this args and what is it 22 | 1:20 and let's comment that out for a moment, 23 | 1:22 if I try this again, you can see that I was given a tuple 7, 42 and so on, 24 | 1:27 if we look here, that's the remainder after the x. 25 | 1:31 So let's go write the code that actually does the biggest part here, 26 | 1:39 there, that seems like a reasonable implementation, 27 | 1:41 we'll start out assuming the one that we have is the biggest 28 | 1:44 and we'll go through everything that was passed to us as many arguments, 29 | 1:47 zero or more that you give us and we'll check, well is this one bigger, 30 | 1:50 if it is we'll set that one to be the result. 31 | 1:52 So what should the answer be at the end? 99 of course. 32 | 1:55 Boom, and it is. 33 | 1:58 So here is another Pythonic way to add flexibility to your methods, 34 | 2:02 in a graphic, we saw we had this limited version 35 | 2:05 that could compare two things, using *args 36 | 2:09 we were able to upgrade it to take an arbitrary number of items 37 | 2:12 and we saw that the thing that's passed in the args 38 | 2:15 is actually a tuple and we just loop over it 39 | 2:17 or do whatever you want to do with the tuple of additional parameters. -------------------------------------------------------------------------------- /transcripts/07-modules-packages/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 Let's talk about isolating your dependencies 2 | 0:03 and your versions for your external packages, 3 | 0:05 from all the other Python projects on your OS. 4 | 0:09 Here we are in the terminal, 5 | 0:10 and I am in this directory here that I have a bunch of Python environments, 6 | 0:14 and I'd like to create a new Python installation basically, 7 | 0:18 for a new web app I am going to work on, or any kind of app, really, 8 | 0:23 I'd like to be able to control the version of Python is there 9 | 0:25 as well as the packages that are installed 10 | 0:28 and their versions independent from everything else on my system. 11 | 0:31 So I can use virtual environments for this, 12 | 0:34 in Python 3, we have a "venv" command 13 | 0:37 and we can also use the virtual environment's external package, 14 | 0:40 we can also use the virtual environment's external package, 15 | 0:43 which works with all versions of Python. 16 | 0:45 So I'll go and use that here but they work exactly the same. 17 | 0:48 So I want to create a directory here that's going to contain my Python environment 18 | 0:52 so I'll say "virtual"- let's make sure we have the right one first, "virtualenv", OK, 19 | 0:57 this is going to be the one for Python 3.5, so good, 20 | 1:01 so I'm going to say this, and I'm going to give it a folder, 21 | 1:03 let's just say sample_web_env, so we know it's an environment, not the web app itself. 22 | 1:12 Spelling is hard, 23 | 1:16 so we can see it's created a version of Python here based on Python 3.5 24 | 1:21 and installed the necessary tools, basically that we need to add more packages. 25 | 1:25 So, now if I say "which pip", it doesn't yet have this environment active, 26 | 1:31 the creation of the environment doesn't activate it, 27 | 1:34 so the next thing we need to do is say dot(.), so the first dot(.) means 28 | 1:38 apply this to this shell rather than a separate one for the execution, 29 | 1:41 and we'll go in the "sample... bin activate", we run this, 30 | 1:44 now our prompt changes and more importantly, 31 | 1:47 if we ask the same question - "which pip" - now it's this one, 32 | 1:50 if we ask "which python", it's this one. 33 | 1:54 OK, so we're ready to use that, let's say pip list to see what we have installed, 34 | 1:57 we just have those 3 that were installed there so we could go install 35 | 2:01 something like I could say "pip install requests", and it's gone and downloaded, 36 | 2:10 now if I ask what's installed, I just have this. 37 | 2:13 So I can have version 2.10.0 of request regardless of 38 | 2:16 whatever else is installed, upgraded, downgraded, in the rest of my OS. 39 | 2:21 All right, let's go and upgrade pip. 40 | 2:27 OK, so here is our sample environment, 41 | 2:31 we'll be using this in the next section. -------------------------------------------------------------------------------- /transcripts/03-Foundational-Concepts/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 This tip is nice and short but really helpful. 2 | 0:03 So let's talk about how we choose a random item. 3 | 0:07 Here in PyCharm, first I want to show you the bad 4 | 0:09 what I am calling C-style but this appears  in many languages. 5 | 0:13 So we have these letters, and letters and numbers, 6 | 0:15 we'd like to randomly pick one and show it to the user. 7 | 0:18 So the most natural thing to do coming from a language like C 8 | 0:21 is to create a random number that will be a proper index into that list, 9 | 0:25 so we'd say "index = rand", we create "rand" and "n" be from 0 to the "len" of letters. 10 | 0:32 And we'll say "item = letters of index". 11 | 0:37 Now, this actually includes the upper bound 12 | 0:40 so we need to take one away from it 13 | 0:42 to make sure we don't have an off-by-one error, but of course, 14 | 0:45 this is something you have to go look up in the documentation 15 | 0:48 to see - is it including the upper bound and lower bound 16 | 0:51 or just lower bound? Something like this. 17 | 0:53 And we can just print out the item, 18 | 0:55 if I run it, apparently "y" is the randomly selected one 19 | 0:58 run it a few more times, "zero", "w", "k", awesome. 20 | 1:02 So what's wrong with this? Well, there is a couple of things. 21 | 1:05 One, I have to go and calculate the length 22 | 1:08 I have to know  this is including the upper bound 23 | 1:11 when I ask for these random numbers, so I have to do minus 1, 24 | 1:14 oh and I forgot to check that there is actually an item in here 25 | 1:17 that the index is not negative, something like that. 26 | 1:20 So let's write the Pythonic version. 27 | 1:23 You'll see it's easier to read, it's shorter, it's safer, everything you want. 28 | 1:27 So, I want a random item, and given a sequence, 29 | 1:31 I can just say "random, choose a random item", from that sequence. 30 | 1:36 Done, I don't have to think about what the documentation says 31 | 1:39 about upper and lower bounds, 32 | 1:41 I don't have to verify anything, just print out the item, 33 | 1:45 run it again, "c" was chosen by the C-style, 34 | 1:48 and "d" by the Python style. 35 | 1:51 "b", "z", "4", "t", and so on. 36 | 1:54 Simple, sweet, but very effective 37 | 1:55 and once you start using it, you will never want to go back to the C-style. 38 | 1:59 So here is that code in a graphic. 39 | 2:01 We have the bad style using the "random int", 40 | 2:04 oh and you can see in my slide we actually have a bug about the upper bound, 41 | 2:08 how interesting, huh? 42 | 2:10 There, I fixed it, but it was interesting that we had this place 43 | 2:13 where we could introduce the bug and of course we get the index, 44 | 2:16 we use the index to index into the letters, get the item and then we can print it out. 45 | 2:19 The Python one instead let's just go random that choice, 46 | 2:22 from a sequence, boom, there you go. 47 | 2:25 In Python it's generally preferable to use declarative code 48 | 2:29 rather than procedural code, and this is a little step in that direction. -------------------------------------------------------------------------------- /transcripts/03-Foundational-Concepts/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 Next let's look at testing for a special case. 2 | 0:03 Imagine that we are going to call a function and that might return a list 3 | 0:06 and that list might have items in it or it might not, 4 | 0:10 maybe we are trying to do some kind of search and there is just no results, 5 | 0:13 and yet, if it was an unable to perform a search, 6 | 0:16 we'd like to indicate that differently than if there is just no results. 7 | 0:19 In that case, we can actually change our algorithm just a little bit 8 | 0:22 and test for something different. 9 | 0:24 Let's look at PyCharm. 10 | 0:26 So here I have this function called find_accounts 11 | 0:27 and you give it some search text; 12 | 0:29 it checks to see if the database was available and if it's not available, 13 | 0:34 it returns None, which we know is False. 14 | 0:36 Otherwise, it's going to actually do a search against our database 15 | 0:40 and return a list of account ids. 16 | 0:42 However, there might not be any matching results 17 | 0:45 in which case that list could be empty, 18 | 0:47 so you might be tempted to say "if not accounts", 19 | 0:50 then maybe I want to just like print something out like this, 20 | 0:54 "else", let's actually try to say we are going to print the accounts. 21 | 0:57 This part is going to work fine if there are results, we would list them. 22 | 1:00 However, it could be that our search just returns no results, 23 | 1:04 in which case this list would evaluate the False, 24 | 1:06 so we are going to change this, we are going to test actually if accounts is None, 25 | 1:10 when you are testing against singletons, and None is a singleton, 26 | 1:14 there is only one instance of it per process, 27 | 1:16 we'll use "is" and that actually compares at the pointers, 28 | 1:20 not just the inherit truthiness of the objects 29 | 1:22 or some kind of overloaded comparison operator. 30 | 1:25 So now our code is going to work as expected, 31 | 1:29 if we literally get nothing back because the DB is unavailable, 32 | 1:32 then we can say oh, DB is not available, otherwise, we might print them out. 33 | 1:36 And of course, that set might be empty, 34 | 1:37 we might want to put in additional testing here to say 35 | 1:40 "well, if it's empty" like your search works but there is no results, whatever. 36 | 1:44 But the Pythonic thing here is to notice that we are using "is" 37 | 1:48 to test against singleton and a special case of singletons 38 | 1:51 of course is the None keyword, which is one of the most common. 39 | 1:56 So here we have this in a graphic, so we are calling find_accounts, 40 | 1:59 giving it some search text and we are going to get some results back 41 | 2:02 and we want to make sure that not only is it truthy, 42 | 2:06 but it's actually worth specifically testing that there is something we got back 43 | 2:10 rather than a None pointer, 44 | 2:12 so "if accounts is not None", 45 | 2:15 and notice, when you negate "is", you say "is not", 46 | 2:18 rather "than not account is", which would also work but is less Pythonic. -------------------------------------------------------------------------------- /transcripts/09-loops/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 One peculiar feature of Python loops is that they have an "else" block, 2 | 0:06 first of all, do you know that the "while" loop 3 | 0:08 and the "for...in" loop have "else" clauses that you can attach to them? 4 | 0:12 Second, can you remember the order in which these happen, 5 | 0:15 what triggers the "else" block to run versus it not running? Let's have a look, 6 | 0:20 so here we have two "while" loops, one "while" loop runs to completion; 7 | 0:25 the other "while" loop breaks out early, let's just run to see the output. 8 | 0:29 Cool, so here we have 5 dots and here we have 4 dots, 9 | 0:32 that means we've run through one all the way to the end, 10 | 0:34 the other we stopped a little bit early. 11 | 0:37 So, there is nothing special about that, but let's see about the "else" clause here, 12 | 0:41 so let's say "for all loops" we can say "else", so "do the loop, else", 13 | 0:48 we'll just put a message to know that it ran or didn't in the "else" clause, 14 | 0:52 so we'll write something like this, "in the else clause of the whole loop", 15 | 0:56 and down here we'll say "else", in the "else" clause of the early break loop, 16 | 1:00 which one is going to run? Let's find out. 17 | 1:06 OK, so if we run through the entire loop, 18 | 1:09 and we go all the way to the end, this becomes False, and then "else", we run this. 19 | 1:15 On the other hand, if we go through here 20 | 1:17 and we never go to a case where this is no longer True 21 | 1:20 but we break out early instead, we don't run the "else" clause. 22 | 1:23 I am sure there is a few good uses for this, somewhere, 23 | 1:26 it doesn't seem like a language feature that's worth it to me, 24 | 1:29 but my recommendation for Pythonic code is: Do not use the "else" clause on loops. 25 | 1:35 Yeah, here is a whole language feature, I say don't use it, now you might say, 26 | 1:39 "Michael, is that really Pythonic? I mean it is a language feature, 27 | 1:42 who would say "Don't use a language feature that was put in there 28 | 1:46 by Guido Van Rossum, the creator of Python himself?"" let's see. 29 | 1:51 Back in 2009, somebody asked about the "for/while/else" 30 | 1:56 without "break" or "return" clauses and Guido dropped in and the guy said, 31 | 2:00 "Well, I am not sure that this is a great choice of words", 32 | 2:03 and Guido dropped in and said, "You know what, 33 | 2:04 I would not have this feature at all if I had to do it over". 34 | 2:07 "I would not put the "else" clause on loops in the language, period." 35 | 2:12 That to me is a pretty strong statement that you know, we don't really need this, 36 | 2:15 there are many successful languages that don't have an "else" clause, 37 | 2:18 I think the "else" clause is confusing, 38 | 2:20 Guido thinks it's probably not the best to put it in there, let's all agree to avoid it. 39 | 2:26 Right, so we saw that we have "else" clauses here, 40 | 2:28 if you break out the loop early in this case, the "else" clause does not run, 41 | 2:33 if we run the loop to completion, basically to a False state, then it will run. 42 | 2:38 "I would not have put the "else" clause in the language at all 43 | 2:42 if I had to do it over", to me it sounds like non-Pythonic. -------------------------------------------------------------------------------- /transcripts/06-methods/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 In Python, there is no function or method overloading. 2 | 0:04 Here we have two methods called simple on this class, 3 | 0:07 now the first one takes no parameters, 4 | 0:10 the second one takes a details parameter, 5 | 0:13 in some languages, these would be two distinct methods 6 | 0:16 and based on the particular signature you are trying to use, 7 | 0:18 the compiler would select one or the other. 8 | 0:21 This does not exist in Python, let's look at it in an example. 9 | 0:25 Here we have the same basic code 10 | 0:27 and we are creating what I call the Sample class, 11 | 0:30 that really doesn't mean anything, and we are going to call a simple method on it, 12 | 0:34 notice we have this kind, we have this kind. 13 | 0:37 PyCharm is giving us a little bit of clue that something is going wrong here, 14 | 0:41 you can see it's highlighting the second simple on line 6 15 | 0:43 but let's go ahead and run it and just see what happens. 16 | 0:47 So look, the first one where we pass some details, this actually worked, 17 | 0:52 I call this simple with details and it said "Some details." 18 | 0:55 However the second one didn't work, 19 | 0:57 "simple() is missing 1 required parameter: 'details'", 20 | 1:01 so what's going on here? 21 | 1:03 It turns out there can only be one method called "simple" on this class, 22 | 1:07 and so when we define the second one 23 | 1:09 we basically eject this one from the class, 24 | 1:12 we just overwrite it in a dictionary that the key "simple" now means something different, 25 | 1:17 and so even though PyCharm is little bit freaked out by this here 26 | 1:21 because it were doing it wrong above, did catch that error, 27 | 1:24 this is the one that is actually not going to work because as far as this class is concerned, 28 | 1:28 there is no method "simple" that takes no parameters, 29 | 1:31 oh look, in the subsequent sections on how Python deals with this, 30 | 1:36 because this kind of flexibility is super powerful and Python does support it, 31 | 1:40 it just doesn't support it in the traditional method overloading way 32 | 1:44 that you might be familiar with coming from C++, Java, C# and so on. 33 | 1:49 In a graphic, here it is. Here is our class, we have the two methods, 34 | 1:52 the top method is being overwritten or ejected by the bottom one, 35 | 1:56 these could just as well be functions and not methods it would have the same effect, 36 | 2:00 but what is really important to notice is 37 | 2:03 when I ran the code, it wasn't the fact the I was redefining "simple" 38 | 2:07 that was actually causing the runtime problem, 39 | 2:09 this code as it's on the screen here, this will run perfectly fine 40 | 2:13 it just won't do what you think, so be very careful here. 41 | 2:16 So for example, if we create the "Sample" class, 42 | 2:20 we call a "simple" method with details, the last one in our list that is going to work correctly, 43 | 2:25 but the bottom one crashes because it no longer exists, basically. 44 | 2:29 Now, there is nothing specifically Pythonic about this, 45 | 2:32 but this lays out the problem the next 3 or 4 lectures 46 | 2:36 are going to show us Pythonic ways to solve this problem 47 | 2:39 that do not have to do with method overloading and signature matching. -------------------------------------------------------------------------------- /transcripts/03-Foundational-Concepts/6.txt: -------------------------------------------------------------------------------- 1 | 0:01 The next Pythonic tip I want to cover 2 | 0:02 is about sending an exit code to indicate 3 | 0:05 whether your application succeeded or failed. 4 | 0:09 Let's have a look, so here I have a little utility app, 5 | 0:11 meant to run on Windows and its job is to format the main drive C: 6 | 0:15 and of course you don't want to just do that, 7 | 0:18 just because the app was run, the script was run, 8 | 0:21 you'd like a little confirmation, so here we ask the user 9 | 0:23 "are you sure you want to format your main hard drive?" 10 | 0:25 "Yes" or "no", you can see the default is "no" 11 | 0:28 but if for some reason they type in "yes", 12 | 0:31 and they come down here, we are going to simulate a little work, 13 | 0:34 we are not actually going to format anyone's hard drive, 14 | 0:37 and then we'll say "format completed". 15 | 0:39 So let's run this. 16 | 0:41 "Are you sure you want to format drive C?" Let's say "no", format canceled, 17 | 0:46 now notice, exit code zero. 18 | 0:49 Let's come down here and say "yes", I would love to format drive C, 19 | 0:53 so we do a little bit of work, you can't format it immediately, it's hard job here, 20 | 0:58 and then boom, formatted successfully, enjoy the new hard drive space. 21 | 1:01 Now, we also got code zero, 22 | 1:04 if I was trying to run this as a subprocess, 23 | 1:06 or I was trying to orchestrate this by chaining it together, 24 | 1:09 there is no way for me to know as a user of this script 25 | 1:13 whether or not the user canceled or they actually formatted the hard drive. 26 | 1:17 We can easily come up here before a return, we don't technically need to return, 27 | 1:22 because this is going to actually stop executing immediately; 28 | 1:26 anyway, I can go over here and I can import "sys", 29 | 1:29 now I was already importing "sys" so that I could use this little flush command 30 | 1:32 that normally I wouldn't have been there, 31 | 1:34 so I'll say "sys.exit" and we are going to exit here with let's say 1, 32 | 1:39 down here, we can do this, we can say "sys.exit(0)" of course, 33 | 1:46 you saw if we don't do anything at all, "exit(0)" is what is going to happen, 34 | 1:49 so maybe I'll leave this one off. 35 | 1:51 Now, we also know that this is going to throw an exception 36 | 1:54 so this "return" is actually unreachable, PyCharm told us that, 37 | 1:57 OK so let's try again; 38 | 2:00 "are you sure you want to format your main hard drive?" 39 | 2:02 No. Exit code 1. 40 | 2:05 Do you want to format your hard drive - oh please do, exit code 0. 41 | 2:09 Again, canceled, exit code 1, perfect. 42 | 2:14 So if there is any chance that  your script is going to be called 43 | 2:17 by other script or other applications 44 | 2:19 you want to make sure that you indicate some kind of exit code 45 | 2:22 so that they can use that information to determine 46 | 2:25 whether or not they can continue whatever they are doing afterwards, 47 | 2:27 whether or not your script succeeded. 48 | 2:30 So if we just let our app, our script exit, well it's always code zero, 49 | 2:34 but we can use "sys.exit" and give some kind of code, 50 | 2:38 typically the convention is if it's non-zero 51 | 2:41 there was some kind of either failure or abnormal exit 52 | 2:44 whereas zero is "all systems nominal". 53 | 2:48 Everything is good. -------------------------------------------------------------------------------- /code/ch_03_dictionaries/_01_perf.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import datetime 3 | import random 4 | 5 | import sys 6 | 7 | DataPoint = collections.namedtuple("DataPoint", "id x y temp quality") 8 | 9 | 10 | def main(): 11 | # ############################# 12 | print("Creating data...", end=' ') 13 | sys.stdout.flush() 14 | 15 | data_list = [] # 500,000 DataPoint items 16 | random.seed(0) 17 | for d_id in range(500000): 18 | x = random.randint(0, 1000) 19 | y = random.randint(0, 1000) 20 | temp = random.randint(-10, 50) 21 | quality = random.random() 22 | data_list.append(DataPoint(d_id, x, y, temp, quality)) 23 | 24 | print("done.") 25 | sys.stdout.flush() 26 | 27 | # Reordering data for random access 28 | print("Reordering data for random access ...", end=' ') 29 | sys.stdout.flush() 30 | 31 | data_list.sort(key=lambda d: d.quality) 32 | 33 | print("done.") 34 | 35 | # Create a set of random IDs to locate without duplication 36 | interesting_ids = {random.randint(0, len(data_list)-1) for _ in range(0, 100)} 37 | print("Creating {} interesting IDs to seek.".format(len(interesting_ids))) 38 | 39 | # Locating data in list 40 | print("Locating data in list...", end=' ') 41 | sys.stdout.flush() 42 | 43 | t0 = datetime.datetime.now() 44 | interesting_points = [] 45 | for i in interesting_ids: 46 | pt = find_point_by_id_in_list(data_list, i) 47 | interesting_points.append(pt) 48 | 49 | t1 = datetime.datetime.now() 50 | dt_list = (t1 - t0).total_seconds() 51 | 52 | print("done.") 53 | sys.stdout.flush() 54 | 55 | print("dt: {} sec".format(dt_list)) 56 | print(interesting_points) 57 | 58 | # ############################# 59 | 60 | # let's try this with a dictionary... 61 | # 1. Create dictionary via comprehension, key = id 62 | 63 | t0 = datetime.datetime.now() 64 | data_dict = {d.id: d for d in data_list} 65 | 66 | # 2. locate the data in dictionary 67 | interesting_points.clear() 68 | for d_id in interesting_ids: 69 | d = data_dict[d_id] 70 | interesting_points.append(d) 71 | 72 | t1 = datetime.datetime.now() 73 | dt_dict = (t1 - t0).total_seconds() 74 | 75 | print("done.") 76 | sys.stdout.flush() 77 | 78 | print("dt: {} sec".format(dt_dict)) 79 | print(interesting_points) 80 | print() 81 | print("Speedup from dict: {:,.0f}x".format(round(dt_list / dt_dict))) 82 | 83 | 84 | def find_point_by_id_in_list(data_list, i): 85 | for d in data_list: 86 | if d.id == i: 87 | return d 88 | 89 | return None 90 | 91 | 92 | if __name__ == '__main__': 93 | main() 94 | 95 | # ################# TYPICAL OUTPUT ################### 96 | # Python 3 97 | # OS X, Macbook Pro 98 | # 99 | # Creating data... done. 100 | # Sorting data... done. 101 | # Creating 100 interesting IDs to seek. 102 | # Locating data in list... done. 103 | # DT: 7.339648 sec 104 | # [DataPoint(id=244225, x=308, y=736, temp=15, quality=0.2616059911451657), ...] 105 | # Creating dictionary...done. 106 | # Locating data in dictionary... done. 107 | # DT: 8e-05 sec 108 | # [DataPoint(id=244225, x=308, y=736, temp=15, quality=0.2616059911451657), ...] 109 | # 110 | # Speedup from dict: 91,746x 111 | -------------------------------------------------------------------------------- /transcripts/04-dictionaries/7.txt: -------------------------------------------------------------------------------- 1 | 0:01 When I introduced a section on dictionaries 2 | 0:02 I said that JSON and dictionaries are basically isomorphic, 3 | 0:05 there is a one to one mapping between these things. 4 | 0:08 Let's explore that relationship really quickly here. 5 | 0:11 So here is some text, just a single string, multiline string here, 6 | 0:17 and it's in the JSON format. 7 | 0:20 We'd like to take this, which looks extremely like a dictionary in Python, 8 | 0:23 in fact if I take this, say "print(type(movie_json), movie_json)" 9 | 0:31 and I run this, you'll see this is a string. 10 | 0:34 However, if I say "movie_dictionary = " just copy and paste out of there 11 | 0:40 and indent a little and I do the same thing, 12 | 0:42 this is actually a dictionary and it's pulled this value, 13 | 0:45 I mean, it's basically like you could take the text or JSON and evaluate it, almost, right. 14 | 0:52 So, that's really cool, but given this text, at runtime, 15 | 0:57 how do I dynamically turn this into JSON 16 | 0:59 because copy and paste static data doesn't make any sense, 17 | 1:02 so we are going to use the JSON module, 18 | 1:05 so "import json", spell correctly, we'll come down here, 19 | 1:08 let's put this down at the bottom for a minute, 20 | 1:10 we'll come over here and we'll say "movie_data ==" we just say "json." 21 | 1:18 Now notice there is a "load", which actually takes 22 | 1:21 a file pointer and a "loads", which loads from the string, 23 | 1:24 so we'll say movie_json, so let's just print this out here. 24 | 1:28 So we'll do something similar, we'll print the type of movie_data 25 | 1:31 and then let's print out movie_data itself. 26 | 1:34 So if we run it, we actually successfully parse this JSON text 27 | 1:38 which in reality probably comes somewhere off the web or off the file system, 28 | 1:42 we parse that into a dictionary and here is the dictionary printed back out. 29 | 1:46 So we could answer questions like "The title is {}.format()" 30 | 1:52 and we'll just say movie_data.get(title), boom. "The title is Johnny 5". 31 | 2:01 So, that's a super nice way to go from JSON into Python dictionaries, 32 | 2:05 what about the reverse? 33 | 2:06 So if I have some dictionary, let's just take the same data and reverse it, 34 | 2:10 we can say so let's just call it movie_json_text_2, 35 | 2:14 and we can get that by just saying "jason.dumps()" and if we give it a dictionary 36 | 2:20 it will dump that out as JSON. 37 | 2:24 So here and we can just print type of this thing and then print that. 38 | 2:30 Perfect, dictionary here is the dictionary data, used it to pull some data back, 39 | 2:34 and we want to go the other way to send it across the wire, 40 | 2:38 save it to a file system, we now have a string and it's actually the JSON text here. 41 | 2:43 So we used the JSON module to make this transformation 42 | 2:46 between JSON and dictionaries, 43 | 2:48 "loads" for strings, "load" for file pointers, 44 | 2:51 and then "dumps" for string, "dump" for file pointers. 45 | 2:56 So to review in a graphic, here we have "import json" so we can use that module, 46 | 3:00 we have string, which presumably comes off the internet 47 | 3:04 or off the file system, which is a set of JSON text that we want to parse, 48 | 3:09 so we can parse that into an in-memory dictionary saying "loads", 49 | 3:13 now movie_data is a dictionary, if we want to save it back to the file system 50 | 3:17 or otherwise treat it as a string, "json.dumps", boom, done. -------------------------------------------------------------------------------- /transcripts/02-PEP8/3.txt: -------------------------------------------------------------------------------- 1 | 0:01 Next up, code layout. Let's see what PEP 8 has to say about it. 2 | 0:03 The most important part about code layout that PEP 8 talks about is indentation. 3 | 0:08 So, imagine we have a method called some_method() 4 | 0:12 it says that you should indent four spaces, 5 | 0:14 and PyCharm of course, as you've seen, knows PEP 8 6 | 0:17 and indents four spaces for us automatically, so: one, two, three, four, there we are. 7 | 0:22 And, we can write something so we could define a variable, hit Enter, 8 | 0:25 like an "if" statement, 9 | 0:28 so something like "if x > 2:", enter; 10 | 0:30 Python does not like us to use tabs, 11 | 0:33 PEP 8 recommends we use spaces and every indent is four spaces, 12 | 0:36 but it even looks like if I hit tab and delete tab and delete, 13 | 0:39 hit tab, back and forth, it's actually four spaces 14 | 0:42 and PyCharm just treats blocks of four spaces like tabs for us 15 | 0:45 so that it's easier for us to work with. 16 | 0:48 Next, PEP 8 also talks about spaces between methods and other statements, 17 | 0:53 so for example here it says there should be two blank lines 18 | 0:57 between this method and anything else, 19 | 0:59 somewhere down here two blank lines, two blank lines, 20 | 1:02 I can go and fix this, but if I hit Command+Alt+L 21 | 1:04 it will actually fix all of those spaces for us. 22 | 1:07 If we look within a method, 23 | 1:09 PEP 8 says there should be either zero or one blank line 24 | 1:12 between any section of that method, 25 | 1:15 and use the blank line sparingly but to indicate a logical grouping; 26 | 1:18 so you can see maybe we wanted to define x 27 | 1:20 and later have this if statement, that would be fine, 28 | 1:23 but if we go farther you can see "hey, PEP 8 warning, too many blank lines", put that back. 29 | 1:28 Last thing that I'll highlight is around classes, 30 | 1:31 so if we have a class called AClass here, you can see it has three methods, 31 | 1:36 and just like functions, it should have two blank lines separated in it 32 | 1:39 from other module or level things like the top of the file, 33 | 1:42 the method down here, and so on, 34 | 1:45 but within the class, there should be one blank line between methods, not two. 35 | 1:49 So that's different than functions, when you are looking at methods within a class, 36 | 1:52 it's a little more tightly grouped as you see here. 37 | 1:56 So let's look at that in a diagram, here we have our method, 38 | 1:59 you can see the little dashes indicate spaces, 39 | 2:01 which have all of our symbols within our method indented four spaces, 40 | 2:04 additionally four more spaces for loops and conditionals 41 | 2:07 and more and more as you nest those things, right, 42 | 2:10 there should be two blank lines between methods, classes 43 | 2:13 and other symbols defined within your module, 44 | 2:16 and finally, PEP 8 recommends that you don't have lines longer than 79 characters, 45 | 2:20 although there is a lot of debate around the value of exactly 79, 46 | 2:24 but you know, it's a guidance, 47 | 2:26 basically try to avoid having super long lines 48 | 2:29 but don't do so by changing your code structures so much that it becomes small, 49 | 2:33 so for example don't, like, create all your variables to just be one character 50 | 2:37 so that when you combine them in some expression they still fit on the line, 51 | 2:41 you would be better off using the continuation, 52 | 2:43 but the idea of not having really long lines of code that's a good idea. -------------------------------------------------------------------------------- /transcripts/05-Generators/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 Next, let's talk about testing for containment and various sequences 2 | 0:05 If you want to look for an item in a set, 3 | 0:07  in a dictionary, in a list, those types of things, 4 | 0:10 and you are new to Python you might look for some kind of find 5 | 0:13 or index of type of method on the type itself. 6 | 0:17 But in Python, we have a special keyword to do this test, 7 | 0:20 over here in PyCharm we have a list, 8 | 0:22 a set and a dictionary and the way we test 9 | 0:24 for containment in them all the same, 10 | 0:26 so if we'd run it, you can see we are just printing out these values 11 | 0:29 and if you look at the numbers, you probably recognize them 12 | 0:33 as the Fibonacci sequence up to 34 anyway 13 | 0:36 or five here where I had to write them out, 14 | 0:38 so what we are going to do is we are going to parse out 15 | 0:40 a number gathered from the user, 16 | 0:42 and then we are going to test whether this is in the set. 17 | 0:45 So, here we'll just do a few "if" tests and maybe we can do this 18 | 0:48 as a tertiary sort of expression, 19 | 0:51 so we can say "print" like so, so we are going to say 20 | 0:54 we'll print out something is in the set 21 | 0:57 and then we'll do out "if" test, we'll say "if n is in nums_list" 22 | 1:04 and then maybe say a list here, keep the same order, 23 | 1:07 otherwise we'll say "not in list". 24 | 1:10 All right, so the test here is "n in nums_list", all right, 25 | 1:14 so this actually goes through and it searches the elements in it 26 | 1:16 and it does a comparison not on index but by value, 27 | 1:20 and then it'll tell you "yes or no it's here", 28 | 1:22 then we could do the same thing as you'll see for the set 29 | 1:25 and we could also do it for the dictionary. 30 | 1:30 All right, let's run it and figure it out, 31 | 1:32 here it says enter a number to test for the small Fibonacci set or a sequence 32 | 1:36 and let's say well, say 21, 21 is in  the list, 21 is in the set, 33 | 1:41 but because I was lazy and didn't write them all out, 21 is not in the dictionary; 34 | 1:45 let's try again, how about 1, it should be in all of them yes, it's in. 35 | 1:48 So, "if item in container", this even works for strings, 36 | 1:54 so if we had some text here like "Why did the multithreaded chicken cross the street?" 37 | 1:59 do you know? Well it depends on when you ask it, 38 | 2:02 you'll always get a different answer, 39 | 2:04 this time we are going to get "Other side to the get". 40 | 2:08 So I could ask a question "word", so here we'll say 41 | 2:14 something, we'll do this not the tertiary way, we can say something like this, 42 | 2:17 "if word in text: print such and such is in such and such", 43 | 2:29 there, so we could say ask user for a word, they type it in, 44 | 2:32 we can do the same "in" test for a string, let's try, 45 | 2:36 first look for 7 which should not be there, now I'll look for chicken, 46 | 2:42 chicken is in the "Why did the multithreaded chicken cross the street", 47 | 2:45 let's try it again, this time we'll enter 2 and here we'll put a cat. 48 | 2:51 Right, so cat I don't believe appears in here. 49 | 2:53 Cat is not in this string. 50 | 2:56 All right, so let's see that in a graphic, 51 | 2:57 so here we are just going to work with dictionaries as you saw, 52 | 2:59 it's basically the same across the three types of containers we worked with. 53 | 3:02 Here we could try to directly index into this dictionary and say 54 | 3:06 I want the thing with key 2 but as we saw in other examples, 55 | 3:09 this could give us a KeyError, if it's not there, 56 | 3:11 so we might want to do this sort of check first style 57 | 3:14 so we could say "if 2 is in the dictionary", 58 | 3:17 then we can safely access it because we know it's not going to KeyError. -------------------------------------------------------------------------------- /transcripts/02-PEP8/5.txt: -------------------------------------------------------------------------------- 1 | 0:01 Let's talk about naming conventions. 2 | 0:03 PEP 8 actually says a lot about how you should name 3 | 0:05 your functions, methods, classes, and other symbols in your code. 4 | 0:08 Before we get to the specifics, 5 | 0:11 I just want to give you one of my core programming guidelines around naming, 6 | 0:14 and that is: Names should be descriptive. 7 | 0:17 Let's imagine you are writing a function. 8 | 0:19 If you write the function and then you start to put a comment 9 | 0:22 at the top to describe what that function does, 10 | 0:24 I encourage you to stop and think about just making the concise statement 11 | 0:29 about what that comment is the name of the function. 12 | 0:32 I am a big fan of Martin Fowler's reafctoring ideas 13 | 0:35 and especially the "code smells" that he talks about, 14 | 0:38 and "code smells" are things in code that are not really broken 15 | 0:41 but they kind of smell off, like a function that is 200 lines long, 16 | 0:47 it's not broken, it just doesn't seem right. 17 | 0:50 It sort of smells wrong, right, so breaking that 18 | 0:53 to a smaller bunch of functions through refactoring might be the solution. 19 | 0:57 And when he talks about comments he says 20 | 0:59 comments are deodorant for code smell, 21 | 1:01 instead of actually fixing the code 22 | 1:03 so it's remarkably descriptive and easy to understand, 23 | 1:06 people often put comments to say "here is this really hard to understand thing, 24 | 1:10 and here is the comment that says why it's this way 25 | 1:13 or what it actually does or maybe in my mind, 26 | 1:15 what it really should be named." 27 | 1:17 So my rule of thumb is if you need a comment 28 | 1:19 to describe what a function does, you probably need a better name. 29 | 1:22 That said, let's talk about how PEP 8 works. 30 | 1:24 So here is some code that is PEP 8 compliant. 31 | 1:26 We have a module called data_access and modules should all have short, 32 | 1:31 all lower case names and if needed for readability 33 | 1:35 you can put underscore separating them. 34 | 1:38 Next you see we have a content that does not change 35 | 1:41 during the execution of our code, it's called TRANSACTION_MODE 36 | 1:43 and I forced it to be serializable. 37 | 1:46 These should be all upper case. 38 | 1:48 Classes, classes have cap word names, like this, 39 | 1:51 CustomerRepository, capital C capital R. 40 | 1:55 Variables and function arguments are always lower case, 41 | 1:59 possibly separated with underscores as well; 42 | 2:02 functions and methods lower case, again, 43 | 2:05 possibly separated with underscores. 44 | 2:07 And if you happen to create your own exception type, 45 | 2:09 here we might want to raise a RepositoryError and it derives from the Exception class, 46 | 2:14 when you have exceptions they should always be named in error, 47 | 2:18 so "SomethingError". Here we have RepositoryError. 48 | 2:22 As you have seen in other cases, PyCharm will help us here, 49 | 2:24 notice I renamed other_method, here to be Javascripty style 50 | 2:28 with the capital M and not the underscore, 51 | 2:31 you hover over it PyCharm will say 52 | 2:34 "no, no, no, you are naming your functions wrong." 53 | 2:36 It'll even let us hit Alt+Enter and give us some options about fixing this up, 54 | 2:41 it doesn't quite understand how to rename it for us 55 | 2:44 but at least it gives us the option to go over here and do a rename. 56 | 2:46 Now this is not this renaming in place, but this is a refactor rename 57 | 2:49 across the entire project, so if we had hundreds of files, docstrings, 58 | 2:53 that sort of stuff, this would fix all of those. 59 | 2:57 It'll show us where it's going to change, 60 | 2:59 here we don't have anything going on really so it's just this one line. 61 | 3:02 Now, our warning is gone. -------------------------------------------------------------------------------- /transcripts/06-methods/7.txt: -------------------------------------------------------------------------------- 1 | 0:01 The next Pythonic concept I want to explore is the relationship 2 | 0:03 between dictionaries and keyword arguments. 3 | 0:08 Over here we have the same function that we explored previously, 4 | 0:11 it takes 3 arguments, a name, a greeting and a number of times 5 | 0:16 to greet that person with that greeting. 6 | 0:18 So we could say "Michael", I'd like to say "Hello" to Michael 3 times, 7 | 0:22 something like that. 8 | 0:24 And we saw that we could use keyword arguments 9 | 0:26 even to change the order or to skip over top some of the default values, 10 | 0:30 so here we can say greeting and then name, 11 | 0:32 using the keyword arguments and skip the times. 12 | 0:36 So let's just run this really quick to see how it works, 13 | 0:39 "Hey, you are out of order Michael!", brilliant. 14 | 0:42 So suppose that I had a dictionary instead, something like this, 15 | 0:51 where we have a name a greeting and times and the keys in the dictionary 16 | 0:55 correspond to the keyword arguments that we are able to use in the method, 17 | 0:59 so there is a way to take this dictionary and turn it into a function call 18 | 1:04 or rather parameters to a function call via keyword arguments, 19 | 1:09 so we could say display_greeting() and we can unpack the dictionary 20 | 1:13 saying **, so it says instead of pass it as a dictionary, unpack it a keyword arguments 21 | 1:18 where you have the names are the keys and the values are the values. 22 | 1:24 Let's see how that works. 23 | 1:27 All right, so "Hey you are out of order, Michael!" 24 | 1:29 from up here and then we said "Ted, long time no see", do that 6 times. 25 | 1:33 That's excellent. 26 | 1:35 Now this concept of ** representing a transformation of a dictionary 27 | 1:40 works in both directions as well, 28 | 1:42 so up here I could say **kwagrs, 29 | 1:46 now this is much like the *args we had before, 30 | 1:50 that was allowing us to pass an arbitrary number of additional parameters, 31 | 1:54 this lets us pass an arbitrary number of additional keywords 32 | 1:58 so if we come over here now, 33 | 2:00 and let's go and just print out what kwargs, we'll say, "kwargs =" 34 | 2:08 so if we'd run it, you can see it's empty but it looks kind of like a dictionary and 35 | 2:12 in fact it is a dictionary. How do we use this? 36 | 2:16 Let's come over here and let's wrap this a little bit, let's say additional=2, mode=7. 37 | 2:23 So now, these do not correspond to any parameters 38 | 2:27 named or otherwise in our display greeting, 39 | 2:30 they will be gathered up into this additional kwargs here and pass along 40 | 2:33 and we can do whatever we want with it, 41 | 2:35 we can check for the existence of mode and do something with that, theoretically. 42 | 2:40 All right, let's see how this runs. 43 | 2:42 So now we pass our items: the greeting, the name, the times, 44 | 2:46 we've also got these two additional arguments to pass in here, so in functions, 45 | 2:51 you'll see this **name and the convention is for that name to be kwargs 46 | 2:57 but it doesn't have to be, you'll see this serving two roles, 47 | 2:59 either this takes keyword arguments and turns them into a dictionary in this case, 48 | 3:04 or the reverse, it takes a dictionary and turns it into keyword arguments. 49 | 3:10 Let's see that in a graphic. 50 | 3:11 So here we have the same function, you can see we have the **kwargs, 51 | 3:16 here you can see we are passing the greeting, 52 | 3:18 the name the times as they are via its default value, 53 | 3:20 we are passing another=2 and you can see we get our greeting 54 | 3:24 and kwargs is a dictionary with another as it's one of its keys. 55 | 3:29 In the reverse, we can take a dictionary and use the **dictionary name 56 | 3:36 or a **pointer to unpack the dictionary 57 | 3:39 as a set of keyword arguments to a method. -------------------------------------------------------------------------------- /transcripts/11-for-humans/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Let's round out the course with a little bit of Python for Humans, 2 | 0:03 and that's a bit of a play in words from some of the most popular 3 | 0:07 open source projects that really add power to Python. 4 | 0:11 This section we are going to look at two separate external packages 5 | 0:15 and how simple and lovely they make it to work with certain types of data 6 | 0:19 and we are going to do this because it really is meant to be a stand-in 7 | 0:22 for look at open source, look at PyPi and look to the community 8 | 0:27 and ecosystem before you start working on your project 9 | 0:31 for all the things that you can build your project from. 10 | 0:34 There is an amazing set of lego blocks out there 11 | 0:36 all you have to do is know to reach for them. 12 | 0:38 So if you find yourself thinking "hm, how am I going to 13 | 0:41 implement this bit?" of whatever subsystem you are working on in Python, 14 | 0:45 stop and do a little research first. 15 | 0:47 For example, if you are going to work with user accounts, 16 | 0:50 and you need to store user names and passwords, 17 | 0:53 you hopefully know that you should hash those passwords. 18 | 0:57 Did you know you should use separate salt per user 19 | 1:00 to go into hashing their password? 20 | 1:02 Did you know that you should fold that hashing over and over and over again 21 | 1:05 to make it computationally difficult? 22 | 1:08 Not five times or ten times but something like a hundred thousand times. 23 | 1:11 So if you are thinking oh my gosh, this is going to be a lot of work, 24 | 1:14 that's one way to think about it, the other one could be "oh, 25 | 1:17 there is a really great library for exactly that thing called passlib", go grab that, OK? 26 | 1:21 So when we look at these two packages yes, 27 | 1:24 it's great that you know about them, 28 | 1:25 but the most important takeaway is not the details of the two packages 29 | 1:29 we are going to focus on, but really the concept of there are so much out there 30 | 1:33 to work from you really should start there. 31 | 1:37 Right, so the first place to look is probably PyPi, 32 | 1:39 the Python package index and you can see at the time of this recording, 33 | 1:43 there is 82 000+ packages there, so about 300 new unique packages a day, 34 | 1:49 and the power of what's available here really is the main reason 35 | 1:53 why Python is popular. 36 | 1:54 Another really important place to look for Python libraries and tools is of course GitHub. 37 | 2:00 So there is this really cool site called GitHut.info, let's check that out quickly. 38 | 2:05 So here you can see, these are all the popular languages on GitHub, 39 | 2:10 and here is their active repositories, the total number of pushes, number of forks, 40 | 2:14 how recent it was, what year it appeared and you can actually hover over these 41 | 2:17 and it tells you all sorts of things, 42 | 2:19 so for example we could compare say Python to C#, to Ruby 43 | 2:25 and you can see that Python is more popular than Ruby, way more popular than C#, 44 | 2:30 anyway, GitHub is such a central part of modern software development 45 | 2:33 and look where Python lives, we have Python, 46 | 2:36 we have Java, and we have Javascript. 47 | 2:39 Now, my interpretation of this data is that Javascript is not really earning its position here, 48 | 2:45 I think this counts the Javascript that are in the Java websites, the Python websites, 49 | 2:50 the PHP websites, the Ruby websites, the C# websites, and so on, 50 | 2:54 so I think Javascript is being over counted here, so either way, 51 | 2:57 Python is either 3 or 2 on this list, so certainly, look to GitHub, 52 | 3:01 look to PyPi for your packages and your features 53 | 3:05 before you just write the whole thing from scratch, 54 | 3:08 remember, Python comes with batteries included, 55 | 3:10 these two places are often where you find those batteries. -------------------------------------------------------------------------------- /code/ch_01_pep8/_01_pep8.py: -------------------------------------------------------------------------------- 1 | # ******** Part 2 - code layout ************************** 2 | 3 | 4 | class AClass: 5 | def m1(self): 6 | pass 7 | 8 | def m2(self): 9 | pass 10 | 11 | def m3(self): 12 | pass 13 | 14 | 15 | def some_method(a1, a2, a3): 16 | """ 17 | some_method returns the larger of 1 or 2 18 | 19 | :param a1: First item to compare 20 | :param a2: Second item to compare 21 | :param a3: Should reverse 22 | :return: 1 or 2 23 | """ 24 | x = 1 25 | 26 | if x > 2: 27 | return 1 28 | else: 29 | return 2 30 | 31 | 32 | some_method(1, 1, 2) 33 | 34 | 35 | def other_method(): 36 | pass 37 | 38 | 39 | def other_method2(): 40 | pass 41 | 42 | 43 | def other_method3(): 44 | pass 45 | 46 | 47 | s = "Text" 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | # Use 4 spaces per indentation level. 66 | # spaces, never tabs 67 | def method(): 68 | four_spaces_indented = True 69 | more_vars = 1 70 | 71 | 72 | # Limit all lines to a maximum of 79 characters. 73 | text = "This is a string which is longer than 79 characters. This is not encouraged but will execute and run OK." 74 | 75 | # blank lines 76 | # Surround top-level function and class definitions with two blank lines. 77 | # Method definitions inside a class are surrounded by a single blank line. 78 | # Use blank lines in functions, sparingly, to indicate logical sections. 79 | 80 | # ******** Part 3 - Naming conventions ************************** 81 | 82 | # Modules should have short, all-lowercase names. Underscores can be used 83 | # in the module name if it improves readability. 84 | 85 | # Class names should normally use the CapWords convention. 86 | 87 | # Because exceptions should be classes, the class naming convention 88 | # applies here. However, you should use the suffix "Error" 89 | 90 | # Function names should be lowercase, with words separated by 91 | # underscores as necessary to improve readability. 92 | 93 | # Arguments 94 | # Always use self for the first argument to instance methods. 95 | # Always use cls for the first argument to class methods. 96 | 97 | # If a function argument's name clashes with a reserved keyword, 98 | # it is generally better to append a single trailing underscore rather 99 | # than use an abbreviation or spelling corruption. Thus class_ is better than clss 100 | 101 | # Constants are usually defined on a module level and written in all capital 102 | # letters with underscores separating words. Examples include MAX_OVERFLOW and 103 | # TOTAL. 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | # ******** Part 1 - Imports ************************** 115 | import collections 116 | 117 | # no: import collections, os, multiprocessing 118 | # import collections 119 | # import os 120 | # import multiprocessing 121 | 122 | 123 | # from my_module import path 124 | from os import chdir, chflags, chown 125 | 126 | # from os import * 127 | 128 | # ERROR: 129 | # import sys, os, multiprocessing 130 | 131 | # But multiple symbols are OK 132 | 133 | # Imports should be grouped in the following order: 134 | # 135 | # standard library imports 136 | # related third party imports 137 | # local application/library specific imports 138 | # 139 | # You should put a blank line between each group of imports. 140 | 141 | 142 | # Wildcards: 143 | # no: import collections, os, multiprocessing 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | # There meaningless lines are here to prevent PyCharm from warning about 155 | # unused imports and such. We want to see real warnings only. In a 156 | # legitimate app, those other warnings would be useful but not here. 157 | s = sys 158 | o = os 159 | m = multiprocessing 160 | z = path 161 | z = chmod 162 | z = chown 163 | m = mean 164 | c = collections 165 | -------------------------------------------------------------------------------- /transcripts/11-for-humans/3.txt: -------------------------------------------------------------------------------- 1 | 2 | 0:01 Next, sticking with our some amazing package for humans, let's look at Records. 3 | 0:05 Records is also by Kenneth Reitz and I chose his work  because I really admire him 4 | 0:08 and I think it brings a great simplicity and powerfulness all together at the same time. 5 | 0:13 So here we have a thing called Records, it's an improvement on the built in DB API 6 | 0:18 that lets you query databases in a really nice way, 7 | 0:22 see it supports things like Postgres, MySql, SQL Server, Oracle and so on. 8 | 0:27 So let's go see how we use this to access to the simple little database. 9 | 0:32 Now here I have a little bit of starter code and we are going to go, 10 | 0:35 let's just look at this little support file, it's going to go and find this demo_db.sqlite 11 | 0:40 and it's going to generate a connection string to that file. 12 | 0:44 And if we look what's in here, let's go over here, 13 | 0:49 you can see there is some ids, x y and values. 14 | 0:52 And so what I want to do is do a query based on this value, 15 | 0:55 so I want to find all the measurements that have a value greater than 0.9, 0.95 and so on. 16 | 1:01 So how do we do that? Well, I've already installed Records, 17 | 1:05 here you can see Records and hey we have the latest version, 18 | 1:08 cool, but again we could go install it from PyPi, with pip or with PyCharm, 19 | 1:12 so we'll say "import records", and the way we get started is 20 | 1:16 we create a database and we give it the connection string, like so, 21 | 1:21 and then we say "db.query()" over here and we just give it some SQL. 22 | 1:27 Remember the tagline, "just write SQL", so we are going to come over here 23 | 1:30 and we are going to do a query, now what I am going to write 24 | 1:33 would normally just be a string, this is SQL embedded in Python, 25 | 1:37 so there is no less support for whatever that means anyway, 26 | 1:40 but because PyCharm over here has this database registered 27 | 1:43 when I drag it across like that, watch what happens, 28 | 1:45 this is, I'll never get tired of seeing this, 29 | 1:47 so if I type "SELECT" it's going to start to think oh, 30 | 1:50 maybe you are writing a database query, not yet, if I say "* FROM", now it has, OK, 31 | 1:56 we have two of these databases active so "SELECT * FROM" 32 | 2:00 and now notice how I got syntax highlighting inside the string 33 | 2:04 and now as I hit space I actually get completion on the column, 34 | 2:08 so we get id and value, x and y, I want to say "where the value is > than 0.9", 35 | 2:15 so there is a query, that's pretty easy, now let's loop over it, 36 | 2:19 so "for r in" and let's just print it out to see what the heck came back here, 37 | 2:25 so if I run this, there, you can see we got our records back from the database, 38 | 2:31 and notice there is a bunch and they are not quite ordered the way I'd like, 39 | 2:34 so let's say "ORDER BY value DESC", how easy is that? 40 | 2:41 Connection string, create a database, create a query, done. 41 | 2:46 And notice, we can come out if we just want to print the value, 42 | 2:49 these have access to all the values, if I want to get just say 43 | 2:54 the top 3 highest measurements or let's say top 5, 44 | 2:57 we can use slicing, right on the results, boom, there is the top 5, beautiful. 45 | 3:03 So again, here is how you use Records, one import statement, 46 | 3:06 create the database, run the query, done. 47 | 3:09 3 lines of code including the import and connecting to the database. 48 | 3:13 Again, just another example of how important it is to look around 49 | 3:17 at what's available when you are working with Python apps, 50 | 3:20 there is so much out there, one of the biggest challenges is actually finding it, 51 | 3:24 hopefully, you are inspired to look around by some of these examples. 52 | 3:28 All right so the final takeaway of this whole section is: 53 | 3:31 "It is Pythonic to leverage PyPi and open source 54 | 3:34 more than it is to implement the coolest clever algorithm on your own code, 55 | 3:39 and keep it private." -------------------------------------------------------------------------------- /transcripts/10-tuples/3.txt: -------------------------------------------------------------------------------- 1 | 0:01 Sometimes you need to return more than one value from a function. 2 | 0:04 Let's see what the story of that is in Python. 3 | 0:07 So over here we have a non-Pythonic way 4 | 0:09 to return more than one value from a function, 5 | 0:11 now in Python, we don't have the concept of reference parameters, 6 | 0:16 passing a pointer by reference, 7 | 0:19 we  just have passing the pointer, which lets us work with the values. 8 | 0:22 Some languages you can do things like this, you can say "int & val 1, int & val 2", 9 | 0:29 that would be like C++, and say C# you might say like this: "out" or "ref" or "in out", 10 | 0:37 things like this, we could even, if this was pass by value, 11 | 0:41 we could even pass a pointer and then let it change, 12 | 0:44 there is lots of things that some languages let us do, 13 | 0:47 Python doesn't let us do that. 14 | 0:48 So here is one way which we can kind of do this in a kind of a hokey way 15 | 0:52 so here we are passing in some value to work with, 16 | 0:54 we want to compute 2 values and return both the values, 17 | 0:57 so here we are going to pass in a list, 18 | 0:59 and if the list is empty we are going to make a spot for 2 entries, 19 | 1:02 otherwise, if the list is not length 2, we are going to complain 20 | 1:05 and say "Oh this is not really what we are looking for", 21 | 1:07 we wanted either a list with 2 elements 22 | 1:09 or an empty list so that we can stuff the two return values into them. 23 | 1:13 Do a quick little bit of math and back here we get the values out 24 | 1:17 and we pull them out, this is super non-Pythonic. 25 | 1:21 This is bad, so the question is: "Can we do better?" 26 | 1:24 First of all, let's see if we pass in 7 27 | 1:27 that we are going to get the right values. 49 and 18.52 28 | 1:31 Those are right values, but the code, not so right, 29 | 1:34 so let's take this and have a good version. 30 | 1:37 Keep that one down here, we'll make this one to be Pythonic, 31 | 1:40 we'll just call it out_params, and we are going to do something entirely different. 32 | 1:43 We are going to get rid of all the stuff, this link, all this junk, 33 | 1:46 watch how much simpler this gets. 34 | 1:49 So we'll have, let's say, return value 1, 35 | 1:53 those are not good names in general for variables 36 | 1:55 but maybe just to make a case of look 37 | 1:57 these are the two values we are returning, we'll call of this. 38 | 2:00 So we can come down here and we can return a tuple and we can say that 39 | 2:04 just say "r1, r2", that defines a tuple, that's one thing, we'll return that. 40 | 2:09 That's a little bit like what we were doing before with our list, 41 | 2:12 so we could like say "return a list" but the thing that's cool 42 | 2:16 is the tuple unpacking lets us get at that value really easy, 43 | 2:20 so we can come over here and we can say we would like to call this function 44 | 2:22 out_params with the value 7, we'd like to capture the values, remember, 45 | 2:26 it's coming back as a tuple, so we can unpack that into individual values 46 | 2:29 and give basically the appearance that our method is returning more than one values, 47 | 2:34 we can say "v1, v2 = this", I can print out let's say the good version 48 | 2:40 instead of this funky stashing stuff in the list, we just say v1, v2. 49 | 2:44 Now it literally looks like this method returns more than one value, 50 | 2:48 but the trick that facilitates it is of course tuples and tuple unpacking. 51 | 2:52 Perfect, besides a little bit of spacing, it looks identical. 52 | 2:56 There, identical, so much better. 53 | 3:00 So we saw we can fiddle with collection types to make it sort of possible 54 | 3:06 to return more than one value, this is really a bad idea, don't do this kind of stuff, 55 | 3:10 instead, leverage the ability to create and return as single tuple 56 | 3:14 and then unpack them as if they were multiple values, 57 | 3:17 so here we are calling compute values, return it to tuple, 58 | 3:19 we are unpacking that into two variables we are calling "b2", and "b32", 59 | 3:24 and we are printing them out. 60 | 3:26 Wonderful. -------------------------------------------------------------------------------- /transcripts/10-tuples/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Tuples play a central role in Python, we are going to look at variety of techniques 2 | 0:05 and ways to use tuples that are particularly Pythonic, 3 | 0:09 let's start with assignment and unpacking, and we'll start in code. 4 | 0:13 So, you probably know that tuples are defined like this, so it could be a number, 5 | 0:19 another number, a string, even be like an array. 6 | 0:23 So if we wanted to just look at the our little creation here, we can go like this, 7 | 0:28 we'll see that it's actually not the parentheses that have anything to do with this, 8 | 0:34 usually, we could just as well write like this, and we get the same output, 9 | 0:38 it's the commas, not the parentheses that make the tuple. 10 | 0:44 In fact, we can come down here and can have a shorter one, like this, 11 | 0:49 sometimes you want to have a tuple with just one element in it, we could write this, 12 | 0:53 there is a tuple of length 1, let's find out. There, a tuple of length 1. 13 | 1:00 So that tuples, they can't grow after they are created, 14 | 1:03 they are basically immutable objects that it can be added to or moved, things like that. 15 | 1:08 So that's not the Pythonic thing, that's just tuples. 16 | 1:12 We get values out of them like this, if we wanted to say 17 | 1:14 print out the word "cat" that's in the second index position, zero-based, 18 | 1:18 so there we'd print "cat", if we didn't actually do this, there that prints "cat", 19 | 1:28 so the first thing we want to look at is unpacking these into variables, 20 | 1:32 let's go over here and work with the shorter version, 21 | 1:34 so let's suppose we had the number 7 and the word "cat" and that was all, 22 | 1:38 if I want to have 2 variables, one for the number and one for the... let's say animal, 23 | 1:45 I could say "n for number = this" and I could say "a for animal = that", 24 | 1:52 that would not be Pythonic, instead, what you would say is "n, a = t", 25 | 1:57 and Python will unpack the first value into the first thing here, 26 | 2:01 the second value into the second thing there, and so on. 27 | 2:04 Now, if we had another one, like another number here, and we try to run this, 28 | 2:08 you'll see it crashes because it says too many values, 29 | 2:11 so if we are doing that we would typically say 30 | 2:13 I don't care what this value is and use an underscore to say 31 | 2:16 "please ignore it", so let's run that. 32 | 2:18 And PyCharm is just saying "look you assign these values 33 | 2:22 and you never use them here", so let's do this. 34 | 2:24 Now, maybe we should show you what numbers came out 35 | 2:26 or what values came out of there, so we'll say like so, "n is 7", "a is cat", beautiful. 36 | 2:32 And underscore, well we could grab it but we don't care, that's the whole point of it. 37 | 2:37 So this concept lets us assign values in a single line 38 | 2:40 so I could say "x, y = 1, 2", then I could print "x" and "y" and I would get 1 and 2. 39 | 2:49 Finally, this tuple unpacking is a very important when we are talking about loops, 40 | 2:55 remember our numerical "for...in" loop where it gives us both index and the item, 41 | 3:00 we wrote something like this: "for index, item in enumerate", something like this, 42 | 3:06 for enumerating over a collection, we'll get the index and the item, 43 | 3:09 so 0 had 1, "cat" and so on, there, 0 goes to 1, 2 goes to "cat", 44 | 3:17 well this returns a tuple and we unpack them into index and item, 45 | 3:22 so this tuple unpacking is super important 46 | 3:24 and we'll see more of it as we go through this chapter. 47 | 3:28 So we saw we can create a tuple, we can unpack it into a variety of values, 48 | 3:31 should we have a tuple of 4 values, we unpack it into greeting and one into enclosing, 49 | 3:36 then we just print them out, you see the values come out 50 | 3:39 as if they had been pulled out individually, 51 | 3:41 we also saw that enumerate returns tuples 52 | 3:44 and the way that we actually separate the values in a really nice clean way 53 | 3:47 maybe didn't even notice this one has happened 54 | 3:50 is we are actually using tuple unpacking into those two items 55 | 3:53 as it comes back from enumerate. -------------------------------------------------------------------------------- /transcripts/07-modules-packages/5.txt: -------------------------------------------------------------------------------- 1 | 0:01 One of the challenges of deploying your code, your set of Python scripts, 2 | 0:04 to run on other systems, is to communicate exactly what you depend upon, 3 | 0:09 so that you make sure that that system has the right things installed. 4 | 0:13 We saw that virtual environments allow you to control this, 5 | 0:16 but how do you state it, how do you help someone grabbing your library 6 | 0:20 or your app know what they've got to install? 7 | 0:22 Let's look at that. 8 | 0:24 Over here I have a little app, it's going to do some downloading 9 | 0:27 and it's using 3 packages, 10 | 0:28 now these would be interspersed throughout your app of course, right, 11 | 0:31 but in this case, we just have them listed here. 12 | 0:35 So we are using a request to download some the homepage in Google, 13 | 0:38 show the status code, imagine somewhere else using records for SQL, 14 | 0:42 over here we are using user accounts and correct hashing with passlib and so on. 15 | 0:47 So if I run this in PyCharm, on my system, it works great, 16 | 0:50 I've got 200 back from Google. 17 | 0:52 Let's imagine I was going to take the same code and run it somewhere else; 18 | 0:56 to simulate running it on another machine, 19 | 0:59 let me go to that virtual environment we created before and I'll just activate it, 20 | 1:02 so I'll say ".user/screencaster/python_environments" 21 | 1:05 this is what I created, I'll say "activate", OK the prompt changes, 22 | 1:08 now if I say "pip list", you'll see I don't have all the things we need, 23 | 1:13 I do have requests but I don't have records or passlib, 24 | 1:17 let me see what happens if I try to run that program. 25 | 1:21 So we are going to run it and oh, that didn't go so well, 26 | 1:25 I guess we needed records, we would go "pip install records" and we tried again, 27 | 1:29 we'd see passlib and that's because- and that would be actually the easy case 28 | 1:33 because it's all in one file, but normally, 29 | 1:35 it would be spread out so eventually you would get to some action 30 | 1:38 that import a module that "oh, well that crashes too" 31 | 1:41 because there is some other missing piece. 32 | 1:43 So how do we solve this? 33 | 1:44 It's quite simple, we can come over here and I would put whatever my code is, 34 | 1:49 I want to run, I would give it a requirements.txt. and in here, 35 | 1:55 the format is super simple, you just list the names, 36 | 1:58 one per line, of the packages you depend upon, 37 | 2:03 so if I copy this and I come back over to my 38 | 2:06 "other system" where I have, remember, 39 | 2:10 I am using this pip from here, 40 | 2:12 if I say "pip install -r" for requirements file and I give it that, 41 | 2:19 it's going to look at all the requirements we've specified, download them, 42 | 2:26 and make sure the system is ready to roll. 43 | 2:29 As long as I have kept that requirements file, 44 | 2:31 up to date with what my app actually uses, we're good. 45 | 2:35 And PyCharm if we are working in certain types of environments, 46 | 2:38 actually is really good about managing that for us, 47 | 2:41 if it sees you work inside of a package like say a pyramid web app, 48 | 2:44 and it sees you using some external package, 49 | 2:46 that is not listed in your setup install requirements 50 | 2:49 it'll actually automatically put them there for you. 51 | 2:51 OK, so now if we say "pip list", this looks like a much better chance, 52 | 2:56 let's try running our little app again, we should just see 200 53 | 2:59 as it talks to Google and gets good response code, server says: 200. 54 | 3:05 So that's the requirement.txt file, add all of your requirements to it, 55 | 3:09 just one package name per line and then "pip install -r requirements.txt". 56 | 3:14 In a graphic, our app is using some external modules, or external packages, 57 | 3:19 here we are just showing requests, in order for this script to run at all, 58 | 3:23 we are going to need a request installed on the system that's going to be running it. 59 | 3:27 How do we communicate that? 60 | 3:28 Well, we have our requirements.txt that lists out our various requirements, 61 | 3:31 then we "pip install -r", give it the requirements text file and boom, 62 | 3:37 problem solved. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Write Pythonic Code Like a Seasoned Developer Course Demo Code 2 | 3 | [![Pythonic Code Course Welcome Video](https://raw.githubusercontent.com/mikeckennedy/write-pythonic-code-demos/master/readme_resources/pythonic-course-welcome-video.png)](https://vimeo.com/171562581) 4 | 5 | [Jump to source code: [write-pythonic-code-demos/tree/master/code](https://github.com/mikeckennedy/write-pythonic-code-demos/tree/master/code)] 6 | 7 | One of the special concepts in Python is the idea of writing idiomatic code that is most aligned with the language features and ideals. In Python, we call this idiomatic code Pythonic. While this idea is easy to understand, it turns out to be fairly hard to make concrete. 8 | 9 | This course will take you on a tour of over 50 of the more popular and useful code examples demonstrating examples of Pythonic code. In the examples, you'll first see non-Pythonic code and then the more natural Pythonic version. 10 | 11 | Topics covered include the expansive use of dictionaries, hacking Python's memory usage via slots, using generators, comprehensions, and generator expressions, creating subsets of collections via slices (all the way to the database) and more. Several of these are Python 3 features so you'll have even more reason to adopt Python 3 for your next project. 12 | 13 | ## What topics will we cover? 14 | 15 | This course covers over 50 concrete programming tips to write more Pythonic code. These tips are grouped into the following broad categories. 16 | 17 | * Pythonic Foundational Concepts 18 | * Dictionaries 19 | * Generators and Collections 20 | * Methods and Functions 21 | * Modules and Packages 22 | * Classes and Objects 23 | * Loops 24 | * Tuples 25 | * Python for Humans 26 | 27 | See [the full course table of contents](https://training.talkpython.fm/courses/explore_pythonic_code/write-pythonic-code-like-a-seasoned-developer#full-course-outline) below. 28 | 29 | ## Join the course 30 | 31 | https://talkpython.fm/pythonic 32 | 33 | 34 | ## What is Pythonic code and why does it matter? 35 | 36 | One of the special concepts in Python is the idea of writing idiomatic code that is most aligned with the language features and ideals. In Python, we call this idiomatic code Pythonic. When you write Pythonic code, you are leveraging over 25 years of experience of many thousands of developers. You are writing code that is expected and tune in the CPython runtime. Most importantly perhaps, you are writing code that is easily read and understood by your fellow and senior Python developers. 37 | 38 | If you are building an open source product, it will be easier for other contributors to join in if your code is Pythonic. If you are running a software team, it will be easier to onboard Python developers new to your company. 39 | 40 | On the flip side, if you are somewhat new to Python, you may be broadcasting this loud and clear to everyone listening: your teammates, interviewers if you're looking for a new job, audience members if you're giving a public presentation. This is less than ideal. 41 | 42 | Finally, many of the over 50 tips covered in this course that are considered "Pythonic" allow you to write more readable code, more maintainable code, and more efficient code. So in some sense, you can think of this course as an effective Python course in its own right. 43 | 44 | ## Who is this course for? 45 | 46 | The course is for beginner to intermediate Python developers looking to hone their Python programming skills and become true professionals in the Python space. It is not a "Learn Python from Scratch" course and assumes you are familiar with language constructs such as modules, functions, classes, and more. 47 | 48 | If you are looking to learn Python from scratch, please consider my Python Jumpstart by Building 10 Apps course. 49 | 50 | ## This course will cover Python 3 51 | 52 | The course will cover Python 3 (3.5 specifically), but 96% of what you learn will be translatable back to Python 2 with virtually no effort. The few features that are Python 3 specific will be highlighted as such (e.g. new dictionary merging syntax). 53 | 54 | Python 2 will literally become unsupported in less than 4 years (in 2020). That's coming up faster than people realize and focusing on Python 3 going forward is important. 55 | -------------------------------------------------------------------------------- /transcripts/06-methods/5.txt: -------------------------------------------------------------------------------- 1 | 0:01 The first Pythonic technique that we can use to address this 2 | 0:03 what you might call a shortcoming, 3 | 0:05 at least a different way of programming 4 | 0:07 around method overloading is with default values. 5 | 0:10 Let's have a look. 6 | 0:12 Here in PyCharm we have some simple method called display_greeting, 7 | 0:16 it's completely contrived but it'll totally work for our purposes. 8 | 0:19 You give it a name, you give it a greeting, 9 | 0:21 so the name might be Jeff, the greeting might be "Good morning" 10 | 0:25 and it will actually say it over and over and over 11 | 0:27 so you can get really excited and say 12 | 0:29 "Good morning Jeff, Good morning Jeff, Good morning Jeff", 13 | 0:32 and in fact this one will do that 3 times, 14 | 0:34 this one on the other hand will just say "Good day Michael", one time. 15 | 0:37 However, there is a problem with these down here, 16 | 0:39 we might like to write code like this, 17 | 0:41 we might just like to say well, let's just greet Mark, 18 | 0:44 and there might be some way to write code that there is a default greeting 19 | 0:48 to be given like "Hello", and the number of times might be 1 20 | 0:51 so like if we don't pass the greeting and the number of times, 21 | 0:56 we'd like to just say "Hello Mark", one time. 22 | 0:58 But if we say "Mark" and "Good afternoon" 23 | 1:00 we'd like it to say "Good afternoon Mark" one time. 24 | 1:03 Of course we can specify all the details and say "Good afternoon Mark" twice. 25 | 1:07 How do we go about this in Python? 26 | 1:09 Well, we use default values. 27 | 1:11 First of all, it's not going to love it the way it is, 28 | 1:13 you can see PyCharm's indicating issues and if we'd run it you will find them, 29 | 1:17 the first part worked just fine, "Good morning Jeff" and then did that 3 times, 30 | 1:21 the little error snuck in between there, and then it said "Good day Michael" 31 | 1:25 and then we started hitting the trouble. 32 | 1:27 Click here and it will take you right to the line of trouble, 33 | 1:29 yeah that's the one we expected. 34 | 1:31 So we can actually go up here and instead of having different signatures, 35 | 1:34 we can have default values, so if we'd like to have default greeting 36 | 1:38 we can come over here and we can say, right in line, "Hello". 37 | 1:42 So if we wanted to say Hello- name, 38 | 1:45 if you don't specify it, then we can do it this way. 39 | 1:48 Now, this would almost fix this line, except for we still need to deal with times 40 | 1:51 and the default values have to go after all the non-default values, so let's set this to 1, 41 | 1:56 that seemed reasonable; now, this actually fixes all of these errors, let's go. 42 | 2:01 Perfect, look, if this one here "Hello Mark", 43 | 2:05 this one said "Good afternoon Mark" one time, 44 | 2:08 this one said "Good afternoon Mark" two times, like so. 45 | 2:12 Finally with these default values we can actually put them in any order we want, 46 | 2:16 here we don't say the name of them we just say "Good afternoon 47 | 2:18 and two", we use them as positional values but I could say something like this, 48 | 2:22 I could say greeting is "Yo!", name is "Michael" and times is 4. 49 | 2:30 So we can put these in any order we want using keyword arguments 50 | 2:33 and we get "Yo Michael" 4 times. 51 | 2:37 More importantly, this lets us two things like skip over, 52 | 2:41 let me keep this here for you, more importantly his lets us skip over 53 | 2:45 some of the default values, so here I can say name is Michael, 54 | 2:48 skip over greeting and use its default "Hello" and then let's just say 2 times here. 55 | 2:53 So they should say "Hello Michael" 2 times at the end. 56 | 2:56 "Hello Michael", 2 times at the end. 57 | 2:59 All right, so in Python default values play a really important role 58 | 3:03 in doing what method overloading based on signature might have done 59 | 3:07 in other circumstances. 60 | 3:09 We'll see some more Pythonic ways to deal with this as well, 61 | 3:12 some other functional techniques we can use. 62 | 3:15 So, specifying default values here, lets us perform what in languages 63 | 3:19 like C# and C++ often are done through signature overloading 64 | 3:24 and having multiple methods that are distinguished by signature. 65 | 3:28 However, there is a warning here, 66 | 3:30 so for these default values there are some extremely serious gotchas 67 | 3:34 and certain circumstances will look at those at the end. -------------------------------------------------------------------------------- /transcripts/05-Generators/5.txt: -------------------------------------------------------------------------------- 1 | 0:01 So we've seen how powerful and amazing 2 | 0:03 the yield keyword is to build these generators. 3 | 0:05 In Python 3, there is actually a new feature that makes working with generators 4 | 0:09 as well as recursive functions even better. 5 | 0:14 So over here we have some code and what it's going to do is 6 | 0:18 it's going to go to some root directory 7 | 0:20 here I just grabbed the transcripts from my other class, 8 | 0:23 Python Jumpstart By Building Ten Apps, 9 | 0:26 and the transcripts are here in these demos folder, 10 | 0:28 and we want to just process through those, 11 | 0:31 so I've written this function called get_files() and I give it a root directory, 12 | 0:35 and it returns using yield the generator that we can iterate over and print them out. 13 | 0:40 So this is pretty straightforward, we are going to go to a directory, 14 | 0:44 this will be the top level directory, and we'll say "for each item I'd like to look at it", 15 | 0:48 let's build up it's full path and ask: "is it a file, 16 | 0:50 or is it a directory?", so if it's s file we'll say "yeah, 17 | 0:54 I found one of the files in this directory, 18 | 0:56 here go loop over this", but if it is itself a directory, 19 | 1:00 well let's just hold off on that for a minute. 20 | 1:03 So here if I run it you can see on my Mac I have this .DS_store thing 21 | 1:07 and then I have this txt file and then that's that. 22 | 1:11 So not many files. But it turns out there is subdirectories in here 23 | 1:14 and maybe I want to look inside them 24 | 1:16 if I had a function that could look inside a folder, 25 | 1:19 and tell you what was in it, tell you the files that are in it, 26 | 1:22 that would be awesome, I could use that, right? 27 | 1:24 Well, what do I have right here, boom, 28 | 1:27 so what we can do is we can use a concept called recursion 29 | 1:29 and if you are used to recursion this makes perfect sense, 30 | 1:32 but if you are not, it's quite interesting and sort of mind bending, 31 | 1:36 so the idea is from within this function we are going to call the same function 32 | 1:40 but with possibly, this time certainly, with different parameters, 33 | 1:44 so we are going to work our way down the directory tree 34 | 1:46 until there are no more subdirectories 35 | 1:48 then we will stop calling our function but as long as there are subdirectories, 36 | 1:51 we are going to be calling it like this, 37 | 1:53 Now this returns a generator of files and technically 38 | 1:56 it will return like a tree of generators, of files so how do I get the files out? 39 | 2:02 Well, I could write this, "for f in files, 40 | 2:05 get files" and then I could say "yield f" and so this is just the idea of recursion, 41 | 2:11 there is no special feature here, but let's just verify it works. 42 | 2:14 Boom, here you go, so you can see in app 10 I've got a couple of transcripts, 43 | 2:19 in the conclusion I've got a couple of transcripts, 44 | 2:22 there is quite a few files that were in subdirectories here. Lovely. 45 | 2:25 And notice we actually are printing the directory as well, 46 | 2:28 I'll turn that off for a minute. So those are just the files. 47 | 2:33 So like I said, this is straight up recursion, and this is kind of not-so-great, 48 | 2:38 we'd like to just say hey here are a bunch of items in a generator 49 | 2:43 and I would like to make all of those items part of my sequence, so in Python 3, 50 | 2:47 we can do this, we can say instead of just yield we can say "yield from get_files" 51 | 2:53 and full_item and this is basically replacing that loop above 52 | 2:57 so we can do it in kind of a inline sequence way, let's try it again, 53 | 3:00 we should get exactly the same output, 54 | 3:03 boom, we do, 55 | 3:04 beautiful, so "yield from", super helpful when you are doing recursion 56 | 3:08 or you want to just grab a bunch of items from some kind of generator 57 | 3:11 or set and throw them into your current set that you are trying to generate. 58 | 3:16 So here you can see we are calling "get items", 59 | 3:18 it's a generator because it's using the yield keyword 60 | 3:21 and we are also calling it recursively 61 | 3:23 and we are able to simplify that recursion using "yield from". 62 | 3:27 So remember, this is a Python 3 only feature and in fact, 63 | 3:29 it was introduced in Python 3.3 so it doesn't even work 64 | 3:33 in the early versions of Python 3. 65 | 3:35 That said, if you are working in 3.3 or above, 66 | 3:38 it's a really cool way to simplify  generators. -------------------------------------------------------------------------------- /transcripts/02-PEP8/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 The first PEP 8 recommendation that we are going to look at 2 | 0:03 is around importing modules and packages. 3 | 0:05 So let's switch over here to PyCharm and have a look at some not-so-great code. 4 | 0:09 You can see on line 7 here, we are importing collections, 5 | 0:12 the recommendation from PEP 8 is that all module level imports 6 | 0:16 should go at the top, unless there is some sort of function level import 7 | 0:19 you are doing kind of unusual, conditional things. 8 | 0:22 And notice there is a little squiggle under here, 9 | 0:24 and that's actually PyCharm saying "here is a PEP 8 violation, 10 | 0:27 a module level import is not at the top of the file." 11 | 0:29 So we can take this and put it at the top of the file, now everything is happy. 12 | 0:34 So let's look at few other things we can do, 13 | 0:37 we could say "from os import chdir, chflags, chown" 14 | 0:44 This is a good way to import a bunch of items 15 | 0:47 from os and not have to state their name, 16 | 0:49 we could do this a different way, using what's called a wildcard import, 17 | 0:53 we could say "from os import *" 18 | 0:55 now that would import the three listed above, 19 | 0:58 but it would also import every symbol defined in os. 20 | 1:01 Now what's wrong with this? 21 | 1:03 Imagine up here I imported another module, 22 | 1:05 "from my_module import path" so if I write this code, 23 | 1:10 line 4 and line 6, path is not going to be what you think it is, 24 | 1:14 because we are importing path here 25 | 1:16 but then we are importing every symbol from os 26 | 1:18 using what is called the wildcard import, 27 | 1:20 and os also has a path so it is going to overwrite the definition of path here. 28 | 1:25 So it's why it's always recommended to use this style of importing. 29 | 1:28 You may wonder why this is gray in PyCharm, 30 | 1:31 PyCharm is just trying to help us out saying 31 | 1:33 "here are some unused imports you can actually remove", 32 | 1:35 but if I write something like "c = chdir", 33 | 1:38 then that part will go away. 34 | 1:40 But of course, because we are importing the same thing twice 35 | 1:42 basically it's also saying, I'll move that. 36 | 1:45 Here we go, so now it says "you are using chdir, chown but not chflags", 37 | 1:49 down below it's using "chown". 38 | 1:52 All right, now "chflags", if I do something with that, it also lights up. 39 | 1:56 All right, so there was never a problem with that import, 40 | 1:59 that was just PyCharm trying to tell us "hey, look out, 41 | 2:01 you are actually not using that import." 42 | 2:03 Another mistake that people make is they might say something like this, 43 | 2:06 "import collections, os" and let's say "multiprocessing", 44 | 2:10 they may put multiple imports on a single line. 45 | 2:14 That also works just fine, however, again, 46 | 2:16 PEP 8 recommends that you put one import per line 47 | 2:19 so it's very clear line by line what you depend on here, 48 | 2:24 so we could fix this by saying, let me just put a "no:", something like that, 49 | 2:28 and we could of course fix this, like so, "import os", "import multiprocessing". 50 | 2:35 So this would be the recommended way to write what was on line 4. 51 | 2:39 So let's look at that import guidance a little more clearly. 52 | 2:43 Here we have at the top two bad styles of imports, 53 | 2:47 first line: "import sys, os, multiprocessing", on a single line; 54 | 2:51 PEP 8 says "do not import multiple modules on a single line", 55 | 2:55 and avoid "from module import star", these wildcard imports 56 | 2:59 because you may accidentally, unknowingly overwrite other imports. 57 | 3:03 So we have our better set of imports, 58 | 3:06 "import sys, import os, import multiprocessing", 59 | 3:09 and these of course are going to allow us to use "module name.symbol" name 60 | 3:14 so "os.path" for example, in this sort of namespace style, 61 | 3:17 and that's really nice to know where the particular symbol 62 | 3:21 that you are working with, like "path", where it came from. 63 | 3:23 If you don't want to use that namespace style, 64 | 3:25 you can use the final import we have here, "from os import path, 65 | 3:29 chmod, chown". 66 | 3:31 PEP 8 also have some guidance on the order and grouping of your imports. 67 | 3:36 It says the standard library imports should go at the top, 68 | 3:39 related third party imports should go in a little section below that 69 | 3:43 and then finally, local app other models within your own code should be put last. 70 | 3:48 Of course, all three of those go at the top of the file. 71 | 3:52 Another thing that's nice about PyCharm - it does this for you automatically 72 | 3:56 if you hit Command+Alt+L for reformat file. -------------------------------------------------------------------------------- /transcripts/03-Foundational-Concepts/7.txt: -------------------------------------------------------------------------------- 1 | 0:01 For this next Pythonic concept, let's go back to the Zen of Python. 2 | 0:04 So here I am in the Python REPL, 3 | 0:07 and one of the core concepts is that flat is better than nested. 4 | 0:11 Now, it turns out this is one of my favorite items here 5 | 0:15 and one of my favorite programming concepts, 6 | 0:17 because a lot of people seem to do it in the reverse. 7 | 0:20 I call this sawtooth style of programming, 8 | 0:23 we have lots of loops with "ifs" and conditionals 9 | 0:25 and then more loops and so on. 10 | 0:28 So let's look at this, how we can apply this in Python. 11 | 0:31 Over in PyChram, we have a program 12 | 0:34 that is meant to simulate downloading a file, 13 | 0:37 and this might not be the best way to do it, obviously it is not the best way, 14 | 0:40 but it really is a simple example to highlight this "flat is better than nested". 15 | 0:45 So what we want to do is we want to download a file 16 | 0:48 and we are going to do a series of tests 17 | 0:50 to make sure that we are able to download the file 18 | 0:51 or at least we think we'll be successful before we actually try. 19 | 0:54 First one uses a little support module here, 20 | 0:56 we are going to ask: "Is the download URL set?" 21 | 0:59 if it is, then we are going to check the network status 22 | 1:02 and then we are going to make sure that the DNS is working 23 | 1:04 then finally we are going to check that we have permission to access the file 24 | 1:06 and if all those things are true, then we are going to try to download it. 25 | 1:11 Otherwise, we are going to say well, this one goes back here 26 | 1:14 so it looks like no access 27 | 1:15 this one here, no DNS, 28 | 1:17 this one here PyCharm even has little like tiny lines 29 | 1:21 that are probably hard to see but I can follow back up, 30 | 1:23 no network and then finally this one is bad URL. 31 | 1:27 This is a serious bad piece of mine, 32 | 1:30 I hate code that looks like this, so let's write a different variation of this 33 | 1:34 which I am going to call download_flat(). 34 | 1:40 So let's just reverse these things, 35 | 1:42 let's look at our conditionals here, 36 | 1:44 instead of having these sort of positive checks, 37 | 1:47 yes you can do this and you can do this and you can do this, 38 | 1:50 we can return these "if" statements into what are called guarding clauses, 39 | 1:54 you don't let the method run if one of them is failed. 40 | 1:57 So I can come over here and say "if not check url", 41 | 2:01 then we'll say oh that's a bad URL. 42 | 2:05 And we can unindent, that's good, 43 | 2:09 of course now that we are up here we want to return early, 44 | 2:13 we'll say "if not check the network" then we want to say "no network". 45 | 2:19 And return, and again, unindent, it's better, again, 46 | 2:24 "if not check DNS", do something and then return 47 | 2:28 we'll print out that there is no DNS 48 | 2:32 and then we'll unindent and finally we'll do this "if not this, return", 49 | 2:41 and then if all the guarding clauses pass 50 | 2:43 then write at the very edge of our method, not indented at all 51 | 2:48 as far as this method is concerned, 52 | 2:50 we can write our meaningful code and this makes it so much easier 53 | 2:53 to maintain and write, instead of trying to do our actual work way down inside here. 54 | 2:59 So this is a very nice way that you can write these guarding clauses 55 | 3:03 instead of this what I call sawtooth programming, 56 | 3:06 so that you have nice, flat, easy to understand, easy to modify things, 57 | 3:10 like for example down here, 58 | 3:12 if I want to insert another test, I've got to make sure 59 | 3:15 I've paired up correctly with the "else" down here and so on, 60 | 3:19 but most importantly, you don't have to work in a hyper-indented way; 61 | 3:23 this also works in loops, you wouldn't return out of the loop 62 | 3:25 but you would just do a "continue" with the guarding clause 63 | 3:28 instead of a test and then put the actual stuff inside the "if" statement. 64 | 3:32 "Flat is better than nested", let's see that in a diagram. 65 | 3:35 All right, here is the code that we wrote 66 | 3:37 that was the sawtooth style with the positive checks, 67 | 3:40 "make sure I can do this, make sure I can do that" and so on, 68 | 3:42 and we rewrote that by inserting or converting those two guarding clauses 69 | 3:47 this flat version is much better, 70 | 3:48 we've converted all the positive checks to guarding clauses 71 | 3:52 and it's much easier to add and remove those guarding clauses, 72 | 3:55 see what the "else" clause is that goes with it 73 | 3:57 because now it's right there and most importantly, at the end, 74 | 4:00 we get to work in a non-indented way. -------------------------------------------------------------------------------- /transcripts/05-Generators/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Let's talk about collections, list comprehension, 2 | 0:03 generators and generator expressions. 3 | 0:06 All of these concepts are extremely central to this idea of Pythonic code, 4 | 0:10 many of them are very unique to Python actually. 5 | 0:13 The first item we are going to look at is iteration. 6 | 0:16 We saw that Python does not have a numerical "for" loop, 7 | 0:19 there is no "for(i=0; ;i++)" style loop, you literally work with sequences, 8 | 0:24 you iterate over collections and so on. 9 | 0:26 There is many built in types that work that way 10 | 0:28 such as lists and dictionaries and so on, 11 | 0:31 but if we had our own type we defined, 12 | 0:33 we might want to be able to iterate over it as well. 13 | 0:35 Here is the ShoppingCart class, 14 | 0:38 and you can add items to it that you are going to buy later, 15 | 0:41 possibly we'd like to create an API such that you can 16 | 0:44 iterate over the shopping cart and get the items back. 17 | 0:46 Let's have a look over in PyCharm and see how that goes. 18 | 0:50 So here is basically the same code and we are defining this thing 19 | 0:53 called the CartItem and it's just really a container for a name and a price, 20 | 0:57 down on line 15 here, we are going to add three items to our cart, 21 | 1:01 a guitar, a cd and an iPhone. 22 | 1:04 What if we wanted to loop over our cart? Maybe it works right now, 23 | 1:08 let's just try, so if we want to write the code "for item in cart:", 24 | 1:12 maybe we'll just print this out, so we'll print, 25 | 1:16 let's do the name and we'll do the price here, 26 | 1:19 we'll do a little format so we'll say "item.name, item.price". 27 | 1:25 And let's do a little header here, so items in your cart. 28 | 1:28 You can see that PyCharm is warning us 29 | 1:30 we are kind of going down a bad path here, so it's like 30 | 1:34 "this is not going to work", but let's go ahead and give it a try, 31 | 1:35 just to see what the error is. 32 | 1:38 Boom, ShoppingCart object is not iterable. 33 | 1:41 OK, so we'd like to write this code but how do we do it? 34 | 1:44 the ability to add iteration to a type is based on the Python data model, 35 | 1:47 which all the dunder methods comprise. 36 | 1:50 So we can come up here and add this particular one, 37 | 1:53 we can say "def __iter__" and form this method 38 | 1:56 we have to return iterator object, which has a length and next. 39 | 2:01 If we just want to loop over the items as they are, 40 | 2:03 we can leverage the underlined collection class itself 41 | 2:06 and it knows how to create one of these 42 | 2:07 so we could just say "self.items.__iter__" 43 | 2:12 go back down here, PyCharm is happy, that's a good sign, let's see if it works. 44 | 2:16 Boom, "items in your cart: guitar, cd, iPhone". Beautiful. 45 | 2:21 What if we wanted to have a little more control than just exposing 46 | 2:24 the underline structure, or underline item here, 47 | 2:27 what if we wanted to say "sort these and then hand them back"? 48 | 2:31 We can come over here and we could say "sorted_items = sorted" 49 | 2:36 and we could pass self.items, and we could pass a key selector, 50 | 2:41 we could say here is a lambda that given an item is going to return item.price, 51 | 2:46 and then we can return sorted_items.__iter__ 52 | 2:55 now you can see we have out items but sorted, 53 | 2:57 not necessarily the same way they were stored before 54 | 3:00 and we could even go and say I'd like the negative price here, 55 | 3:04 so now we have the most expensive ones first. 56 | 3:08 So you might think that this is fairly distasteful here and I don't really like it either, 57 | 3:13 we are going to talk more later about generators, 58 | 3:16 but if you are familiar with the yield keyword, 59 | 3:19 we could write something like this: "for i in sorted items 60 | 3:23 yield i", we could write this code as well, 61 | 3:26 and this would do basically the same thing, 62 | 3:29 it returns the generator rather than list but that's fine. 63 | 3:33 So take your pick, we'll talk more about yield later. 64 | 3:37 OK, we saw that in order to add iteration to our shopping cart, 65 | 3:40 we just need to add a __iter__ method here 66 | 3:44 rather than just exposing the underline self.items 67 | 3:47 we are actually exposing a sorted version of it as a generator. 68 | 3:51 So now we come over here we add some items into it and if you want, 69 | 3:55 we can do a "for...in" loop over our cart point out the items as you saw 70 | 3:58 and we can grab once we have them, the name and the price and print those out. 71 | 4:02 So it's super easy to add custom iterations to your type 72 | 4:05 and building on this Python data model with the dunder methods 73 | 4:08 sometimes called magic methods is a very Pythonic thing to do. -------------------------------------------------------------------------------- /transcripts/01-introduction/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 What areas are we going to cover in this class? 2 | 0:03 Well, we are going to start with the foundations and this concept called PEP 8. 3 | 0:06 So, PEP 8 is a standardized document 4 | 0:09 that talks about the way code should be formatted, 5 | 0:11 and even some of the Pythonic ideas and Pythonic code examples. 6 | 0:16 However, we are going to go way beyond PEP 8 in this course, 7 | 0:19 and so we'll probably spend 15 minutes talking about PEP 8 8 | 0:22 and then we'll move onto other foundational items. 9 | 0:25 Then we are going to focus on dictionaries, 10 | 0:28 dictionaries play a super important role in Python, 11 | 0:30 they are basically the backing store for classes, 12 | 0:34 they are used for data exchange all over the place, 13 | 0:37 and there are a lot of interesting use cases and ways 14 | 0:40 in which dictionaries are used in a language. 15 | 0:43 We are going to talk about a lot of interesting aspects 16 | 0:45 and optimal ways to use and leverage dictionaries. 17 | 0:49 Next up are working with collections, 18 | 0:51 things called list comprehensions and generator expressions. 19 | 0:55 And we'll see that Python has a lot of interesting flexibility 20 | 0:58 around working with sequences, 21 | 1:01 and we'll see the best way to do this here. 22 | 1:04 Next, functions and methods. 23 | 1:06 This will include the use of things like lambda expressions for small inline methods, 24 | 1:11 as well as returning multiple values from methods and that sort of things. 25 | 1:14 There is a lot to look at to write Pythonic functions. 26 | 1:17 One of the great powers of Python 27 | 1:20 is the ability to import or pip install a whole variety of packages, 28 | 1:25 there is even a great xkcd cartoon about importing packages in Python, 29 | 1:31 and we'll see that there are a lot of interesting Pythonic conventions 30 | 1:33 around working with packages and modules. 31 | 1:36 Next up, we are going to look at classes and objects. 32 | 1:40 Object-oriented programming (OOP) in Python is a key cornerstone concept, 33 | 1:44 even though it may play a slightly less important role 34 | 1:47 than languages like Java and C#, 35 | 1:50 still, classes are really important 36 | 1:52 and there is a lot of idiomatic conventions around working with classes, 37 | 1:55 we'll focus on that in this section. 38 | 1:57 Python has a lot of powerful ways of working with loops, 39 | 2:01 one of the first giveaways if somebody is brand new to Python 40 | 2:04 is they are not using loops correctly, 41 | 2:07 so we'll talk about when and how you should use loops 42 | 2:10 and we'll even talk about the controversial else clause 43 | 2:12 for "for...in" and "while" loops. 44 | 2:14 Next, we'll talk about tuples. 45 | 2:16 Tuples are smallish, read-only collections 46 | 2:18 that let you package up related possibly heterogeneous data 47 | 2:23 and pass it around, 48 | 2:25 If we go into a basic database queries and the built in DB API 49 | 2:28 you'll see that the rows come back as tuples. 50 | 2:31 Some of the powerful techniques we'll learn about loops involve tuples 51 | 2:34 and we'll see that tuples in general play a really important role, 52 | 2:37 and there is some powerful and useful conventions 53 | 2:40 around working with tuples in Python. 54 | 2:42 Finally, we are going to look beyond the standard library, 55 | 2:45 with something I am calling Python for Humans; 56 | 2:47 one of the great powers of Python is the ability to go out to PyPi 57 | 2:51 and grab one of the over 80 000 packages, 58 | 2:54 install them using pip or something like this 59 | 2:57 and add amazing powers to your application. 60 | 3:00 People who are new to Python often skip this step 61 | 3:03 and they look at something they have to do and are just like 62 | 3:07 OK I think "I can implement it in these 20 lines of code". 63 | 3:10 It's very likely that there is already a package out there 64 | 3:14 that you can use to do this, 65 | 3:16 so we are going to study two packages one for HTTP 66 | 3:18 and one for database access to really bring home this point of look to PyPi 67 | 3:24 and look to open source first before you start writing your own algorithms. 68 | 3:28 Of course, over time, we may add more topics than what are described here, 69 | 3:32 I am sure as more and more people take this class they will say, 70 | 3:35 "Hey Michael, did you think about having this", 71 | 3:38 or "I also consider this little bit to be idiomatic." 72 | 3:40 Now I don't want to just grab every single detail that I can find, 73 | 3:44 that is possibly Pythonic code and cram it in here, 74 | 3:48 I want to cover the stuff that's most important and not waste your time, 75 | 3:50 but of course, I am sure we'll hear about some new ones 76 | 3:53 that are great and those may be folded in over time. -------------------------------------------------------------------------------- /transcripts/05-Generators/7.txt: -------------------------------------------------------------------------------- 1 | 0:01 Now let's look at determining how many items are in a generator. 2 | 0:04 So if I have something like this, high measurements, 3 | 0:06 and it's I am getting the value, looping over some collection called measurements, 4 | 0:11 and I am doing a test, well, I actually have a really hard time 5 | 0:15 knowing how many are in there, 6 | 0:16 let's go look at that in PyCharm to see why. 7 | 0:19 So this is basically the same code that we used to talk 8 | 0:22 about generator expressions in the first place, this time, 9 | 0:25 this is set actually, fixed that, so here we have our high values, 10 | 0:29 now if I try to print out the "len" of them, get the length, 11 | 0:33 you can see PyCharm is already trying to tell me I am going down a bad path, 12 | 0:36 but let's see what happens anyway. 13 | 0:39 Boom, object or type generator has no length, 14 | 0:42 what might be wrong with getting the length, 15 | 0:44 well it could be infinite, and it might take forever to determine that, 16 | 0:47 but most importantly this is not going to work, all right? 17 | 0:50 So we are not going to do that because it crashes, 18 | 0:53 we could do something like this, we could say "create a list and pass into it 19 | 0:58 the high values in them", if you pass a collection here, 20 | 1:00 it will iterate overall the items and put it in the list and then I could print "len of list". 21 | 1:06 However, one of the beautiful things with generators is 22 | 1:09 if even if there is a million items here, we only hold one in memory ever, 23 | 1:13 well if we do this, we kind of undo that, 24 | 1:17 we now have one million of them in memory but let's just see that it works, 25 | 1:20 ta da, 4, OK, 4, that's cool, 26 | 1:25 so we can use a combination of some really cool things, 27 | 1:29 so if I had something like, let me just do something simple here, 28 | 1:32 I'll say "sum" and say given any set of numbers, I could add them up, 29 | 1:36 so those 3 numbers should have 7, we should see 7 down here, perfect, OK, 30 | 1:42 so we can use sum to give it some kind of collection here, let me separate these, 31 | 1:50 so we can kind of isolate them, count here and we'll say "count equals this", right, 32 | 1:55 so we could add this up using sum, we could leverage this, 33 | 1:58 along with another chained comprehension, 34 | 2:02 so what do I want to put in here, 35 | 2:04 for every item I see in here I would like to add one, 36 | 2:06 because what I want to know is how many items not what is some of the values, 37 | 2:10 so I could sum up the high values here, OK, now this actually broke, 38 | 2:16 now this is worth knowing why did this break, so we have 4 and 0, 39 | 2:20 obviously it should at least be 4 right, these are not 0 numbers that we saw, 40 | 2:24 but here is the thing, unlike lists, once you run through a generator, 41 | 2:28 it's done, you have to recreate it, so basically this, 42 | 2:32 the fact that I did this used up the generator in a sense, 43 | 2:36 you can only use them once, if you want to do it again you recreate it, 44 | 2:38 if that's not going to work for you, create a list comprehension instead. 45 | 2:42 There you go, so 375, there is not 375 items in there, 46 | 2:46 there is 4 so what I want to do is somehow in the best, 47 | 2:50 most efficient possible way go through this list and say 48 | 2:53 "for every time I find an item, give me one", 49 | 2:56 so what I can do is I can create another generator, 50 | 2:58 I can say "give me the number 1 for n in high values". 51 | 3:03 OK, so what we are going to do is we are going to through and say 52 | 3:05 "every time you find a value, I don't care, I am not going to use this value", let's say 1, 53 | 3:10 and that's going to add it up, now remember, 54 | 3:12 it's very Pythonic to say "if I am not going to use a value, 55 | 3:14 use this indicator say underscore", 56 | 3:17 like here is a variable that has to come out and be stored somewhere 57 | 3:21 but by saying underscore I have no any intention of using it, 58 | 3:24 so we should get the answer 4. 59 | 3:26 Beautiful, so here we have our generator, you saw generators don't have a length, 60 | 3:31 we can't use that, if we try to throw them into a list or something along those lines, 61 | 3:35 that's not good because that undoes all the benefits of the generator, 62 | 3:38 it loads everything in a memory all at once, 63 | 3:40 so we can use this cool combination of the sum method 64 | 3:44 and a chained generator expression, 65 | 3:47 so the chained generator expression is "give me one 66 | 3:50 for every time you find an item in high measurements", 67 | 3:52 which is itself a generator, and then if we sum up that set, 68 | 3:55 we actually get the number of items in the generator. 69 | 3:58 Remember, just be really careful it's used up after this, 70 | 4:02 so if you want to run it again, you need to either recreate it 71 | 4:06 by having the 5 lines above again, 72 | 4:08 or turn into a list where you can reuse it over and over. -------------------------------------------------------------------------------- /transcripts/11-for-humans/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 The first of the two packages that we are going to look at is the Requests package. 2 | 0:05 It turns out the Requests is the most popular package for Python, 3 | 0:10 let's look at its website really quickly. 4 | 0:12 So over here you can see HTTP for Humans, very nice, 5 | 0:15 "the only Non-GMO HTTP library for Python, 6 | 0:19 safe for human consumption", basically the idea of Requests is the urllib, urllib2 7 | 0:24 that are in Python, are cumbersome and hard to work with and overly complicated. 8 | 0:28 Here is a re-imagine of it by a guy named Kenneth Reitz 9 | 0:31 who does amazing work to make it much more delightful and easy to work with. 10 | 0:36 You'll see this package gets a little bit of traffic, so if I search for downloads, 11 | 0:39 Requests is one of the most downloaded Python packages 12 | 0:42 of all time pulling in over 7 million downloads every month. 13 | 0:46 Think about that, 7 million downloads a month. 14 | 0:49 And it's been around for really long time. 15 | 0:52 There is actually some talk about making Requests the new urllib more or less 16 | 0:57 like bringing this into the standard library to keep Requests more agile, 17 | 1:01 they decided to keep it out, nonetheless, 18 | 1:03 Requests is even recommended over the built in URL libraries: urllib, urllib2 and so on. 19 | 1:08 So let's do something awesome with Requests, 20 | 1:10 so over here we have a URL to the omdbapi, that's the Open Movie Database API, 21 | 1:16 and we can do search here and get the data back as JSON 22 | 1:20 and do a search for some text so we are going to ask the user 23 | 1:23 to enter some kind of text like "enter the name of a movie" or whatever 24 | 1:27 and it will go pull that down. 25 | 1:28 We did play with this in another example previously but we didn't focus on Requests. 26 | 1:32 So in order to do this, the first thing we have to do is get started. 27 | 1:36 We've already installed Requests, and of course, 28 | 1:39 we talked about pip and packaging and pip installing various packages 29 | 1:45 but also from PyCharm we can come over here and look at our current environment 30 | 1:48 and see that we already have Requests installed but in fact it's a little bit out of date 31 | 1:53 we won't mess with it, but we could upgrade it. 32 | 1:55 If we didn't have this installed we could hit "+" and say "hm, 33 | 1:58 I am looking for Requests", it turns out there is a lot of stuff that's built upon Requests here, 34 | 2:05 did you know there is requests-middleware, requests-guards, requests-ftp, requests-cloudkit? 35 | 2:10 All of these things, but here is the one that we want 36 | 2:12 and we can just hit "boom, install" but great, it's already installed. 37 | 2:16 So let's just use it, remember "import antigravity"? Well, here is "import requests". 38 | 2:21 So the way it works is we get a response back and we give it some URL here, 39 | 2:26 and URL we are going to construct from the user text, 40 | 2:28 we probably should check "if response.status_code" is not equal to 100, print, 41 | 2:34 "wow, that is code such and such". 42 | 2:38 All right, so hopefully we don't end up in that case, but you never know. 43 | 2:41 Now let's go down here and actually get the data, 44 | 2:44 so we made this request and everything was OK, 45 | 2:46 we'll say that the data is going to be response.json, 46 | 2:51 so that's going to actually finish downloading all the text and the response, 47 | 2:54 convert it from json into a dictionary so this is going to be like a movie lookup. 48 | 2:59 Now, the format of this json is a little funky, if we actually want to search data, 49 | 3:03 we have to go and ask for the search text here 50 | 3:06 and then we can say "for m in search" and we could print out just the title of the movie 51 | 3:11 we are looking for, and that comes in as title, something like that, and great, that's it. 52 | 3:17 Access is API, download it, possibly even authenticate against it, 53 | 3:20 all we'd have to do is put the user name and password in here and we are done. 54 | 3:24 So this is just part of the power being leverage all these amazing packages on PyPi, 55 | 3:28 let's see if it works, that would be a good thing. 56 | 3:31 So I want to search for let's say "Night Rider" can we find it? 57 | 3:34 I don't see Night Rider but there sure is a bunch of stuff about nights, 58 | 3:38 let's try one more, see if Silicon Valley is there, Silicon Valley fantastic, 59 | 3:44 the Spirit of Silicon Valley, the Hermits of Silicon Valley, all those things, 60 | 3:48 and we didn't get just the title back, we got lots of data, like that, 61 | 3:53 we just happened to only be printing out the title. 62 | 3:56 So here is one piece, one example of looking just beyond the standard library 63 | 4:00 out in the broader ecosystem 64 | 4:02 where you'll find amazingly powerful packages to help you, 65 | 4:05 here it is in a graphic, we'll say "import request", generate the URL, 66 | 4:09 "requests.get" given the URL, check the status code, otherwise, 67 | 4:14 we'll just call .json, pull out the data boom, couldn't be easier. -------------------------------------------------------------------------------- /transcripts/10-tuples/4.txt: -------------------------------------------------------------------------------- 1 | 0:01 We've seen how central tuples are to many parts of Python, 2 | 0:03 let's look at a better version of tuples. 3 | 0:07 So over here I have a very simple program; we have two methods, 4 | 0:11 "main", which is going to print out some data 5 | 0:13 and it's going to get that data from this get_data_tricky version, 6 | 0:16 you probably wouldn't call your function tricky but you know, 7 | 0:20 just to highlight it for the course, right? 8 | 0:21 So you can see it returns a list of tuples, here we have some incrementing numbers, 9 | 0:27 so maybe this is an id, we have 3 values here in the middle, 10 | 0:33 so those must represent something important, we'll see, not totally sure what that is. 11 | 0:38 So we'll come over here, here I've got a little template 12 | 0:42 for printing out some stuff 13 | 0:43 and notice I've got some values just so it can run, 14 | 0:46 so here we've got 3 things back and I just put "ones" everywhere. 15 | 0:49 Suppose I want to print out the id, the rating and the position. 16 | 0:53 Now remember, this is not the function I am getting it from, 17 | 0:55 so maybe you don't have it handy easy to look at, 18 | 0:59 so we are going to come over here and say well, 19 | 1:00 I think that I remember the first one being this 20 | 1:04 and let's see the rating maybe that was next, 21 | 1:08 and the last two got maybe those represent the position 22 | 1:11 and we know that doesn't look right because these are like floating point 23 | 1:14 and that's like and integer, so maybe actually this is a 3, what was that, that was 1, 24 | 1:19 yeah, yeah, OK, so that's right, so this is what we wanted, 0, 3, 2, 1, 25 | 1:25 what if I wanted to add an item, a thing, another element to this tuple? 26 | 1:30 How easy this is going to be to maintain, to review, 27 | 1:34 to bring on new people, and so on, 28 | 1:37 this is not a good way to work, so let's talk about a better kind of tuple. 29 | 1:41 so let's import collections, and let's define something called a Rating. 30 | 1:47 Now this is like defining a class or something like that, like a custom type, 31 | 1:52 but we can do it in a very concise short way, using "collections.namedtuple"s. 32 | 1:58 So what you put here is the name, the type name, 33 | 2:01 and then you can put the fields separated by commas, 34 | 2:04 so maybe we want to call this id, rating, "x" and "y", something like that. 35 | 2:09 So now, give us some space, so PEP 8 is not mad about our spacing, 36 | 2:13 we can write a better version here, 37 | 2:19 instead of doing all this, let's have a rating, 38 | 2:23 just like the way you initialize a class, or something like that, 39 | 2:26 here we are going to initialize our rating and it takes an id 40 | 2:30 and a rating and then "x" and then "y". 41 | 2:32 Actually, it looks like I have that wrong, so let's put it like this, 42 | 2:35 or I would have to reorder my data but let's say it goes like this. 43 | 2:38 Now, let's comment out this tricky version, here we go, 44 | 2:41 the data from the better and we'll hide this, 45 | 2:43 it didn't even matter what order it comes in, 46 | 2:45 we don't care, we don't have to look at it, 47 | 2:47 so we are going to say now we are going to work with this better version of data, 48 | 2:50 I want to put the id first, so let's see what is that, "d." all right, "id". 49 | 2:55 Cool, and we are going to have "d.rating" I think was next, "d.x", "d.y", 50 | 3:02 see how much nicer that is? 51 | 3:04 And literally, that is all it takes, name the type, 52 | 3:07 state the basically the names of the positions, 53 | 3:11 and then when you allocate it, instead of just saying regular tuple like this, 54 | 3:14 you just allocate it as an instance of a class. 55 | 3:18 So let's run it, make sure I didn't pull any sneaky tricks, that it still works, 56 | 3:22 boom, id 1, 2, 3, rating looks right, position x y, great. 57 | 3:27 And, these named tuples, they have all the properties that you would expect, 58 | 3:33 so for example, I can come down here I could say 59 | 3:36 let's say we want the x y value so I could have, so here, like this, 60 | 3:42 I could say I want to unpack this tuple and I want to print x and y 61 | 3:47 so you should just see just the numbers next to each other alongside the other values. 62 | 3:52 There is the x y values we are unpacking. 63 | 3:54 So these are regular tuples, they do everything regular tuples do 64 | 3:57 but they are upgraded and have names, wonderful. 65 | 4:01 So to create one, we just import the collections module, 66 | 4:04 we create a collections.namedtuple, we give it a name, 67 | 4:07 we catch the instance of the class, generate it from name tuple here, 68 | 4:13 and when I use that every time when I allocate one of them. 69 | 4:16 We name the positions, this case we have temperature, latitude, longitude and quality, 70 | 4:21 then you can see we create one and we can access it 71 | 4:23 in the traditional style in bracket 0 but much better "m.temp", "m.quality". 72 | 4:28 We print it out, we even get a nicer looking string friendly version 73 | 4:32 rather than just the basic tuple without the names or understanding 74 | 4:37 of what the positions mean, so named tuples, very Pythonic, 75 | 4:41 definitely make use of them. -------------------------------------------------------------------------------- /transcripts/01-introduction/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Hello and welcome to the course Write Pythonic Code Like a Seasoned Developer. 2 | 0:04 My name is Michael Kennedy and we are going to be on this journey together 3 | 0:07 to help you write more readable, more efficient and more natural Python code. 4 | 0:13 So what is Pythonic code anyway? 5 | 0:16 When developers are new to Python, they often hear this phrase Pythonic 6 | 0:20 and they ask what exactly does that mean? 7 | 0:23 In any language, there is a way of doing things naturally 8 | 0:26 and a way that kind of fight the conventions of the language. 9 | 0:30 When you work naturally with the language features and the runtime features, 10 | 0:33 this is called idiomatic code, 11 | 0:35 and in Python when you write idiomatic Python we call this Pythonic code. 12 | 0:40 One of the challenges in Python is it's super easy to get started 13 | 0:43 and kind of learn the basics and start writing programs 14 | 0:46 before you really master the language. 15 | 0:48 And what that often means is people come in from other languages 16 | 0:51 like C++ or Java or something like that, 17 | 0:53 they will take algorithms or code that they have and bring it over to Python 18 | 0:57 and they will just tweak the syntax until it executes in Python, 19 | 1:00 but this often uses the language features of - let's just focus on Java. 20 | 1:04 In Python there is often a more concise, more natural way of doing things, 21 | 1:09 and when you look at code that came over from Java, 22 | 1:11 we'll just take a really simple example- 23 | 1:13 if you have a class and the class has a getValue() and setValue(), 24 | 1:17 because in Java that is typically the way you do encapsulation, 25 | 1:20 and people bring those classes in this code over and migrate it to Python, 26 | 1:24 they might still have this weird getter/setter type of code. 27 | 1:28 And that would look completely bizzare in Python 28 | 1:31 because we have properties for example. 29 | 1:33 So we are going to look at this idea of Pythonic code, 30 | 1:36 now, it's pretty easy to understand but it turns out to be fairly hard to make concrete, 31 | 1:40 you'll see a lot of blog posts and things of people trying to put structure 32 | 1:44 or examples behind this concept of Pythonic code. 33 | 1:48 We are going to go through over 50 examples of things I consider Pythonic 34 | 1:53 and by the end you'll have many examples, 35 | 1:56 patterns and so on to help you have a solid grip on what Pythonic code is. 36 | 2:02 So what is Pythonic code and why does it matter? 37 | 2:05 Well, when you write Pythonic code, 38 | 2:07 you are leveraging the experience of 25 years of many thousands, 39 | 2:10 maybe millions of developers, 40 | 2:12 these guys and girls have worked in this language day in and day out 41 | 2:16 for the last 25 years and they really perfected the way 42 | 2:19 of working with classes, functions, loops, and so on, 43 | 2:23 and when you are new especially, 44 | 2:25 it's very helpful to just study what those folks have done and mimic that. 45 | 2:29 When you write Pythonic code, you are writing code 46 | 2:32 that is specifically tuned to the CPython runtime. 47 | 2:35 The CPython interpreter and the Python language have evolved together, 48 | 2:40 they have grown up together, 49 | 2:42 so the idioms of the Python language are of course matched 50 | 2:44 or paired well with the underlying runtime, 51 | 2:47 so writing Pythonic code is an easy way 52 | 2:50 to write code that the interpreter expects to run. 53 | 2:54 When you write Pythonic code, 54 | 2:56 you are writing code that is easily read and understood by Python developers. 55 | 2:59 A Python developer can look at standard idiomatic Python 56 | 3:03 and just glance at sections and go, 57 | 3:05 "oh, I see what they are doing here, I see what they are doing there, bam, bam, bam" 58 | 3:09 and quickly understand it. 59 | 3:10 If instead it's some algorithm that is taken from another language with other idioms, 60 | 3:15 the experienced developer has to read through and try to understand 61 | 3:20 what is happening at a much lower level, 62 | 3:23 and so your code is more readable to experienced developers 63 | 3:27 and even if you are new will become probably more readable to you 64 | 3:30 if you write Pythonic code. 65 | 3:32 One of the super powers of Python is that it is a very readable 66 | 3:36 and simple language without giving up the expressiveness of the language. 67 | 3:40 People coming from other languages that are less simple, 68 | 3:43 less clean and easy to work with 69 | 3:46 will bring those programming practices or those idioms over 70 | 3:49 and they will write code that is not as simple as it could be in Python 71 | 3:53 even though maybe it was a simple as it could be in C. 72 | 3:56 So when you write Pythonic code, 73 | 3:57 you are often writing code that is simpler and cleaner 74 | 4:00 than otherwise would be the case. 75 | 4:02 If you are working on an open source project, 76 | 4:04 it will be easier for other contributors to join in because like I said, 77 | 4:08 it's easier for them to read and understand the code at a glance, 78 | 4:11 and they will more naturally know what you would expect them to write. 79 | 4:14 If you are working on a software team, 80 | 4:16 it's easier to onboard new Python developers into your team 81 | 4:20 if you are writing idiomatic code, 82 | 4:22 because if they already know Python 83 | 4:23 it's much easier for them to grok 84 | 4:26 your large project. -------------------------------------------------------------------------------- /transcripts/08-classes/3.txt: -------------------------------------------------------------------------------- 1 | 0:01 One of the primary reasons people will write non-Pythonic code 2 | 0:03 is they come from other languages that have other idioms 3 | 0:05 and they just move their code over and make them work 4 | 0:08 using the same former idioms and not really adopt the new Python ones. 5 | 0:11 So here we have some NotSoPythonicPet that we've been playing with, 6 | 0:14 it's got some private fields - age and name, 7 | 0:16 we'd like a way for us to get the name and get the age but not set it, 8 | 0:20 here we wrote a get_name and get_age, so that you can get those. 9 | 0:24 But this is not Pythonic at all, and when you use the code, it's not pretty, 10 | 0:28 it looks something like this, so here we create a NotSoPythonicPet, 11 | 0:31 it's going to be a cow called Betsy who is 4 12 | 0:33 and we can say she is named such and such 13 | 0:36 and is however many years old, so "cow.get_name", "cow.get_age". 14 | 0:41 It doesn't have to be this way, let's see how it should be. 15 | 0:45 All right, so here is the Betsy code again, not so Pythonic, 16 | 0:48 we are doing this and this, let's go down to this PetSnake type 17 | 0:51 that we are working with and do something different, do it better. 18 | 0:54 You should almost never write these getters and setters in Python, 19 | 0:57 instead, the much more natural way to work with this 20 | 1:00 would be to say "py.name", "py.age" 21 | 1:04 as if they were a field, now this can be just accessing underlined variables, 22 | 1:08 these could be computed like in a shopping cart you could say "cart.total" 23 | 1:12 and maybe that just actually does a loop and adds up all of the items, 24 | 1:15 but as a consumer, you don't want to think of these as functions, 25 | 1:19 you want to think of them as just attributes of the class, right? 26 | 1:23 So in Python, instead of writing those getters and setters, 27 | 1:26 we can say come over here and say I'd like to have a function called "name" 28 | 1:30 and this is going to return "self.__name", now if I try to run this code, 29 | 1:36 and let's do the same for age, 30 | 1:41 and if I come down here and I write this code, 31 | 1:42 we are going to get something entirely unexpected, 32 | 1:45 what do you get if you say the name of a function? Without parentheses, 33 | 1:48 you get the bound method, bound to this object. 34 | 1:53 That's not what we wanted, so then we have to say this, and that's not so pretty, 35 | 1:58 technically it works, but we are kind of back to the previous example 36 | 2:02 that was names for our getters, 37 | 2:05 so in Python we can use a decorator called a property decorator, from the built-ins, 38 | 2:09 down here I can say actually this is not a regular method but a property 39 | 2:13 and now if I say "py", well if I say it far enough down, "py. name and age" 40 | 2:18 you can see the little "p" by there, and if I access it like this, 41 | 2:22 this actually just calls the function, beautiful, right, don't write getters, write this. 42 | 2:26 Suppose I want to be able to change the age but not the name, 43 | 2:29 if we try to set the age, right now it's read-only, obviously, 44 | 2:33 and it says can't set the attribute we've already got this read-only property called age 45 | 2:38 but if we wanted to set the age, we can come down here and write another function 46 | 2:41 called "age", that takes the value and we'll say "self.__age = value". 47 | 2:48 Now we give it another attribute up here, we say "age.setter", 48 | 2:52 now this is less delightful than just add property but that's how it works. 49 | 2:57 So now we should be able to set the age and run it, first our snake is 6, then 7, 50 | 3:02 so just to make it clear, these are actual function calls, 51 | 3:04 not just changing the underline property, I'll do a print, there, 52 | 3:09 so all right, two little prints that every time you execute this code, 53 | 3:12 which could do anything we wanted to do, we just happen to be 54 | 3:15 setting the underlined private field, 55 | 3:17 it will print this and then when we get it will print that. 56 | 3:19 So here you can see, "Here is my pet snake", all we are getting is age, 57 | 3:22 its name and age and such and such, setting the age, getting the age and so on. 58 | 3:28 And finally like I said, you can have computed properties, 59 | 3:32 aren't really backed by underline store, so here I could say let's have "is_protected", 60 | 3:39 we could do return True or False depending on how high the protection level is 61 | 3:42 so we'll say, let's say "self.protected level value is greater than 5", 62 | 3:46 so if it's greater than 5, the snake is protected. if it's not then it's not, whatever that means. 63 | 3:51 so we'll just at the very end I'll print "py.is_protected" 64 | 3:55 you can see this property read-only property, run that, no, the snake is not protected. 65 | 4:01 All right, so this is not based on just returning something, 66 | 4:03 we can compute whatever we want to. 67 | 4:05 Properties are really useful, very Pythonic 68 | 4:07 and I recommend that you make good use of them. 69 | 4:10 So let's see in a graphic how we evolve this, right, getter/setter - bad idea. 70 | 4:14 In our Pythonic pet, our PetSnake, we've got our age and name 71 | 4:19 and here we are writing a property that says get me the read only version of the name 72 | 4:23 and the read only version of the age. 73 | 4:25 We also saw we can make writers or setters for the properties, 74 | 4:29 as well as computed properties that are not just returning underlined fields. 75 | 4:33 So with this in place, we can write as a consumer of the class, 76 | 4:36 much more natural code. 77 | 4:38 If we create a pet, it's called "py" 78 | 4:40 and its name is "py.name" and its age is "py.age". -------------------------------------------------------------------------------- /transcripts/07-modules-packages/3.txt: -------------------------------------------------------------------------------- 1 | 0:01 One entirely non-obvious convention 2 | 0:03 that you quickly learn when you get into Python, is this __main__ concept, 3 | 0:07 for controlling when you should execute your code as a program 4 | 0:11 and when your code should behave as a library or a module. 5 | 0:14 Let's look at this simple example. 6 | 0:16 So here we have two modules. 7 | 0:21 We have the one that is suppose to run as a program, over here, 8 | 0:25 and we have another one which defines a method, 9 | 0:27 a variable and a class that we are going to use, 10 | 0:29 and you can see we are importing this or redefining it to something reasonable 11 | 0:33 and we are saying things like "the variable we got out of s", 12 | 0:37 so s.method/class/variable, there they are, 13 | 0:40 so the variable we've got out of here is whatever, when we run, 14 | 0:45 let's comment this out for just a second, if I go and run this, 15 | 0:51 you can see it printed out this code it said the variable value is such and such, great, 16 | 0:56 the variable value is a variable. 17 | 0:58 So it looks like we imported this successfully, we worked with some of its data, 18 | 1:01 we could use methods and classes and you know, everything. 19 | 1:04 But what is this deal down here, well, 20 | 1:07 this is something you are going to see often in Python 21 | 1:11 and you don't have to call this method "main", you can call it "run", call it whatever, 22 | 1:14 but basically we are saying: "Define this function up here 23 | 1:17 and in the case where __name__ is main, run this." 24 | 1:22 We'll just print out __name__ really quick, so we'll print this. 25 | 1:27 We run it, OK it is __main__, let's go over here, this one, 26 | 1:32 and we'll say print to the same thing, print __name__. 27 | 1:35 So the name of this is this great long support module thing, 28 | 1:39 so we are going to run it, well, 29 | 1:41 first of all, when I import this, it should trigger this to go. 30 | 1:45 So the name is, this full name here "chapter 6 packages, blah blah blah", 31 | 1:49 not a nice name, 32 | 1:50 it wasn't really built to be reused, it was built to be descriptive 33 | 1:53 about what part of the class it fits in, but watch this, 34 | 1:56 if I go and run this, let's make this a little more obvious, 35 | 1:58 let's say- 36 | 2:01 so here we have the support library name 37 | 2:03 and let's put this as main app name and we'll run this first, 38 | 2:09 so here the support library name is what you expect, main app name is __main__, 39 | 2:13 that's not what it says up here but we'll talk about why that is, 40 | 2:16 but if I run the support library, its name is now main. 41 | 2:20 So here is the convention: If the thing that is being executed directly, 42 | 2:24 the module or the script that has been executed directly 43 | 2:27 regardless of what its real name is, it's called __main__. 44 | 2:30 Everything that is imported up here like so, 45 | 2:33 when it's not the target but it's just being imported, it's given its module name, 46 | 2:37 basically the name without the py extension. 47 | 2:39 So, what we do in Python is we use this to only conditionally execute code, 48 | 2:44 suppose over here and this support library we had something like this, 49 | 2:48 we said, we wanted to ask a question, 50 | 2:50 "while True", we'll say "age = input("How old are you? ")", like so, 51 | 2:56 and then we'll convert that to an int, and let's go over here and say age=0, 52 | 3:01 let's say "while age is equal or less than 0", OK? What happens here if I go and run this? 53 | 3:07 I would like to see I'm going to import this and just use this variable, so let's try, 54 | 3:12 let's try to run this one. 55 | 3:14 OK, so it tells us what the library- wait a minute, 56 | 3:18 why are we getting "How old are you?", let's say 0,0,7, oh, 57 | 3:25 so that ran and then our code ran, let's make this a little more obvious, 58 | 3:30 let's go up here and say print "About to import support lib", I'll say "done importing", 59 | 3:38 so we try and you can see about import support lib 60 | 3:41 and then we are just like stuck here, because when you import a module, 61 | 3:44 you are literally just executing it top to bottom, executing this defines a class, 62 | 3:48 executing that defines a method, executing that defines a variable, but this, 63 | 3:52 this is not what we are going to run, 64 | 3:54 we are just trying to expose our variables and methods and so on, 65 | 3:57 this might be if this was actually the script, so you can see this is a problem. 66 | 4:02 7 now we are done importing, now this runs, 67 | 4:04 so we could come down here and we could say, we could use our convention, 68 | 4:08 we could say "if __name__ == '__main__'", only in that case, 69 | 4:13 only when this is running as a program do we do this, when we do that, 70 | 4:17 this won't run, if we import it. 71 | 4:20 So let's try again, about to import, printed out the name here, 72 | 4:24 this is not __main__, done importing library, we've got our variable. 73 | 4:27 But if we go over here and run this one, support library name is now __main__, 74 | 4:32 "How old are you?" I'm 7, awesome. 75 | 4:34 So here is what that convention is all about, 76 | 4:37 the "__name__ == '__main__'" 77 | 4:42 is all about disambiguating the case where your script is being used 78 | 4:45 as a library, as a module in somebody else's code, 79 | 4:48 or it's being executed as the target. 80 | 4:50 This is so common that PyCharm has a shortcut for you, 81 | 4:53 you just type main tab and it expands it up. 82 | 4:56 we saw that when we import a module 83 | 4:59 its name is just the name of whatever the file is, 84 | 5:02 so in this case it's going to be support for the top class 85 | 5:05 when we import it in the bottom. 86 | 5:07 However, if it were executed, it would have a different name, 87 | 5:10 it would be "__name__ == '__main__'" and that allows you to disambiguate the times 88 | 5:15 when your script is being run as a program 89 | 5:17 and when it's being reused for its functionality. -------------------------------------------------------------------------------- /transcripts/07-modules-packages/2.txt: -------------------------------------------------------------------------------- 1 | 0:01 Let's talk about import statements. 2 | 0:02 Over here we have a variety of things being used 3 | 0:05 that should come out of modules that are mostly in the standard library 4 | 0:09 but some of them we build ourselves. 5 | 0:11 So you can see we're using the system module, 6 | 0:13 we say "sys.versioninfo.major" so this should print out "3" 7 | 0:17 the way we have our system setup right now, 8 | 0:19 of course you can see there is an error here 9 | 0:21 and we should come over here and say "import sys". 10 | 0:24 So this is one way we can do this, 11 | 0:27 and this is certainly considered Pythonic, it's using namespaces, right, 12 | 0:32 so "sys.", we know where this version info is coming from, 13 | 0:36 and remember, namespaces, those are one honking great idea, 14 | 0:41 let's do more of those. 15 | 0:42 OK, cool, so we are doing more of those here, now sometimes, 16 | 0:46 you don't want to have to say the namespace or the module name here, 17 | 0:49 and especially this really long like "SQLAlchemy.orm.ext.declaration.", 18 | 0:56 this is maybe a little bit long, but sys, sys is great. 19 | 1:00 However, if you would like to use a type over here we can say "import os" 20 | 1:06 and we could come over here and we could say "os.path", 21 | 1:08 but we'd rather just say path, so instead we'll say "from os import path". 22 | 1:14 We can do it like this and this is also considered very Pythonic, 23 | 1:18 there is a shortcut way, we could get hold of, here we are using the stats module, 24 | 1:24 statistics technically, we'll rename it, so we could get hold of median, 25 | 1:28 the mode, the mean and all those things, 26 | 1:30 if we said "from statistics import", rather than naming them, 27 | 1:36 the correct way would be to say median mean and so on, 28 | 1:40 name them, but we could say, let’s leave this here and I'll comment it out, 29 | 1:45 we could just say you know, I'll just use everything, that's cool thanks. 30 | 1:48 Well, you can see all these 3 lit up 31 | 1:51  this is considered to be not Pythonic at all, let me show you why. 32 | 1:55 So we have this, suppose we want to use median and mean from the statistics module, 33 | 1:59 and let me go ahead and import this as well, 34 | 2:01 so we'll say "import statistics as stats", 35 | 2:06 so here if we want and if we had something really long, 36 | 2:08 like I said "SQLAlchemy.orm.declarative. da da da", we could rename it, 37 | 2:12 so here we could say "import statistics as stats", 38 | 2:15 and then just say "stats.", so kind of taking over the namespace, to greatly simplify it. 39 | 2:20 Suppose mode here is not the statistics mode, 40 | 2:23 but we want to set the mode of operation of our program, 41 | 2:26 so we have kind of a crazily named file here, beware_the_trade_imbalance support file 42 | 2:34 and in there, there is a mode function, you can see "def mode", it prints, 43 | 2:39 that really didn't do anything, 44 | 2:40 but under one condition it prints "Mode set to DEFAULT" or "Mode set to ADVANCED". 45 | 2:44 So when we say "mode" down here, this is actually the mode we want to use, 46 | 2:47 so I can come over here and say OK, cool, 47 | 2:49 so I'll say "from..." we want to import mode. 48 | 2:55 Now, PyCharm just because of this weird naming convention I had here, 49 | 2:59 thinks this is a private thing, 50 | 3:02 so let me just tell it: "Don't make this look like an error for us." 51 | 3:07 Because normally that would maybe mean "protected" but it doesn't in this case. 52 | 3:11 So, now all of our code should run, we print out the major version, the current path, 53 | 3:17 the median of those numbers, again, that should be the same median, then the mean, 54 | 3:22 so the median, median, mean, and just like we would expect 55 | 3:26 we have our mode set to advanced. 56 | 3:29 So our mode is set to advanced. 57 | 3:31 The None is coming from, let me actually, just this doesn't return the value 58 | 3:34 in that case we wrote. 59 | 3:35 OK, so that's the way it worked, 60 | 3:37 but suppose instead of doing this Pythonic style of importing without the namespace, 61 | 3:41 I said you know, I just don't want to worry about it, let me, 62 | 3:44 I want to have a bunch, let's just get them all, so we can go like this. 63 | 3:48 However, even though I was not using the mode from statistics, 64 | 3:55 this imports mode from statistics, flat out, and all the other ones, 65 | 3:59 I am not sure how many are in there but many of them. 66 | 4:01 Now notice, PyCharm knows something is amiss here. 67 | 4:05 This has gone gray and says, "you are not really using this mode", 68 | 4:08 that should be a warning that something is wrong, 69 | 4:11 so if I remember this said "mode set to ADVANCED", now If I run it, 70 | 4:15 mode is seven, what happened? 71 | 4:18 We imported mode and then we reimported mode, 72 | 4:21 which replaced its definition in this module. 73 | 4:24 So mode is gone. All right, we never intended to use mode from statistics, 74 | 4:28 we just wanted to be lazy and not import those two separately, 75 | 4:32 this is bad, don't do this. 76 | 4:35 Notice when I comment this out, this gets enabled again 77 | 4:38 and let's do one more thing, notice how this got some errors 78 | 4:42 and these are undefined, and I did type it up here but let me remove it for a minute 79 | 4:46 and just show you the PyCharm can fix this, 80 | 4:48 so if I go over here I can hit Alt+Enter and it will say, 81 | 4:50 now because I have the stats up here already it knows, 82 | 4:53 it's trying to help too much, so if I come over here and I hit Alt+Enter, 83 | 4:56 it'll say "import statistics.median" and I hit this, great, 84 | 5:01 and again Alt+Enter, you can see, 85 | 5:03 it's doing the Pythonic way of doing the import here for us, perfect. 86 | 5:07 And of course, let's put our stats back because we were confusing PyCharm there. 87 | 5:11 Let's see this in a graphic. 88 | 5:14 So namespaces are great, "import sys", that was cool, 89 | 5:17 importing bare items by name, that's fine, "from os import path", 90 | 5:23 we could rename or shorten our namespaces, "import statistics as stats", 91 | 5:28 maybe it doesn't make sense in this case, 92 | 5:30 but I will give you some examples where it does, 93 | 5:32 and we also saw that we have to be very careful 94 | 5:35 with wild card imports, "import *", because the two lines above, 95 | 5:39 we are trying to get mode from our custom package 96 | 5:42 and mean and median from stats 97 | 5:44 and if we wanted to get lazy and say "forget mean, median, 98 | 5:47 and we should write *", we would blast away our custom packages mode definition. -------------------------------------------------------------------------------- /transcripts/08-classes/1.txt: -------------------------------------------------------------------------------- 1 | 0:01 Let's talk about the Python idioms on classes and objects. 2 | 0:04 And let's start in the construction of classes, how do you build them up, 3 | 0:07 how do you add fields and to a lesser degree when we get farther along, methods. 4 | 0:13 So here I have a class defined called NotSoPythonicPet. 5 | 0:16 And we are going to do several non-Pythonic things to it 6 | 0:19 but let's start up by giving this pet a way to set a name and to set an age 7 | 0:23 so that we can ask it, hey pet, what is your name, what is your age. 8 | 0:26 So we can come over here and this is very non-Pythonic 9 | 0:30 so don't do this, we can come over here and we can say 10 | 0:31 "set_name" and give it a name here, and say "self.name = name". 11 | 0:38 OK, that's great and we could do the same thing for age 12 | 0:46 so now we can set the name and we can set the age. 13 | 0:49 Now, there is nothing technically wrong with this code, 14 | 0:52 we shouldn't do this but technically it's not wrong, 15 | 0:54 these are just warnings saying "don't do" what we are doing here because it's not Pythonic, OK, 16 | 0:58 so but we can come over here and say "cow.set_name()" 17 | 1:02 and let's call it Betsy, and we could set its age and how old is this cow, 7. 18 | 1:10 OK, so let's run this, make sure everything hangs together, we have a pet. 19 | 1:14 Now this little string over right here, 20 | 1:17 this is not the best description of our not-so-Pythonic cow, 21 | 1:21 because we could say "hey, this is a pet, it's name is Betsy, it's age is 7", 22 | 1:26 so why don't we do that. 23 | 1:28 So a pet whose name is {} and age is {}. 24 | 1:36 So now we have pet whose name is whatever the name is 25 | 1:40 and the age is whatever the age is, so let's try this. 26 | 1:44 Great we have a pet whose name is Betsy and age is 7. 27 | 1:47 Now, technically, you can set fields on these types anywhere you want, within them, 28 | 1:53 out here, I can also say "cow.happiness = 11", it's a very happy cow, 29 | 2:00 probably one from those California milk commercials, 30 | 2:03 and we could even incorporate the happiness up here 31 | 2:07 and run it, and great, it's happy. 32 | 2:10 What's wrong with this? Obviously, we should not be doing this. 33 | 2:13 Why? Because, what if we forget to set the name for example, 34 | 2:18 bam. Sorry, this pet has no attribute called name, that's unfortunate, isn't it. 35 | 2:26 And even if we called all the methods, 36 | 2:27 how are you suppose to know that you've got to do this, right? 37 | 2:30 This is really a terrible way to create classes, let's go over here and turn, 38 | 2:36 we'll get rid of all of these, we'll just comment those out. 39 | 2:39 Forget this happiness bit for a minute, that was just playing around, OK, 40 | 2:43 so how should we do it? Of course, we probably want to pass this to the initializer, 41 | 2:48 if we did want to have the ability to set a name and set an age, 42 | 2:51 we could leave this here and say "self.name = None", and "age = None", 43 | 2:58 or maybe "age = 0", as it's unset, something like this. 44 | 3:04 So run, that doesn't give us a great answer, but at least in the __init__ 45 | 3:09 we know all the moving parts of our class, of our type. 46 | 3:12 So, this is not, well, this type has a name field, 47 | 3:17 if and only if you happen to call this other method but before that it doesn't, right, 48 | 3:21 this is a terrible way to write code, so don't do this. 49 | 3:24 And if you feel you must do something like this, 50 | 3:27 at least initialize these to somewhere empty. 51 | 3:29 But I am going to go ahead and say "let's not do this", 52 | 3:32 let's instead... I'll leave this commented out for you, for the code you download, 53 | 3:36 let's instead say "you have to supply a name and age to create this class", 54 | 3:40 to create an object of this class, 55 | 3:42 and we come down here to say "self.name = name" or in PyCharm you just hit Alt+Enter, 56 | 3:47 and it will do that for you, and Alt+Enter and it will do that for you, 57 | 3:52 and it does it of course, the way we are recommending. 58 | 3:56 But now, we've got to supply a name and an age 59 | 3:58 or it's going to crash as you can see, and Betsy's back. 60 | 4:05 Let me show you one other way of adding fields to a class 61 | 4:08 that works really well and is very common. 62 | 4:11 So remember, in our slicing example we were over here 63 | 4:13 doing this query against this measurement type, 64 | 4:16 let's look at that measurement class, for a minute. 65 | 4:18 This is a SQLAlchemy ORM-based class 66 | 4:21 and it's mapped into our database, via SQLAlchemy, 67 | 4:25 so basically there is a measurement table, corresponds to this class, 68 | 4:29 now notice, here we are not using the __init__ 69 | 4:33 we are actually defining the type by adding these class level fields, 70 | 4:36 so we've got id, x, y and value, 71 | 4:40 over here, notice we can say things like "cow.name" and that works fine, it runs fine, 72 | 4:48 but we can't say NotSoPythonicPet.name 73 | 4:52 Now, PyCharm finds it here but it probably shouldn't because if you run it, 74 | 4:56 this type has no name, obviously. 75 | 5:00 This is an instance level property, 76 | 5:02 you can think that this is kind of a static level type of thing, 77 | 5:05 as a static field from other languages if you will, 78 | 5:08 so over here, if we write it like this, we get to write code like this, 79 | 5:12 so here is how we actually wrote our query to do this, 80 | 5:15 I want to create a "query(Measurement)", "filtering(Measurement.value > .9)", 81 | 5:19 order_by(measurement.value.desc()), things like that. 82 | 5:23 OK, so this is another very common way 83 | 5:26 to define fields of our class that I think is Pythonic, 84 | 5:30 if you want them to be type level, if you want them to be only instance level, 85 | 5:34 this is the way to do it. 86 | 5:37 Here we have our NotSoPythonicPet, 87 | 5:40 and it's doing a couple of things right and a couple of things wrong. 88 | 5:43 First of all it has a "self.age = age", "self.name = name", in the __init__, that's good, 89 | 5:49 but when we call "get_name" it happens to set another field, 90 | 5:53 another attribute that only exists after you call "get_name" 91 | 5:57 that turns out to be a really bad idea. 92 | 5:59 And we can get the age back by saying "object.get_age" 93 | 6:02 we should define our fields only in the __init__ or in the more static style 94 | 6:06 that I showed you, a SQLAlchemy, 95 | 6:08 and we should never create new fields outside of __init__ 96 | 6:12 based on some behavior that makes it very hard 97 | 6:14 to understand if and when those fields will be there. --------------------------------------------------------------------------------