├── website ├── .htaccess ├── index.css └── index.html ├── src ├── cover │ ├── .gitignore │ └── goatbarcode.png ├── idle.png ├── .gitignore ├── Makefile ├── bgpython_part_0400_toolintro.md ├── bgpython_part_0300_install.md ├── README.md ├── bgpython_part_A0300_valref.md ├── bgpython_part_A0400_bases.md ├── bgpython_part_0100_intro.md ├── bgpython_part_A0200_repl.md ├── bgpython_part_0200_whatis.md ├── bgpython_part_A0500_shelltools.md ├── bgpython_part_A0100_math.md ├── bgpython_part_0900_dicts.md └── bgpython_part_0500_vars.md ├── source ├── examples │ ├── .htaccess │ ├── ex_printpid.py │ ├── ex_replist.py │ ├── ex_rand1000.py │ ├── ex_xfourth.py │ ├── inputtest.py │ ├── ex_10ksum.py │ ├── Makefile │ ├── hello.py │ ├── while.py │ ├── README.md │ ├── ex_goatcount.py │ ├── ex_listcompx5.py │ ├── ex_fstring.py │ ├── ex_listcompcap.py │ ├── ex_curdate.py │ ├── zipdir.py │ ├── ex_xsquared.py │ ├── ex_ntimes10.py │ ├── ifelse1.py │ ├── wargames.txt │ ├── ex_grav.py │ ├── ex_ennum.py │ ├── ex_uuidgen.py │ ├── booltest.py │ ├── fileread1.py │ ├── ex_listchange.py │ ├── ex_refval.py │ ├── ex_max.py │ ├── ex_listadd2.py │ ├── ex_printkeys.py │ ├── ex_oddsbetween.py │ ├── ex_sort.py │ ├── ex_listsum.py │ ├── ex_printkeysvals.py │ ├── games.csv │ ├── fiblist.py │ ├── ex_fizzbuzz.py │ ├── ex_wc.py │ ├── vartest.py │ ├── ex_charat.py │ ├── ex_listadd.py │ ├── listdouble.py │ ├── multtable.py │ ├── ex_multtablefile.py │ ├── twosum.py │ ├── ex_twosumfloat.py │ ├── ex_uppervowel.py │ ├── rocks.txt │ ├── ex_twosumdiff.py │ ├── ex_quadratic.py │ ├── ex_sliceat.py │ ├── ex_wrap.py │ ├── hashast.py │ ├── ex_car.py │ ├── ex_threesumdiff.py │ ├── ex_list2dict.py │ ├── ex_subway.py │ ├── ex_catchorder.py │ ├── ex_quadratic2.py │ ├── ex_listmult.py │ ├── ex_zipextract.py │ ├── listops.py │ ├── ex_writelines.py │ ├── moviesign.py │ ├── head.py │ ├── adv1.py │ ├── shipdist.py │ ├── ex_list2dictsort.py │ ├── moviesign2.py │ ├── familytree.py │ ├── ex_adv2.py │ ├── ex_simplecsv.py │ └── lineedit.py └── Makefile ├── Makefile ├── .gitignore └── README.md /website/.htaccess: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/cover/.gitignore: -------------------------------------------------------------------------------- 1 | bgn_cover*.pdf 2 | -------------------------------------------------------------------------------- /source/examples/.htaccess: -------------------------------------------------------------------------------- 1 | RemoveHandler .py 2 | -------------------------------------------------------------------------------- /source/examples/ex_printpid.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | print(os.getpid()) -------------------------------------------------------------------------------- /source/examples/ex_replist.py: -------------------------------------------------------------------------------- 1 | l = [1, 2, 3] * 6 2 | 3 | print(l) 4 | 5 | -------------------------------------------------------------------------------- /source/examples/ex_rand1000.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | print(random.randint(0, 1000)) -------------------------------------------------------------------------------- /source/examples/ex_xfourth.py: -------------------------------------------------------------------------------- 1 | for x in range(100): 2 | print(x, " ", x**4) 3 | -------------------------------------------------------------------------------- /src/idle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beejjorgensen/bgpython/HEAD/src/idle.png -------------------------------------------------------------------------------- /source/examples/inputtest.py: -------------------------------------------------------------------------------- 1 | value = input("Enter a value: ") 2 | print("You entered", value) 3 | -------------------------------------------------------------------------------- /source/examples/ex_10ksum.py: -------------------------------------------------------------------------------- 1 | t = 0 2 | 3 | for i in range(1, 10001): 4 | t += i 5 | 6 | print(t) 7 | -------------------------------------------------------------------------------- /src/cover/goatbarcode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/beejjorgensen/bgpython/HEAD/src/cover/goatbarcode.png -------------------------------------------------------------------------------- /source/examples/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean pristine 2 | 3 | all: 4 | 5 | clean: 6 | 7 | pristine: 8 | 9 | -------------------------------------------------------------------------------- /source/examples/hello.py: -------------------------------------------------------------------------------- 1 | print("Hello, world!") 2 | print("My name's Beej and this is (possibly) my first program!") -------------------------------------------------------------------------------- /source/examples/while.py: -------------------------------------------------------------------------------- 1 | x = 1200 2 | 3 | while x <= 1210: 4 | print(x) 5 | x += 1 6 | 7 | print("All done!") 8 | -------------------------------------------------------------------------------- /source/examples/README.md: -------------------------------------------------------------------------------- 1 | # Beej's Guide to Python Programming Examples 2 | 3 | Various Python programs from the Guide. 4 | -------------------------------------------------------------------------------- /source/examples/ex_goatcount.py: -------------------------------------------------------------------------------- 1 | s = "How many goats could a goat goat goat if a goat could goat" 2 | 3 | print(s.count("goat")) 4 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | bgpython*.pdf 2 | bgpython*.html 3 | bgpython*.epub 4 | bgpython_temp_* 5 | bg-css*.html 6 | split/ 7 | split-wide/ 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PACKAGE=bgpython 2 | BGBSPD_BUILD_DIR?=../bgbspd 3 | 4 | WEB_IMAGES=$(wildcard src/*.png src/*.svg) 5 | 6 | include $(BGBSPD_BUILD_DIR)/main.make 7 | -------------------------------------------------------------------------------- /source/examples/ex_listcompx5.py: -------------------------------------------------------------------------------- 1 | a = [14, 31, 44, 46, 54, 59, 45, 55, 21, 11, 8, 34, 66, 41] 2 | 3 | b = [x for x in a if x % 5 == 0] 4 | 5 | print(b) 6 | 7 | -------------------------------------------------------------------------------- /source/examples/ex_fstring.py: -------------------------------------------------------------------------------- 1 | x = 3490 2 | y = 3.14159 3 | 4 | print(f"x is {float(x):7.2f}") 5 | print(f"y is {y:7.2f}") 6 | print(f"x + y = {x + y:7.2f}") 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vim swap files 2 | 3 | .*.sw[op] 4 | 5 | # Build products 6 | 7 | stage/ 8 | 9 | __pycache__ 10 | 11 | # Temp files 12 | 13 | foo.* 14 | bar.* 15 | -------------------------------------------------------------------------------- /source/examples/ex_listcompcap.py: -------------------------------------------------------------------------------- 1 | a = ["alice", "beej", "chris", "dave", "eve", "frank"] 2 | 3 | b = [x.upper() for x in a if x[0] not in "aeiou"] 4 | 5 | print(b) 6 | 7 | -------------------------------------------------------------------------------- /source/examples/ex_curdate.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | # %a Abbreviated month 4 | # %b Abbreviated weekday 5 | # %d Day (number) of the month 6 | 7 | print(time.strftime("%a %b %d")) -------------------------------------------------------------------------------- /source/examples/zipdir.py: -------------------------------------------------------------------------------- 1 | import zipfile 2 | 3 | # Important: make sure example.zip is in the same directory 4 | # as this program! 5 | 6 | zf = zipfile.ZipFile('example.zip') 7 | 8 | zf.printdir() -------------------------------------------------------------------------------- /source/examples/ex_xsquared.py: -------------------------------------------------------------------------------- 1 | # Enter a number and store it in x 2 | x = input("Enter a number: ") 3 | 4 | # Convert to an integer 5 | x = int(x) 6 | 7 | # Print the square 8 | print(x, "squared is", x**2) 9 | -------------------------------------------------------------------------------- /source/examples/ex_ntimes10.py: -------------------------------------------------------------------------------- 1 | done = False 2 | 3 | while not done: 4 | x = input('Enter a number (or "quit"): ') 5 | if x == "quit": 6 | done = True 7 | else: 8 | x = int(x) 9 | print(x, "times 10 is", x * 10) 10 | 11 | -------------------------------------------------------------------------------- /source/examples/ifelse1.py: -------------------------------------------------------------------------------- 1 | x = input("Enter a number: ") 2 | x = float(x) 3 | 4 | if x >= 50 and x <= 59: 5 | print(x, "is between 50 and 59, inclusive") 6 | print("Well done!") 7 | else: 8 | print(x, "is not between 50 and 59, inclusive") 9 | -------------------------------------------------------------------------------- /source/examples/wargames.txt: -------------------------------------------------------------------------------- 1 | What he did was great! He designed his computer so that it could learn 2 | from its own mistakes. So, they'd be better the next time they played. 3 | The system actually learned how to learn. It could teach itself! 4 | -------------------------------------------------------------------------------- /source/examples/ex_grav.py: -------------------------------------------------------------------------------- 1 | def newtons_gravity(m1, m2, r): 2 | G=6.67430e-11 3 | 4 | return G * (m1 * m2) / (r * r) 5 | 6 | print(newtons_gravity(10, 20, 30)) 7 | print(newtons_gravity(10, 40, 30)) 8 | print(newtons_gravity(100, 5, 10)) 9 | -------------------------------------------------------------------------------- /source/examples/ex_ennum.py: -------------------------------------------------------------------------------- 1 | def ennum(n): 2 | english = [ "zero", "one", "two", "three", "four", "five", 3 | "six", "seven", "eight", "nine" ] 4 | 5 | return english[n] 6 | 7 | print(ennum(3), ennum(4), ennum(9), ennum(0)) 8 | 9 | -------------------------------------------------------------------------------- /source/examples/ex_uuidgen.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import uuid 3 | 4 | if len(sys.argv) != 2: 5 | print("usage: py_uuidgen.py count") 6 | sys.exit(1) 7 | 8 | count = int(sys.argv[1]) 9 | 10 | for _ in range(count): 11 | print(uuid.uuid4()) -------------------------------------------------------------------------------- /source/examples/booltest.py: -------------------------------------------------------------------------------- 1 | print(True) # True 2 | print(False) # False 3 | 4 | x = 10 5 | print(x == 10) # True 6 | print(x < 5) # False 7 | 8 | # You can store the Boolean result in a variable! 9 | r = x >= 7 10 | print(r) # True 11 | -------------------------------------------------------------------------------- /source/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: clean pristine all 2 | 3 | all: 4 | 5 | clean: 6 | $(MAKE) -C examples $@ 7 | #$(MAKE) -C exercises $@ 8 | #$(MAKE) -C man $@ 9 | 10 | pristine: 11 | $(MAKE) -C examples $@ 12 | #$(MAKE) -C exercises $@ 13 | #$(MAKE) -C man $@ 14 | -------------------------------------------------------------------------------- /source/examples/fileread1.py: -------------------------------------------------------------------------------- 1 | # Open the file 2 | f = open("wargames.txt") 3 | 4 | # Read all data from the file 5 | data = f.read() 6 | 7 | # Close the file 8 | f.close() 9 | 10 | # Print out the data we read earlier 11 | print(data) 12 | #print(data.upper()) -------------------------------------------------------------------------------- /source/examples/ex_listchange.py: -------------------------------------------------------------------------------- 1 | a = [11, 22, 33] 2 | 3 | print(a) # [11, 22, 33] 4 | 5 | a.append(99) 6 | 7 | print(a) # [11, 22, 33, 99] 8 | 9 | a.pop(1) 10 | 11 | print(a) # [11, 33, 99] 12 | 13 | a.insert(2, 88) 14 | 15 | print(a) # [11, 33, 88, 99] 16 | -------------------------------------------------------------------------------- /source/examples/ex_refval.py: -------------------------------------------------------------------------------- 1 | a = [1, 2, 3] 2 | #b = a # Copies reference to same list 3 | 4 | b = a.copy() # Makes a new list 5 | #b = list(a) # Also makes a new list 6 | #b = a[:] # Also makes a new list 7 | 8 | b[0] = 99 9 | 10 | print(a[0]) 11 | 12 | -------------------------------------------------------------------------------- /source/examples/ex_max.py: -------------------------------------------------------------------------------- 1 | done = False 2 | numbers = [] 3 | 4 | while not done: 5 | n = input("Enter a number (or \"done\"): ") 6 | 7 | if n == "done": 8 | done = True 9 | else: 10 | numbers.append(n) 11 | 12 | print(max(numbers)) 13 | 14 | -------------------------------------------------------------------------------- /source/examples/ex_listadd2.py: -------------------------------------------------------------------------------- 1 | def listadd(a, i0, i1): 2 | return a[i0] + a[i1] 3 | 4 | try: 5 | print(listadd([1, 2, 3], 0, 2)) # 1 + 3 = 4 6 | print(listadd([1, 2, 3], 0, 3)) # exception--3 out of range 7 | 8 | except IndexError as e: 9 | print(e) # "list index out of range" -------------------------------------------------------------------------------- /source/examples/ex_printkeys.py: -------------------------------------------------------------------------------- 1 | d = { 2 | "key1": "value1", 3 | "key2": "value1", 4 | "key3": "value1", 5 | "key4": "value1", 6 | } 7 | 8 | for key in d: 9 | print(key) 10 | 11 | # This also works, but is less idiomatic: 12 | for key in d.keys(): 13 | print(key) -------------------------------------------------------------------------------- /source/examples/ex_oddsbetween.py: -------------------------------------------------------------------------------- 1 | x = input("Enter a number: ") 2 | x = int(x) 3 | 4 | y = input("Enter a second number, bigger than that: ") 5 | y = int(y) 6 | 7 | # If x is even, bump it up to the next odd 8 | if x % 2 == 0: 9 | x += 1 10 | 11 | for i in range(x, y + 1, 2): 12 | print(i) 13 | -------------------------------------------------------------------------------- /source/examples/ex_sort.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if len(sys.argv) != 2: 4 | print(f"usage: {sys.argv[0]} filename") 5 | sys.exit(1) 6 | 7 | filename = sys.argv[1] 8 | 9 | with open(filename) as f: 10 | lines = list(f) 11 | 12 | lines.sort() 13 | 14 | for l in lines: 15 | print(l, end="") -------------------------------------------------------------------------------- /source/examples/ex_listsum.py: -------------------------------------------------------------------------------- 1 | a = [14, 31, 44, 46, 54, 59, 45, 55, 21, 11, 8, 34, 66, 41] 2 | 3 | total = 0 4 | 5 | for value in a: 6 | total += value 7 | 8 | print(total) 9 | 10 | # Python actually has this as a built-in with the sum() function: 11 | 12 | print(sum(a)) # Does the same as the loop above 13 | -------------------------------------------------------------------------------- /source/examples/ex_printkeysvals.py: -------------------------------------------------------------------------------- 1 | d = { 2 | "key1": "value1", 3 | "key2": "value1", 4 | "key3": "value1", 5 | "key4": "value1", 6 | } 7 | 8 | for k, v in d.items(): 9 | print(f"{k}: {v}") 10 | 11 | # This also works. Which do you like more? Why? 12 | 13 | for k in d: 14 | print(f"{k}: {d[k]}") -------------------------------------------------------------------------------- /source/examples/games.csv: -------------------------------------------------------------------------------- 1 | Title,Release Year,Studio,Publisher 2 | Minecraft,2011,Mojang,Microsoft Studios 3 | M.U.L.E.,1983,Ozark Softscape,Activision 4 | X-Men The Official Game,2006,Z-AXIS,Activision 5 | Populous,1989,Bullfrog Productions,Electronic Arts 6 | DOOM,1993,id Software,id Software 7 | Lemmings,1991,DMA Design,Psygnosis -------------------------------------------------------------------------------- /source/examples/fiblist.py: -------------------------------------------------------------------------------- 1 | # initialize the list with [0, 1] 2 | fib = [0, 1] 3 | 4 | # for 98 times 5 | for _ in range(98): 6 | 7 | # compute the sum of the previous two numbers 8 | s = fib[-1] + fib[-2] 9 | 10 | # append sum to the list 11 | fib.append(s) 12 | 13 | # print the list 14 | print(fib) 15 | -------------------------------------------------------------------------------- /source/examples/ex_fizzbuzz.py: -------------------------------------------------------------------------------- 1 | # There are a couple shortcuts that could be made to make this solution 2 | # more concise. Hunt for them! 3 | 4 | for i in range(1, 101): 5 | if i % 3 == 0 and i % 5 == 0: 6 | print("FizzBuzz") 7 | elif i % 3 == 0: 8 | print("Fizz") 9 | elif i % 5 == 0: 10 | print("Buzz") 11 | else: 12 | print(i) 13 | -------------------------------------------------------------------------------- /source/examples/ex_wc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if len(sys.argv) != 2: 4 | print(f"usage: {sys.argv[0]} filename") 5 | sys.exit(1) 6 | 7 | filename = sys.argv[1] 8 | 9 | word_count = 0 10 | 11 | with open(filename) as f: 12 | for line in f: 13 | words = line.split() 14 | word_count += len(words) 15 | 16 | print(word_count) -------------------------------------------------------------------------------- /source/examples/vartest.py: -------------------------------------------------------------------------------- 1 | x = 34 # Variable x is assigned value 34 2 | print(x) 3 | x = 90 # Variable x is assigned value 90 4 | print(x) 5 | 6 | y = x + 40 # y is assigned x + 40 which is 90 + 40, or 130 7 | print(x) # Still 90! Nothing changed here 8 | print(y) # Prints "130" 9 | 10 | x = 1000 11 | print(y) # Still 130! 12 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | TITLE="Beej's Guide to Python Programming" 2 | SUBTITLE="For Beginners" 3 | AUTHOR='Brian “Beej Jorgensen” Hall' 4 | VERSION_DATE="v0.0.15, Copyright © December 9, 2025" 5 | 6 | GUIDE_ID=bgpython 7 | 8 | BGBSPD_BUILD_DIR?=../../bgbspd 9 | 10 | WEB_IMAGES=$(wildcard *.png *.svg) 11 | 12 | include $(BGBSPD_BUILD_DIR)/source.make 13 | -------------------------------------------------------------------------------- /source/examples/ex_charat.py: -------------------------------------------------------------------------------- 1 | s = input("Enter a string: ") 2 | 3 | valid_input = False 4 | 5 | while not valid_input: 6 | i = input(f"Enter a number (0 to {len(s)-1}): ") 7 | 8 | i = int(i) 9 | 10 | # Set valid_input directly with Boolean expression 11 | valid_input = i >= 0 and i <= len(s)-1 12 | 13 | print(f'Character at index {i} is "{s[i]}"') 14 | -------------------------------------------------------------------------------- /source/examples/ex_listadd.py: -------------------------------------------------------------------------------- 1 | class InvalidListSize(Exception): 2 | pass 3 | 4 | def listadd(a): 5 | if len(a) != 3: 6 | raise InvalidListSize("list must be 3 elements in length") 7 | 8 | return sum(a) 9 | 10 | try: 11 | print(listadd([1, 2, 3])) 12 | print(listadd([1, 2])) 13 | 14 | except InvalidListSize as e: 15 | print(e.args[0]) -------------------------------------------------------------------------------- /source/examples/listdouble.py: -------------------------------------------------------------------------------- 1 | x = [1, 2, 3, 4, 5, 6] 2 | 3 | # for each element in the list 4 | 5 | for i, v in enumerate(x): 6 | 7 | # if that element is even 8 | 9 | if v % 2 == 0: # check if v is even 10 | 11 | # double the value and store it at the same place in the list 12 | x[i] = v * 2 13 | 14 | print(x) # Print it out, just for fun 15 | -------------------------------------------------------------------------------- /source/examples/multtable.py: -------------------------------------------------------------------------------- 1 | valid_input = False 2 | 3 | while not valid_input: 4 | x = input("Enter a number between 1 and 19: ") 5 | x = int(x) 6 | 7 | if x >= 1 and x <= 19: 8 | valid_input = True 9 | 10 | for row in range(1, x + 1): 11 | for product in range(row, row * (x + 1), row): 12 | print(f"{product:4}", end="") 13 | print() 14 | -------------------------------------------------------------------------------- /source/examples/ex_multtablefile.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if len(sys.argv) != 3: 4 | print(f'usage: {sys.argv[0]} size filename') 5 | sys.exit(1) 6 | 7 | size = int(sys.argv[1]) 8 | filename = sys.argv[2] 9 | 10 | with open(filename, "w") as f: 11 | for row in range(1, size + 1): 12 | for product in range(row, row * (size + 1), row): 13 | f.write(f"{product:4}") 14 | f.write('\n') 15 | -------------------------------------------------------------------------------- /source/examples/twosum.py: -------------------------------------------------------------------------------- 1 | # Read string from keyboard into variable x 2 | x = input("Enter a number: ") 3 | 4 | # Convert x to int and store it back in x again 5 | x = int(x) 6 | 7 | # Read string from keyboard into variable y 8 | y = input("Enter another number: ") 9 | 10 | # Convert y to int and store it back in y again 11 | y = int(y) 12 | 13 | # Print the sum of x + y 14 | print("The sum of the two numbers is:", x + y) 15 | -------------------------------------------------------------------------------- /source/examples/ex_twosumfloat.py: -------------------------------------------------------------------------------- 1 | # Read string from keyboard into variable x 2 | x = input("Enter a number: ") 3 | 4 | # Convert x to float and store it back in x again 5 | x = float(x) 6 | 7 | # Read string from keyboard into variable y 8 | y = input("Enter another number: ") 9 | 10 | # Convert y to float and store it back in y again 11 | y = float(y) 12 | 13 | # Print the sum of x + y 14 | print("The sum of the two numbers is:", x + y) 15 | -------------------------------------------------------------------------------- /source/examples/ex_uppervowel.py: -------------------------------------------------------------------------------- 1 | s = "The quick brown fox jumps over the lazy dogs." 2 | 3 | s2 = "" 4 | 5 | for c in s.lower(): 6 | if c == "a" or c == "e" or c == "i" or c == "o" or c == "u": 7 | c = c.upper() 8 | 9 | # A more concise way to write the above line is with "in", something 10 | # we haven't discussed yet: 11 | #if c in "aeiou": 12 | # c = c.upper() 13 | 14 | # Build the new string a character at at time: 15 | s2 += c 16 | 17 | print(s2) 18 | -------------------------------------------------------------------------------- /source/examples/rocks.txt: -------------------------------------------------------------------------------- 1 | marble 2 | coal 3 | granite 4 | quartzite 5 | slate 6 | phylite 7 | gniess 8 | amphibolite 9 | hornfels 10 | serpentinite 11 | gabbro 12 | eclogite 13 | migmatite 14 | diorite 15 | basalt 16 | obsidian 17 | scoria 18 | andesite 19 | dacite 20 | peridotite 21 | tuff 22 | shale 23 | breccia 24 | limestone 25 | chalk 26 | chert 27 | siltstone 28 | argillite 29 | claystone 30 | graywacke 31 | mudstone 32 | turbidite 33 | schist 34 | flint 35 | rhyolite 36 | pumice 37 | sandstone 38 | pegmatite 39 | -------------------------------------------------------------------------------- /source/examples/ex_twosumdiff.py: -------------------------------------------------------------------------------- 1 | # read string from keyboard into variable x 2 | x = input("Enter a number: ") 3 | 4 | # convert x to int and store it back in x again 5 | x = int(x) 6 | 7 | # read string from keyboard into variable y 8 | y = input("Enter another number: ") 9 | 10 | # convert y to int and store it back in y again 11 | y = int(y) 12 | 13 | # print the sum of x + y 14 | print("The sum of the two numbers is:", x + y) 15 | 16 | # print the difference of x - y 17 | print("The difference of the two numbers is:", x - y) 18 | -------------------------------------------------------------------------------- /source/examples/ex_quadratic.py: -------------------------------------------------------------------------------- 1 | import math # Needed for sqrt 2 | 3 | # Input all values 4 | a = input("Enter value for a: ") 5 | b = input("Enter value for b: ") 6 | c = input("Enter value for c: ") 7 | 8 | # Convert from string to float 9 | a = float(a) 10 | b = float(b) 11 | c = float(c) 12 | 13 | # Compute the square root part 14 | sqrt_part = math.sqrt(b**2 - 4*a*c) 15 | 16 | # Compute plus and minus answers 17 | x_plus = (-b + sqrt_part) / (2*a) 18 | x_minus = (-b - sqrt_part) / (2*a) 19 | 20 | # Print the result 21 | print("x is", x_plus, "or", x_minus) 22 | -------------------------------------------------------------------------------- /source/examples/ex_sliceat.py: -------------------------------------------------------------------------------- 1 | s = input("Enter a string: ") 2 | 3 | valid_input = False 4 | 5 | while not valid_input: 6 | i0 = input(f"Enter a number (0 to {len(s)}): ") 7 | 8 | i0 = int(i0) 9 | 10 | # Set valid_input directly with Boolean expression 11 | valid_input = i0 >= 0 and i0 <= len(s) 12 | 13 | valid_input = False 14 | 15 | while not valid_input: 16 | i1 = input(f"Enter a number ({i0} to {len(s)}): ") 17 | 18 | i1 = int(i1) 19 | 20 | # Set valid_input directly with Boolean expression 21 | valid_input = i1 >= 0 and i1 <= len(s) 22 | 23 | print(f'Slice from {i0} to {i1} is "{s[i0:i1]}"') 24 | -------------------------------------------------------------------------------- /source/examples/ex_wrap.py: -------------------------------------------------------------------------------- 1 | import textwrap 2 | 3 | matrix = """The Matrix is everywhere. It is all around us. Even 4 | now, in this very room. You can see it when you look out your window, 5 | or when you turn on your television. You can feel it when you go to 6 | work, when you go to church, when you pay your taxes.""" 7 | 8 | # Get a list of lines of max width 40 9 | lines = textwrap.wrap(matrix, width=40) 10 | 11 | # We could do this to print out each line 12 | #for l in lines: 13 | # print(l) 14 | 15 | # Or I could just join them together by newlines and print the whole 16 | # thing 17 | 18 | print("\n".join(lines)) -------------------------------------------------------------------------------- /source/examples/hashast.py: -------------------------------------------------------------------------------- 1 | input_valid = False # Assume it's invalid to start 2 | 3 | while not input_valid: # While input invalid 4 | x = input("Enter a number, 5-50 inclusive: ") 5 | x = int(x) 6 | 7 | if x >= 5 and x <= 50: 8 | input_valid = True # We got a good number! 9 | else: 10 | print("The number must be between 5 and 50, inclusive!") 11 | 12 | # Print the line 13 | for i in range(x): 14 | if i < 30: 15 | print("#", end="") # Set the end-of-line character to nothing 16 | else: 17 | print("*", end="") 18 | 19 | print() # Add a newline to the end of the line 20 | 21 | -------------------------------------------------------------------------------- /source/examples/ex_car.py: -------------------------------------------------------------------------------- 1 | # There's no single right answer here. This is just an example. 2 | 3 | class Car: 4 | def __init__(self, name, make, model, year, value): 5 | self.name = name 6 | self.make = make 7 | self.model = model 8 | self.year = year 9 | self.value = value 10 | self.location = None 11 | 12 | def honk(self): 13 | print("Beep beep!") 14 | 15 | def drive_to(self, destination): 16 | print(f'Welcome to {destination}!') 17 | self.location = destination 18 | 19 | def rename(self, name): 20 | self.name = name 21 | 22 | def total(self): 23 | print("Oh noes!") 24 | self.value = 0 25 | -------------------------------------------------------------------------------- /source/examples/ex_threesumdiff.py: -------------------------------------------------------------------------------- 1 | # read string from keyboard into variable x 2 | x = input("Enter a number: ") 3 | 4 | # convert x to int and store it back in x again 5 | x = int(x) 6 | 7 | # read string from keyboard into variable y 8 | y = input("Enter another number: ") 9 | 10 | # convert y to int and store it back in y again 11 | y = int(y) 12 | 13 | # read string from keyboard into variable z 14 | z = input("Enter another number: ") 15 | 16 | # convert z to int and store it back in z again 17 | z = int(z) 18 | 19 | # print the sum of x + y + z 20 | print("The sum of the three numbers is:", x + y + z) 21 | 22 | # print the difference of x - y - z 23 | print("The difference of the three numbers is:", x - y - z) 24 | -------------------------------------------------------------------------------- /source/examples/ex_list2dict.py: -------------------------------------------------------------------------------- 1 | # Starting list 2 | a = ["Chris", "Annie", "Beej", "Aaron", "Charlie"] 3 | 4 | # Eventually everything will be in this dict: 5 | d = {} 6 | 7 | # Loop through the names 8 | for name in a: 9 | 10 | # Get the first letter to use as the key 11 | first_letter = name[0] 12 | 13 | # If the key doesn't exist in d yet, we need to add it 14 | if first_letter not in d: 15 | # Make it an empty list to start 16 | d[first_letter] = [] 17 | 18 | # Now that we're sure there's a list in d for this particular key, 19 | # let's append the name to the end of the list: 20 | d[first_letter].append(name) 21 | 22 | # Print out the dict 23 | for k, v in d.items(): 24 | print(f"{k}: {v}") 25 | -------------------------------------------------------------------------------- /source/examples/ex_subway.py: -------------------------------------------------------------------------------- 1 | class SubwayCar: 2 | """Information about a single subway car""" 3 | 4 | def __init__(self, name): 5 | self.name = name 6 | self.next = None 7 | 8 | def __str__(self): 9 | return self.name 10 | 11 | head = SubwayCar("Engine") 12 | car1 = SubwayCar("Passenger car 1") 13 | car2 = SubwayCar("Passenger car 2") 14 | car3 = SubwayCar("Passenger car 3") 15 | 16 | # Hook all the cars together 17 | head.next = car1 18 | car1.next = car2 19 | car2.next = car3 20 | car3.next = None # End of the train 21 | 22 | # We start at the head of the train 23 | location = head 24 | 25 | # Walk along the train until the end 26 | while location is not None: 27 | print(location) 28 | location = location.next # Move to the next car -------------------------------------------------------------------------------- /source/examples/ex_catchorder.py: -------------------------------------------------------------------------------- 1 | # When handling exceptions, the first except clause to match the 2 | # exception is the one that triggers. 3 | # 4 | # Since the Exception class is the base class for all exceptions, it 5 | # effectively means that all exceptions _are_ Exceptions. 6 | # 7 | # So if we handle Exception _before_ ZeroDivisionError, then it's 8 | # Exception's handler than will run (because ZeroDivisionE#rror is an 9 | # Exception). 10 | # 11 | # To fix it, we need to put ZeroDivisionError _before_ Exception in our 12 | # # list of except clauses. 13 | 14 | try: 15 | x = 3490 / 0 16 | except ZeroDivisionError: 17 | # This will catch it 18 | print("Division by Zero") 19 | except Exception: 20 | # This will catch any other exceptions that might have occurred 21 | print("Exception") -------------------------------------------------------------------------------- /source/examples/ex_quadratic2.py: -------------------------------------------------------------------------------- 1 | import math # Needed for sqrt 2 | 3 | # Input all values 4 | a = input("Enter value for a: ") 5 | b = input("Enter value for b: ") 6 | c = input("Enter value for c: ") 7 | 8 | # Convert from string to float 9 | a = float(a) 10 | b = float(b) 11 | c = float(c) 12 | 13 | # Compute the square root part 14 | sqrt_part = math.sqrt(b**2 - 4*a*c) 15 | 16 | # Compute plus and minus answers 17 | x_plus = (-b + sqrt_part) / (2*a) 18 | x_minus = (-b - sqrt_part) / (2*a) 19 | 20 | # Print the result 21 | print("x is", x_plus, "or", x_minus) 22 | 23 | # Compute the quadratic ax^2 + bx + c to compare against 0. 24 | # We can use either x_plus or x_minus since they're both 25 | # solutions. 26 | result = a * x_plus**2 + b * x_plus + c 27 | 28 | print("Plugging x back in, we should get zero:", result) 29 | -------------------------------------------------------------------------------- /source/examples/ex_listmult.py: -------------------------------------------------------------------------------- 1 | multtable = [] # Will contain rows 2 | 3 | for row_num in range(13): # 0-12, inclusive 4 | row = [] # Will hold products for this row 5 | multtable.append(row) # Add it to the table 6 | 7 | for col_num in range(13): 8 | row.append(row_num * col_num) # Add product to the row 9 | 10 | print(multtable[5][6]) # 30 11 | print(multtable[6][5]) # 30 12 | print(multtable[4][7]) # 28 13 | print(multtable[0][0]) # 0 14 | print(multtable[12][12]) # 144 15 | 16 | # Test to verify ALL values are correct: 17 | 18 | for row_num in range(13): 19 | for col_num in range(13): 20 | product = row_num * col_num 21 | if multtable[row_num][col_num] != product: 22 | print(f"Bad data: {row_num} x {col_num} != {product}") 23 | 24 | # No news is good news 25 | -------------------------------------------------------------------------------- /source/examples/ex_zipextract.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import zipfile 3 | 4 | arg_count = len(sys.argv) 5 | 6 | # Make sure the user specified the right number of arguments, and 7 | # help them out if they didn't 8 | if arg_count < 2 or arg_count > 3: 9 | print("usage: zipextract.py zipfile [memberfile]") 10 | sys.exit(1) # Exit program, indicating error code 1 11 | 12 | # Name of the ZIP file 13 | zip_filename = sys.argv[1] 14 | 15 | if arg_count == 3: 16 | # Name of the file we want to extract from the ZIP file 17 | member_filename = sys.argv[2] 18 | else: 19 | # Sentinel value to let us know it wasn't specified 20 | member_filename = None 21 | 22 | # Open the ZIP file 23 | zf = zipfile.ZipFile(zip_filename) 24 | 25 | if member_filename is not None: 26 | # If the user specified a file to extract, extract it 27 | zf.extract(member_filename) 28 | else: 29 | # Otherwise, extract everything 30 | zf.extractall() -------------------------------------------------------------------------------- /source/examples/listops.py: -------------------------------------------------------------------------------- 1 | a = [5, 2, 8, 4, 7, 4, 0, 9] 2 | 3 | print(len(a)) # 8, the number of elements in the list 4 | 5 | a.append(100) 6 | 7 | print(a) # [5, 2, 8, 4, 7, 4, 0, 9, 100] 8 | 9 | print(a.count(4)) # 2, the number of 4s in the list 10 | 11 | print(a.index(4)) # 3, the index of the first 4 in the list 12 | 13 | v = a.pop() # Remove the 100 from the end of the list 14 | 15 | print(v) # 100 16 | print(a) # [5, 2, 8, 4, 7, 4, 0, 9] 17 | 18 | a.reverse() # Reverse the list 19 | 20 | print(a) # [9, 0, 4, 7, 4, 8, 2, 5] 21 | 22 | a.insert(2, 999) # insert 999 before index 2 23 | 24 | print(a) # [9, 0, 999, 4, 7, 4, 8, 2, 5] 25 | 26 | b = [1, 2, 3] 27 | 28 | a.extend(b) # Add contents of b to end of a 29 | 30 | print(a) # [9, 0, 999, 4, 7, 4, 8, 2, 5, 1, 2, 3] 31 | 32 | a.sort() # Sort all elements 33 | 34 | print(a) # [0, 1, 2, 2, 3, 4, 4, 5, 7, 8, 9, 999] 35 | 36 | a.clear() # Remove all elements 37 | 38 | print(a) # [], an empty list of length 0 39 | -------------------------------------------------------------------------------- /source/examples/ex_writelines.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # Make sure the user has entered enough stuff on the command line 4 | if len(sys.argv) < 4: 5 | # If not, tell them how to use this program 6 | print(f'usage: {sys.argv[0]} filename count word1 [word2 ...]') 7 | # Exiting with a non-zero code indicates failure to the shell, and 8 | # is conventional: 9 | sys.exit(1) 10 | 11 | # Let's get some decent names for these values to make our code easier 12 | # to read 13 | filename = sys.argv[1] 14 | count = int(sys.argv[2]) 15 | 16 | # Now build up the lines. We'll join all the remaining arguments with 17 | # spaces, and add a newline to the end: 18 | line = " ".join(sys.argv[3:]) + '\n' 19 | 20 | # Open the file 21 | with open(filename, "w") as f: 22 | # Loop through the count and write lines that many times. "_" as a 23 | # variable name indicates to readers that you're not interested in 24 | # the value from the range() iterator---you're merely using it to 25 | # loop a number of times. 26 | for _ in range(count): 27 | f.write(line) 28 | -------------------------------------------------------------------------------- /source/examples/moviesign.py: -------------------------------------------------------------------------------- 1 | class Theater: 2 | """Holds all the information about a specific theater.""" 3 | def __init__(self, name): 4 | self.name = name 5 | self.movies = [] 6 | 7 | class Movie: 8 | """Holds all the information about a specific movie.""" 9 | def __init__(self, name, duration, genre): 10 | self.name = name 11 | self.duration = duration 12 | self.genre = genre 13 | 14 | movies = [ 15 | Movie("Star Wars", 125, "scifi"), 16 | Movie("Shaun of the Dead", 100, "romzomcom"), 17 | Movie("Citizen Kane", 119, "drama") 18 | ] 19 | 20 | theaters = [ 21 | Theater("McMenamin's Old St. Francis Theater"), 22 | Theater("Tin Pan Theater"), 23 | Theater("Tower Theater") 24 | ] 25 | 26 | # McMenamin's is showing Star Wars and Shaun of the Dead 27 | theaters[0].movies.append(movies[0]) 28 | theaters[0].movies.append(movies[1]) 29 | 30 | # Tin Pan is showing Shaun of the Dead and Fastest Indian 31 | theaters[1].movies.append(movies[1]) 32 | theaters[1].movies.append(movies[2]) 33 | 34 | # Tower is showing all three 35 | theaters[2].movies.append(movies[0]) 36 | theaters[2].movies.append(movies[1]) 37 | theaters[2].movies.append(movies[2]) 38 | 39 | def print_theater(theater): 40 | """Print all the information about a theater.""" 41 | 42 | print(f'{theater.name} is showing:') 43 | 44 | for m in theater.movies: 45 | print(f' {m.name} ({m.genre}, {m.duration} minutes)') 46 | 47 | # Main code 48 | for t in theaters: 49 | print_theater(t) -------------------------------------------------------------------------------- /source/examples/head.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | if len(sys.argv) != 3: 4 | print("usage: head.py filename count") 5 | sys.exit(1) 6 | 7 | filename = sys.argv[1] 8 | 9 | try: 10 | # Convert the line count to an int--this might raise an exception 11 | total_count = int(sys.argv[2]) 12 | 13 | # If the count is a valid number, let's check to see if it's 14 | # invalid: 15 | if total_count < 1: 16 | # And if it is, let's force the exception handler to run 17 | raise ValueError() 18 | 19 | except ValueError: 20 | # This handler gets called if either the call to int() fails, or if 21 | # we detect that count is less than 1 22 | 23 | print("head.py: count must be a positive integer") 24 | 25 | # exit with a different error code just in case the shell wants to 26 | # do something more specific with this error 27 | sys.exit(2) 28 | 29 | line_count = 0 # Number of lines we've read so far 30 | 31 | try: 32 | # Open the file 33 | with open(filename) as f: 34 | 35 | # Read lines one at a time 36 | for line in f: 37 | 38 | # Keep track of the number of lines we've read 39 | line_count += 1 40 | 41 | # If it's greater than our target amount, bail out 42 | if line_count > total_count: 43 | break 44 | 45 | # Otherwise, print the line (suppressing the newline, since 46 | # there's already one at the end of the line) 47 | print(line, end="") 48 | 49 | except IOError as e: 50 | # I know that IOError objects have a useful message stored in the 51 | # `sterror` attribute, so we'll print that. 52 | # 53 | # This will give us output like 54 | # 55 | # head.py: filename.txt: No such file or directory 56 | # 57 | print(f'head.py: {filename}: {e.strerror}') 58 | -------------------------------------------------------------------------------- /source/examples/adv1.py: -------------------------------------------------------------------------------- 1 | # The map 2 | 3 | map_data = [ 4 | "#####################", 5 | "#...#...............#", 6 | "#...#..........#....#", 7 | "#...#..........#....#", 8 | "#...#..........#....#", 9 | "#...#..........#....#", 10 | "#..............#....#", 11 | "#..............#....#", 12 | "#####################" 13 | ] 14 | 15 | # Player position 16 | 17 | player_row = 4 18 | player_column = 9 19 | 20 | quit = False 21 | 22 | while not quit: 23 | 24 | # Print map and player indicator 25 | 26 | for row_index, row in enumerate(map_data): # for each row 27 | for col_index, map_character in enumerate(row): # for each col 28 | if row_index == player_row and col_index == player_column: 29 | print("@", end="") # end="" no newline 30 | else: 31 | print(map_character, end="") 32 | print() 33 | 34 | # Get input 35 | 36 | command = input("Enter a move (n,s,w,e,q): ") 37 | 38 | # Figure out the new row and column of the player 39 | # Make sure input is valid 40 | if command == "n": 41 | new_row = player_row - 1 42 | new_column = player_column 43 | elif command == "s": 44 | new_row = player_row + 1 45 | new_column = player_column 46 | elif command == "w": 47 | new_row = player_row 48 | new_column = player_column - 1 49 | elif command == "e": 50 | new_row = player_row 51 | new_column = player_column + 1 52 | elif command == "q": 53 | quit = True 54 | continue 55 | else: 56 | print(f'Unknown command {command}') 57 | 58 | if map_data[new_row][new_column] != ".": 59 | print("You can't move that way!") 60 | else: 61 | # Set the current position to the new position 62 | player_row = new_row 63 | player_column = new_column 64 | 65 | -------------------------------------------------------------------------------- /source/examples/shipdist.py: -------------------------------------------------------------------------------- 1 | # We need the math module for sqrt() 2 | import math 3 | 4 | def get_ship_locations(): 5 | """ 6 | Ask the user to enter a number of X,Y,Z ships coordinates. 7 | Returns a list of all the coordinates. 8 | """ 9 | 10 | done = False # True when the user asks to be done 11 | locations = [] # Master list of all ship positions 12 | 13 | while not done: 14 | xyz = input('Enter ship location x,y,z (or "done"): ') 15 | 16 | if xyz == "done": 17 | done = True 18 | else: 19 | # Get a list of the x,y,z coordinates 20 | xyz_list = xyz.split(',') 21 | 22 | # Convert to integers 23 | for i, v in enumerate(xyz_list): 24 | xyz_list[i] = int(v) 25 | 26 | # Build the master list 27 | locations.append(xyz_list) 28 | 29 | return locations 30 | 31 | def dist3d(p0, p1): 32 | """Return the Euclidean distance between 2 3D points.""" 33 | 34 | # Compute the difference in the Xs, Ys, and Zs 35 | dx = p0[0] - p1[0] 36 | dy = p0[1] - p1[1] 37 | dz = p0[2] - p1[2] 38 | 39 | # Compute the distance. (Remember that multiplying a number by 40 | # itself is the same as squaring the number.) 41 | return math.sqrt(dx*dx + dy*dy + dz*dz) 42 | 43 | def print_grid(locations): 44 | """Print a grid of ship-to-ship distances.""" 45 | 46 | num_ships = len(locations) 47 | 48 | print(" " * 8, end="") 49 | 50 | for i in range(num_ships): 51 | print(f'{i:8}', end="") 52 | 53 | print() 54 | 55 | for i in range(num_ships): 56 | print(f'{i:8}', end="") 57 | for j in range(num_ships): 58 | dist = dist3d(locations[i], locations[j]) 59 | print(f'{dist:8.2f}', end="") 60 | print() 61 | 62 | locations = get_ship_locations() 63 | print_grid(locations) 64 | 65 | -------------------------------------------------------------------------------- /source/examples/ex_list2dictsort.py: -------------------------------------------------------------------------------- 1 | # Starting list 2 | a = ["Chris", "Annie", "Beej", "Aaron", "Charlie"] 3 | 4 | # Eventually everything will be in this dict: 5 | d = {} 6 | 7 | # You have a couple options for sorting the names in the final sublists. 8 | # You can sort them at the end, when you print them. But you can also 9 | # sort them in advance. If we sort the list with the names, then they'll 10 | # naturally be in sorted order when we append them to the end of the 11 | # sublists. 12 | # 13 | # In this case, it doesn't make that much difference since we have to 14 | # run through all the names to sort them one way or another. But there's 15 | # potentially less overhead calling .sort() once instead of multiple 16 | # times. 17 | # 18 | # But there are a number of places algorithms can be simplified if you 19 | # sort the list up front ahead of time. It takes time to sort, so you 20 | # don't want to do it if it doesn't help you. But every time I see an 21 | # algorithmic problem looping over data, I always ask myself if it buys 22 | # me anything to sort the data ahead of time. 23 | 24 | a.sort() # Sort the list in advance 25 | 26 | # Loop through the names 27 | for name in a: 28 | 29 | # Get the first letter to use as the key 30 | first_letter = name[0] 31 | 32 | # If the key doesn't exist in d yet, we need to add it. This is a 33 | # really common programming pattern: 34 | if first_letter not in d: 35 | # Make it an empty list to start 36 | d[first_letter] = [] 37 | 38 | # Now that we're sure there's a list in d for this particular key, 39 | # let's append the name to the end of the list: 40 | d[first_letter].append(name) 41 | 42 | # Print out the dict sorted by key 43 | for k in sorted(d): 44 | # We don't have to sort the sublists here because we did it above. 45 | # If you decided to do it here, instead, that's not really wrong, 46 | # either. Which way do you like more? Why? 47 | print(f"{k}: {d[k]}") 48 | -------------------------------------------------------------------------------- /website/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica,Arial,sans-serif; 3 | font-size: 12pt; 4 | margin: 30px; 5 | } 6 | 7 | #adsense { 8 | float: right; 9 | margin: 10px; 10 | } 11 | 12 | .maintitle { 13 | font-weight:bold; 14 | font-size: 2.0em; 15 | margin-bottom: 0px; 16 | margin-top: 3ex; 17 | } 18 | 19 | .subtitle { 20 | font-weight: bold; 21 | font-style: italic; 22 | font-size: 1.5em; 23 | margin-top: 0.5ex; 24 | } 25 | 26 | .secttitle { 27 | font-weight: bold; 28 | font-size: 1.2em; 29 | } 30 | 31 | .subsecttitle { 32 | font-weight: bold; 33 | } 34 | 35 | .email { 36 | font-family: "Courier","Courier New",monospace; 37 | font-weight: bold; 38 | font-size: 0.9em; 39 | } 40 | 41 | .headerbar { 42 | border: #000 solid 2px; 43 | border-color: #000; 44 | border-style: solid; 45 | border-width: 2px; 46 | background-color: #DDD; 47 | margin: 1em; 48 | padding: 1em; 49 | } 50 | 51 | .beggartable { 52 | border: #000,solid,2px; 53 | border-color: #000; 54 | border-style: solid; 55 | border-width: 2px; 56 | margin: 1em; 57 | padding: 1em; 58 | } 59 | 60 | hr.singleline { 61 | border: #000 solid 0px; 62 | height: 1px; 63 | background-color: #000; 64 | border-color: #000; 65 | } 66 | 67 | .ilb { 68 | display: inline-block; 69 | } 70 | 71 | .w50p { 72 | width: 50% 73 | } 74 | 75 | .w100p { 76 | width: 100% 77 | } 78 | 79 | .center { 80 | text-align: center; 81 | } 82 | 83 | .left { 84 | text-align: left; 85 | } 86 | 87 | #suggestive-sell { 88 | font-weight: bold; 89 | margin-bottom: 1ex; 90 | margin-top: 1ex; 91 | } 92 | 93 | .hr { 94 | height: 1px; 95 | background: #333; 96 | display: inline-block; 97 | } 98 | 99 | .float-left { 100 | float: left; 101 | } 102 | 103 | .clear-both { 104 | clear: both; 105 | } 106 | 107 | .mb2ex { 108 | margin-bottom: 2ex; 109 | } 110 | 111 | .ib800 { 112 | display: inline-block; 113 | max-width: 800px; 114 | } 115 | 116 | .small { 117 | font-size: 0.8em; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /source/examples/moviesign2.py: -------------------------------------------------------------------------------- 1 | class Theater: 2 | """Holds all the information about a specific theater.""" 3 | def __init__(self, name): 4 | self.name = name 5 | self.movietimes = [] # <-- Now this is MovieTime objects 6 | 7 | class Movie: 8 | """Holds all the information about a specific movie.""" 9 | def __init__(self, name, duration, genre): 10 | self.name = name 11 | self.duration = duration 12 | self.genre = genre 13 | 14 | class MovieTime: 15 | """Holds a movie and the times it is playing""" 16 | def __init__(self, movie, times): 17 | self.movie = movie 18 | self.times = times 19 | 20 | movies = [ 21 | Movie("Star Wars", 125, "scifi"), 22 | Movie("Shaun of the Dead", 100, "romzomcom"), 23 | Movie("Citizen Kane", 119, "drama") 24 | ] 25 | 26 | theaters = [ 27 | Theater("McMenamin's Old St. Francis Theater"), 28 | Theater("Tin Pan Theater"), 29 | Theater("Tower Theater") 30 | ] 31 | 32 | # McMenamin's is showing Star Wars and Shaun of the Dead 33 | theaters[0].movietimes.append(MovieTime(movies[0], ["7pm", "9pm", "10pm"])) 34 | theaters[0].movietimes.append(MovieTime(movies[1], ["5pm", "8pm"])) 35 | 36 | # Tin Pan is showing Shaun of the Dead and Fastest Indian 37 | theaters[1].movietimes.append(MovieTime(movies[1], ["2pm", "5pm"])) 38 | theaters[1].movietimes.append(MovieTime(movies[2], ["6pm", "8pm", "10pm"])) 39 | 40 | # Tower is showing all three 41 | theaters[2].movietimes.append(MovieTime(movies[0], ["3pm"])) 42 | theaters[2].movietimes.append(MovieTime(movies[1], ["5pm", "7pm"])) 43 | theaters[2].movietimes.append(MovieTime(movies[2], ["6pm", "7pm", "8pm"])) 44 | 45 | def print_theater(theater): 46 | """Print all the information about a theater.""" 47 | 48 | print(f'{theater.name} is showing:') 49 | 50 | for mt in theater.movietimes: 51 | m = mt.movie 52 | t = " ".join(mt.times) # Make string of times separated by spaces 53 | print(f' {m.name} ({m.genre}, {m.duration} minutes): {t}') 54 | 55 | # Main code 56 | for t in theaters: 57 | print_theater(t) -------------------------------------------------------------------------------- /source/examples/familytree.py: -------------------------------------------------------------------------------- 1 | tree = { 2 | "Beej Jorgensen": { 3 | "born": 1990, 4 | "mother": "Mom Jorgensen", 5 | "father": "Dad Jorgensen", 6 | "siblings": ["Brother Jorgensen", "Sister Jorgensen", "Little Sister Jorgensen"] 7 | }, 8 | "Mom Jorgensen": { 9 | "born": 1970, 10 | "mother": "Grandma Jorgensen", 11 | "father": "Grandpa Jorgensen", 12 | "siblings": ["Auntie Jorgensen"] 13 | }, 14 | "Dad Jorgensen": { 15 | "born": 1965, 16 | "mother": "Granny Jorgensen", 17 | "father": "Grandad Jorgensen", 18 | "siblings": ["Uncle Jorgensen"] 19 | }, 20 | "Brother Jorgensen": { 21 | "born": 1989, 22 | "mother": "Mom Jorgensen", 23 | "father": "Dad Jorgensen", 24 | "siblings": ["Beej Jorgensen", "Sister Jorgensen", "Little Sister Jorgensen"] 25 | }, 26 | "Sister Jorgensen": { 27 | "born": 1991, 28 | "mother": "Mom Jorgensen", 29 | "father": "Dad Jorgensen", 30 | "siblings": ["Beej Jorgensen", "Brother Jorgensen", "Little Sister Jorgensen"] 31 | }, 32 | "Little Sister Jorgensen": { 33 | "born": 1992, 34 | "mother": "Mom Jorgensen", 35 | "father": "Dad Jorgensen", 36 | "siblings": ["Beej Jorgensen", "Brother Jorgensen", "Sister Jorgensen"] 37 | }, 38 | "Grandma Jorgensen": { 39 | "born": 1950, 40 | "mother": "Great Grandma Jorgensen", 41 | "father": "Great Grandpa Jorgensen", 42 | "siblings": [] 43 | }, 44 | "Grandpa Jorgensen": { 45 | "born": 1945, 46 | "mother": "Great Granny Jorgensen", 47 | "father": "Great Grandad Jorgensen", 48 | "siblings": [] 49 | } 50 | } 51 | 52 | done = False 53 | 54 | while not done: 55 | name = input("Enter a name (or q to quit): ") 56 | 57 | if name == "q": 58 | done = True 59 | continue 60 | 61 | record = tree.get(name) 62 | 63 | if record is None: 64 | print(f'No record for "{name}"') 65 | continue 66 | 67 | mother_name = record["mother"] 68 | father_name = record["father"] 69 | 70 | mother_record = tree.get(mother_name) 71 | father_record = tree.get(father_name) 72 | 73 | if mother_record is not None: 74 | mother_born_date = mother_record["born"] 75 | else: 76 | mother_born_date = "no record" 77 | 78 | if father_record is not None: 79 | father_born_date = father_record["born"] 80 | else: 81 | father_born_date = "no record" 82 | 83 | print("Parents:") 84 | print(f' {mother_name} ({mother_born_date})') 85 | print(f' {father_name} ({father_born_date})') 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Beej's Guide to Python Programming 2 | 3 | This is the source for Beej's Guide to Python Programming. 4 | 5 | If you merely wish to read the guide, please visit the [Beej's Guide to 6 | Python Programming](https://beej.us/guide/bgpython/) website. 7 | 8 | This is here so that Beej has everything in a repo and so translators 9 | can easily clone it. 10 | 11 | ## Build Instructions 12 | 13 | ### Dependencies 14 | 15 | * [Gnu make](https://www.gnu.org/software/make/) (XCode make works, too) 16 | * [Python 3+](https://www.python.org/) 17 | * [Pandoc 2.8+](https://pandoc.org/) 18 | * XeLaTeX (can be found in [TeX Live](https://www.tug.org/texlive/)) 19 | * [Liberation fonts](https://en.wikipedia.org/wiki/Liberation_fonts) (sans, serif, mono) 20 | 21 | Mac dependencies install (reopen terminal after doing this): 22 | 23 | ``` 24 | xcode-select --install # installs make 25 | brew install python # installs Python3 26 | brew install pandoc 27 | brew install --cask mactex # installs XeLaTeX 28 | brew tap homebrew/cask-fonts 29 | brew install font-liberation # installs sans, serif, and mono 30 | ``` 31 | 32 | ### Dependency: Build System 33 | 34 | This depends on an external repo to build: [Beej's Guide Build System 35 | for Pandoc](https://github.com/beejjorgensen/bgbspd). 36 | 37 | You'll want to clone that repo as a sibling to this one: 38 | 39 | ``` 40 | mystuff-->bggit 41 | \-->bgbspd 42 | ``` 43 | 44 | The Makefiles here will look for the build system there. 45 | 46 | You can override the `bgbspd` directory before running `make` like this: 47 | 48 | ``` 49 | export BGBSPD_BUILD_DIR=/some/path/to/bgbspd 50 | ``` 51 | 52 | ### Build 53 | 54 | 1. Type `make all` from the top-level directory. 55 | 56 | If you have Gnu Make, it should work fine. Other makes might work as 57 | well. Windows users might want to check out Cygwin. 58 | 59 | 2. Type `make stage` to copy all the build products and website to the 60 | `stage` directory. 61 | 62 | 3. There is no step three. 63 | 64 | You can also `cd` to the `src` directory and `make`. 65 | 66 | `make clean` cleans, and `make pristine` cleans to "original" state. 67 | 68 | To embed your own fonts in the PDFs, see the `src/Makefile` for examples. 69 | 70 | The `upload` target in the root `Makefile` demonstrates the build steps 71 | for a complete release. You'll need to change the `UPLOADDIR` macro in 72 | the top-level `Makefile` to point to your host if you want to use that. 73 | You're free to upload whatever versions you desire individually, as 74 | well. 75 | 76 | ## Pull Requests 77 | 78 | Please keep these on the scale of typo and bug fixes. That way I don't 79 | have to consider any copyright issues when merging changes. 80 | 81 | ## TODO 82 | 83 | ### Content 84 | 85 | * Everything 86 | -------------------------------------------------------------------------------- /source/examples/ex_adv2.py: -------------------------------------------------------------------------------- 1 | class Room: 2 | """Holds information about a room in the game""" 3 | 4 | def __init__(self, name): 5 | self.name = name 6 | self.n_to = None 7 | self.s_to = None 8 | self.w_to = None 9 | self.e_to = None 10 | 11 | def __str__(self): 12 | return self.name 13 | 14 | # All the rooms 15 | 16 | hallmist = Room("Hall of Mists") 17 | hallmtking = Room("Hall of the Mountain King") 18 | lowns = Room("Low N/S passage") 19 | y2 = Room("Y2") 20 | winpit = Room("Low Window Overlooking a Pit") 21 | westside = Room("West Side Chamber") 22 | southside = Room("South Side Chamber") 23 | 24 | # Connect all the rooms 25 | 26 | hallmist.n_to = hallmtking 27 | hallmtking.e_to = hallmist # This passage turns a corner! 28 | 29 | hallmtking.s_to = southside 30 | southside.n_to = hallmtking 31 | 32 | hallmtking.w_to = westside 33 | westside.e_to = hallmtking 34 | 35 | hallmtking.n_to = lowns 36 | lowns.s_to = hallmtking 37 | 38 | lowns.n_to = y2 39 | y2.s_to = lowns 40 | 41 | y2.w_to = winpit 42 | winpit.e_to = y2 43 | 44 | # Player location 45 | 46 | location = hallmtking 47 | 48 | done = False 49 | 50 | def cant_go(): 51 | print("You can't go that way") 52 | 53 | while not done: 54 | print(location) 55 | 56 | dir = input("Enter a direction (n,s,w,e,q): ") 57 | 58 | if dir == 'q': 59 | done = True 60 | continue 61 | 62 | 63 | """ 64 | # This code is really repeatingly and redundantly repetitive. 65 | # So, even though it works, I've commented it out and replaced 66 | # it with something more concise, below. 67 | 68 | if dir == 'n': 69 | if location.n_to is None: 70 | cant_go() 71 | else: 72 | location = location.n_to 73 | 74 | elif dir == 's': 75 | if location.s_to is None: 76 | cant_go() 77 | else: 78 | location = location.s_to 79 | 80 | elif dir == 'w': 81 | if location.w_to is None: 82 | cant_go() 83 | else: 84 | location = location.w_to 85 | 86 | elif dir == 'e': 87 | if location.e_to is None: 88 | cant_go() 89 | else: 90 | location = location.e_to 91 | """ 92 | 93 | # Due to a happy coincidence, the name of the direction attribute we 94 | # want to follow is always what the user entered plus the string 95 | # "_to". So if they entered "n", we want to look at the attribute 96 | # "n_to", and so on. 97 | # 98 | # So we have a string of the attribute we want to check. This is a 99 | # prime use case for getattr(). 100 | 101 | dir_attr = dir + "_to" 102 | 103 | # Get the next room in that direction, or return None if there is no 104 | # room that way 105 | next_room = getattr(location, dir_attr, None) 106 | 107 | if next_room is None: 108 | cant_go() 109 | else: 110 | location = next_room 111 | 112 | # Replaced all that stuff, above, with six lines. Not bad! -------------------------------------------------------------------------------- /source/examples/ex_simplecsv.py: -------------------------------------------------------------------------------- 1 | class Game: 2 | def __init__(self, title, year, studio, publisher): 3 | self.title = title 4 | self.year = year 5 | self.studio = studio 6 | self.publisher = publisher 7 | 8 | def __str__(self): 9 | # There's a lot of stuff to unpack on the next line. 10 | # 11 | # A colon followed by a number is the field width that will 12 | # display this value. (That is, it will pad with spaces to make 13 | # it that big, and we use this to align columns.) 14 | # 15 | # When you specify a field width, strings justify to the left by 16 | # default, and numbers to the right. I want them all to justify 17 | # left, so I force the year to justify left with a less-than 18 | # sign. 19 | # 20 | # Now, this line goes longer than I want it to aesthetically (80 21 | # columns is my max). So I split the line and put a backslash at 22 | # the very end. In this context the backslash is referred to as 23 | # an "escape". It's a way to telling Python, "Hey, pretend 24 | # there's no newline here." 25 | # 26 | # Finally, I take advantage of the fact that in Python, two 27 | # strings next to each other are treated as a single string. 28 | # 29 | # Example: 30 | # 31 | # s = 'foo' 'bar' 32 | # print(s) # foobar 33 | return f'{self.title:25} {self.year:<6} {self.studio:22} ' \ 34 | f'{self.publisher:22}' 35 | 36 | def __repr__(self): 37 | # We don't use the repr, but it's good practice to include it in 38 | # all your classes just in case. 39 | return f'Game({repr(self.title)},{repr(self.year)},' \ 40 | f'{repr(self.studio)},{repr(self.publisher)})' 41 | 42 | 43 | # List of games that we read from the CSV file 44 | games_list = [] 45 | 46 | with open("games.csv") as f: 47 | 48 | # Call next() right away to get and discard the first line of the 49 | # file (the header line) 50 | next(f) 51 | 52 | # Go through every line in the file 53 | for line in f: 54 | # Extract the fields 55 | title, year, studio, publisher = line.strip().split(",") 56 | 57 | # Make a new Game object with that data in it 58 | g = Game(title, int(year), studio, publisher) 59 | 60 | # Append the new object to the games list 61 | games_list.append(g) 62 | 63 | # Sort the list 64 | # 65 | # In the following like, the keyword arg "key" is set to a function that 66 | # returns the value that should be used as a key when determining the 67 | # sort order. We want to sort by year, so we pass it a function that 68 | # returns the year for any given game. 69 | games_list.sort(key=lambda g: g.year) 70 | 71 | # Lambda is like a small, anonymous function. g is the parameter. And 72 | # the thing after the : is the return value. The above line is 73 | # equivalent to: 74 | # 75 | # def get_game_sort_key(g): 76 | # return g.year 77 | # 78 | # games_list.sort(key=get_game_sort_key) 79 | # 80 | # Same thing, but the lambda function makes it far more concise. 81 | 82 | # Print all the games 83 | for g in games_list: 84 | print(g) 85 | -------------------------------------------------------------------------------- /src/bgpython_part_0400_toolintro.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # How do I write a program? 6 | 7 | ## Objectives 8 | 9 | * Edit some source code in the IDLE editor. 10 | * Run that program. 11 | 12 | ## The Problem That Needs Solving 13 | 14 | Let's use our problem-solving framework! 15 | 16 | 1. **Understand the Problem**: We want to write a program that prints a 17 | neat little message to the screen. 18 | 19 | 2. **Devise a plan** 20 | 21 | 1. Run IDLE. 22 | 2. Open a new file from the File pulldown. 23 | 4. Write code and save it to that file. 24 | 5. Run your program from within the IDE. 25 | 26 | 3. **Carry out the Plan**: This is where we execute our plan. We'll 27 | do that in the following sections. 28 | 29 | 4. **Look Back**: We'll do this, below, as well. In future chapters, 30 | we'll leave these last two off the number list and just do them in 31 | subsequent sections. 32 | 33 | Let's go! 34 | 35 | ## Launching your IDE and Opening a File 36 | 37 | Run IDLE as discussed in the previous chapter. 38 | 39 | Once in there, we're going to make a new file. 40 | 41 | > It used to be that everyone who used computers knew what a file was. 42 | > But these days, many people use computers for years without 43 | > encountering the concept. 44 | > 45 | > So! A _file_ is a collection of data with a name. Examples of files 46 | > would be images, movies, PDF documents, music files, and so on. 47 | > 48 | > The name indicates something about the contents of the file. 49 | > Generally. It really can be anything, but that would be misleading, 50 | > like labeling a box of raisins as "Chocolate". 51 | > 52 | > The name is split into two parts, commonly, separated by a period. The 53 | > first part is the name, and the second part is the _extension_. 54 | > Confusingly sometimes people refer to the name and extension together 55 | > as the "name", so you'll have to rely on context. 56 | > 57 | > Sometimes, depending on the system, the extension is optional. 58 | > 59 | > As an example, here's a complete file name and extension: 60 | > 61 | > ``` {.default} 62 | > hello.py 63 | > ``` 64 | > 65 | > 66 | > There we have a file named `hello` and an extension `.py`. This is a 67 | > common extension that means "this is a Python source code file". 68 | 69 | Pull down "File→New" and that'll bring up a blank window. 70 | 71 | And let's enter some code! 72 | 73 | Type [flx[the following|hello.py]] into the editor (the line numbers, 74 | below, are for reference only and shouldn't be typed in): 75 | 76 | ``` {.py .numberLines} 77 | print("Hello, world!") 78 | print("My name's Beej and this is (possibly) my first program!") 79 | ``` 80 | 81 | We're (almost) ready to run! 82 | 83 | ## Running the Program! 84 | 85 | Hit `F5` from the editor window (you might have to hit `fn-F5` on the 86 | Mac) to run the code. Alternately, you can pull down the "Run" menu and 87 | select "Run Module". 88 | 89 | If you haven't saved the file, it will prompt you to save the file. 90 | (You can pull down "File→Save", or hit `COMMAND-S` or `CTRL-S` to do 91 | this preemptively.) 92 | 93 | Give it a good name, like `hello.py`. 94 | 95 | And then, in the console window, you'll see the output appear! _[Angelic 96 | Chorus!]_ 97 | 98 | ``` {.default} 99 | Hello, world! 100 | My name's Beej and this is (possibly) my first program! 101 | ``` 102 | 103 | Did you miss it? Hit `F5` again and you'll see it appear again. 104 | 105 | You just wrote some instructions and the computer carried it out! 106 | 107 | Next up: write a Quake III clone! 108 | 109 | Okay, so maybe there might be a small number of _in between_ things that 110 | I skimmed over, but, as Obi-Wan Kenobi once said, "You've taken your 111 | first step into a larger world." 112 | 113 | ## Exercises 114 | 115 | Remember to use the [four problem-solving steps](#problem-solving) to 116 | solve these problems: understand the problem, devise a plan, carry it 117 | out, look back to see what you could have done better. 118 | 119 | 1. Make another program called `dijkstra.py` that prints out your three 120 | favorite [fl[Edsger Dijkstra 121 | quotes|https://en.wikiquote.org/wiki/Edsger_W._Dijkstra]]. 122 | 123 | ## Summary 124 | 125 | * Use the problem solving framework! 126 | * Edit some source code in the IDLE editor. 127 | * Run that program from within IDLE. 128 | 129 | -------------------------------------------------------------------------------- /src/bgpython_part_0300_install.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # What software will I need? 6 | 7 | ## Objectives 8 | 9 | * Install Python, and explain what it does 10 | * Learn what an _Integrated Development Environment_ (IDE) is. 11 | 12 | ## What Is All This? 13 | 14 | Python is a _programming language_. It interprets instructions that you, 15 | the programmer, give it, and it executes them. Think of it like a robot 16 | you can give a series of commands to ahead of time, and then have it run 17 | off and do them on its own. 18 | 19 | In programmer parlance, we call these sets of instructions _code_. 20 | 21 | In addition to being a programming language, Python is also a program, 22 | itself! It's a program that runs other programs! We don't have to worry 23 | about the details of that at all, except that since Python is a program, 24 | it's something we'll have to install on our computers. 25 | 26 | > Python comes in different versions. The big versions are 2 and 3. 27 | > We'll be using Python 3 for everything in this book. Python 2 is 28 | > older, and is rarely used in new projects. 29 | 30 | Python also comes with an _Integrated Development Environment_, or IDE. 31 | An IDE is a program that helps you write, run, and debug (remove the 32 | errors from) code. 33 | 34 | It's main components are: 35 | 36 | * The _editor_. This is like a word processor except specifically 37 | designed for use with code. 38 | 39 | * The _debugger_. This helps you step through your code a line at a time 40 | and watch the data values change as you go. It can help you find 41 | places where your code is incorrect. 42 | 43 | * The _console_ or _terminal_. This is a window where the output from 44 | your program appears (and where you might type input to the program). 45 | 46 | The name of Python's built-in IDE is _IDLE_. There are other IDEs we'll 47 | talk about later. 48 | 49 | ## Installing Python 50 | 51 | ### Windows 52 | 53 | There are two ways to do this: 54 | 55 | * Install from the Microsoft Store 56 | * Install from the official website 57 | 58 | I can't see any disadvantage to installing it from the store. Just 59 | remember to install Python 3 (not Python 2). 60 | 61 | If you install it from the [fl[official 62 | website|https://www.python.org/downloads/]], you need to remember to 63 | check the "Add to PATH" box during the install procedure! 64 | 65 | Another option to installing Python on Windows is through WSL. We'll 66 | cover this later. 67 | 68 | ### Mac 69 | 70 | Download and install Python for Mac from the [fl[official 71 | website|https://www.python.org/downloads/]]. 72 | 73 | Another option to installing Python on Mac is through Homebrew. We'll 74 | cover this later. 75 | 76 | ### Linux/Unix-likes 77 | 78 | The Linux community tends to be pretty supportive of people looking to 79 | install things. Google for something like `ubuntu install python3`, 80 | replacing `ubuntu` with the name of your distribution. 81 | 82 | 83 | ## Running IDLE 84 | 85 | Here's where we get to run the IDE for the first time. First we'll look 86 | at how to do it on various platforms, and then we'll run some Python 87 | code in it. 88 | 89 | Running IDLE depends on the platform: 90 | 91 | |Platform|Commands| 92 | |--------|------------------------------------------------------------| 93 | |Windows|Hit the Start menu and type "idle". It should show up in the pick list and you can click to open it.| 94 | |Mac|Hit CMD-SPACE and type "idle". It should show up in the pick list and you can click to open it.| 95 | |Unix-like|Type `idle` in the terminal or find it in your desktop pulldown menu.| 96 | 97 | If you run `idle` on the command line and it says something about the command not being 98 | found, try running `idle3`. 99 | 100 | If you get an error on the command line that looks like this: 101 | 102 | ``` {.default} 103 | ** IDLE can't import Tkinter. 104 | Your Python may not be configured for Tk. ** 105 | ``` 106 | 107 | you'll have to install the Tk graphical toolkit. This might be a package 108 | called `tk` or maybe `python-tk`. If you're on a Unix-like, search for 109 | how to install on your system. On a Mac with Homebrew, you can `brew 110 | install python-tk`. 111 | 112 | If you get another error, cut and paste that error into your favorite 113 | search engine to see what other people say about how to solve it. 114 | 115 | Once IDLE is up, you should see a window that looks vaguely like this: 116 | 117 | ![IDLE Window](idle.png) 118 | 119 | ## Your First Command 120 | 121 | In the IDLE window after the `>>>` prompt, type: 122 | 123 | ```python 124 | print("Hello, world!") 125 | ``` 126 | 127 | and hit `RETURN`. This commands Python to output the words "Hello, 128 | world!". 129 | 130 | ```python 131 | >>> print("Hello, world!") 132 | Hello, world! 133 | ``` 134 | 135 | And it did! 136 | 137 | This is just the beginning! 138 | 139 | ## Summary 140 | 141 | * The integrated development environment (IDE) has an editor, a 142 | debugger, and a terminal window. 143 | * The code editor in the IDE is where you'll be typing your programs. 144 | * The programs, also known as _code_, are a series of instructions that 145 | Python will execute. 146 | * Python is a program that will run your Python programs! 147 | 148 | -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # Beej's Guide to Python Programming source markdown 2 | 3 | ## TODO 4 | * Get terminology right in list comps 5 | * backport variable chapter structure to previous chapters 6 | 7 | * How to Google 8 | - Variables 9 | - Data types 10 | - Conditionals 11 | - Looping 12 | * More data types 13 | - Reference Types 14 | - Strings 15 | - Lists 16 | - List Comprehensions 17 | - Dicts 18 | * Sets 19 | * Tuples 20 | - REPL 21 | - File I/O 22 | * CSV 23 | * Debugger, Debugging 24 | - Functions 25 | - Try/except 26 | * Sorting 27 | * Unicode, character encoding 28 | * Bytes and binary data 29 | * marshalling 30 | * OOP 31 | * Number Bases 32 | * Unit testing, testing in general 33 | - Modules 34 | * Making your own modules 35 | * Closures 36 | * Generators 37 | * Iterators 38 | * Decorators 39 | * Networking 40 | 41 | * Recursion 42 | * Data Structures? 43 | * Big O? 44 | 45 | - Appendix: programmer math 46 | * Appendix: compiled vs interpreted 47 | 48 | * debugging exercises throughout (Remember those PC lint challenges?) 49 | 50 | 51 | ## Beej's extensions to markdown 52 | 53 | These are brought to life with the `bin/preproc` script. 54 | 55 | * `[nobr[s]]` 56 | 57 | Don't break text of string `s`. Doesn't work inside code markdown, 58 | sadly. 59 | 60 | You can also just escape a space with backslash in markdown for the 61 | same effect. 62 | 63 | * `[[pagebreak]]` 64 | 65 | Issue a page break on the printed version. Renders as LaTeX 66 | `\newpage`. 67 | 68 | * `[nh[word]]` 69 | 70 | Don't allow hypenation of this word. Translates to 71 | `\hyphenation{word}`. Underscores are prohibited due to LaTeX 72 | restrictions. 73 | 74 | * `[ix[entry]]` 75 | 76 | Build a LaTeX index entry, substituting it as-is into an `\index{}` 77 | element. **Remember to escape your underscores with `\_` in these 78 | entries!** [LaTeX indexing examples 79 | here](https://en.wikibooks.org/wiki/LaTeX/Indexing#Sophisticated_indexing). 80 | 81 | * `[ixtt[entry]]` 82 | 83 | For a single, simple index entry, renders in typewriter text with 84 | `\index{entry@\texttt{entry}}`. More complex entries should be 85 | rendered by passing raw LaTeX into `[ix[]]`. 86 | 87 | * `[fl[linktext|url]]` 88 | 89 | Footnote Link. Hyperlink linktext to the url and show the URL in a 90 | footnote. Translates to `[linktext](url)^[url]`. 91 | 92 | * `[flx[linktext|file]]` 93 | 94 | Footnote Link to Beej's Examples. Automatically prepends 95 | `https://beej.us/guide/bgnet/examples/` to the file and shows the URL 96 | in a footnote. 97 | 98 | * `[flr[link|id]]` 99 | 100 | Footnote Link to Beej's Redirect. Automatically prepends 101 | `https://beej.us/guide/url/` to the link id and shows the URL in a 102 | footnote. 103 | 104 | * `[flrfc[link|num]]` 105 | 106 | Footnote Link to RFC. Automatically prepends 107 | `https://tools.ietf.org/html/rfc` to the RFC number and shows the URL 108 | in a footnote. 109 | 110 | 111 | ## pandoc markdown quirks 112 | 113 | If you have multiple inline footnotes in the same paragraph but on 114 | different lines of the markdown, all lines except the last must end with 115 | a trailing space. 116 | 117 | man page subsections are h4 `####` to keep from showing up in the 118 | contents. Pandoc 2.8 should have a way to suppress h3 from contents. 119 | 120 | Table rows need to be on one line for proper wrapping (newlines are 121 | preserved in table cells). 122 | 123 | Tables: the relative widths of the headers is reflected in the final 124 | output. 125 | 126 | Table header template: 127 | 128 | ``` 129 | | Macro | Description | 130 | |-----------------|--------------------------------------------------------| 131 | ``` 132 | 133 | `` doesn't work. Use header `{#tags}`. 134 | 135 | Fenced code with a standard language name can sometimes cause LaTeX to puke. 136 | 137 | ```` 138 | ```c Don't do this 139 | 140 | ``` {.c} Do this 141 | ``` {.c .numberLines} Or this 142 | ```` 143 | 144 | Indexing done with latex `\index{foo}` markers. They don't show up in 145 | HTML output. 146 | 147 | LaTex indexing examples: 148 | 149 | (Escape underscores with `\_`!) 150 | 151 | ```latex 152 | \index{foo} plain element 153 | \index{foo\_bar} element with underscore 154 | \index{foo()@\texttt{foo()}} render foo() in monospace in index 155 | \index{O\_NONBLOCK@\texttt{O\_NONBLOCK}} mono with underscores 156 | \index{foo!bar} subindex bar in foo 157 | \index{bind()@\texttt{bind()}!implicit} subindex with mono 158 | ``` 159 | 160 | [More LaTeX indexing examples 161 | here](https://en.wikibooks.org/wiki/LaTeX/Indexing#Sophisticated_indexing). 162 | 163 | Index entries should be at the main level and not in headers or bold or 164 | italicized text (or they'll appear as seperate entries in the index). 165 | 166 | Put a `\newpage` before each manpage to force a page break. 167 | 168 | When editing `bgnet_amazon.md`, use `\newpage` to force widows onto the 169 | next page. 170 | 171 | Footnotes after links on sequential lines need a blank space at the end 172 | of the line to work properly...? 173 | 174 | ```markdown 175 | [Hey](url)^[footnote], <-- Need a blank space at the end of this line 176 | [Again](url2)^[footnote2]. 177 | ``` 178 | 179 | 180 | -------------------------------------------------------------------------------- /source/examples/lineedit.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | 4 | def read_file(filename): 5 | """Read a file from disk""" 6 | lines = [] 7 | 8 | with open(filename) as f: 9 | for line in f: 10 | lines.append(line) 11 | 12 | return lines 13 | 14 | 15 | def write_file(lines, filename): 16 | """Write a file to disk""" 17 | with open(filename, "w") as f: 18 | for line in lines: 19 | f.write(line) 20 | 21 | 22 | def zero_to_one(n): 23 | """Convert a number from a 0-based index to a 1-based index.""" 24 | return n + 1 25 | 26 | 27 | def one_to_zero(n): 28 | """Convert a number from a 1-based index to a 0-based index.""" 29 | return n - 1 30 | 31 | 32 | def get_num_arg(arg): 33 | """Helper function to get a numeric argument from a command.""" 34 | v = int(arg) 35 | v = one_to_zero(v) 36 | 37 | return v 38 | 39 | 40 | def handle_write(args, lines): 41 | """Handle the write command""" 42 | 43 | if len(args) == 1: 44 | filename = args[0] 45 | else: 46 | print("usage: w filename") 47 | return 48 | 49 | write_file(lines, filename) 50 | 51 | 52 | def handle_list(args, lines): 53 | """List lines from the file.""" 54 | 55 | if len(args) == 1: 56 | # Compute start and end lines 57 | start = get_num_arg(args[0]) 58 | end = start + 10 59 | 60 | else: 61 | print("usage: l line_num") 62 | return 63 | 64 | # Make sure start isn't before the beginning of the list 65 | if start < 0: 66 | start = 0 67 | 68 | # Make sure end isn't past the end of the list 69 | if end > len(lines): 70 | end = len(lines) 71 | 72 | # Print all the lines 73 | for i in range(start, end): 74 | # end="" to suppress newlines (since lines already have them) 75 | print(f'{zero_to_one(i)}: {lines[i]}', end="") 76 | 77 | 78 | def handle_edit(args, lines): 79 | """Edit a line in the file.""" 80 | 81 | if len(args) == 1: 82 | # Get the line number to edit 83 | line_num = get_num_arg(args[0]) 84 | else: 85 | print("usage: e line_num") 86 | return 87 | 88 | # Make sure we're in range 89 | if line_num < 0 or line_num >= len(lines): 90 | print("no such line") 91 | return 92 | 93 | # Edit the line, adding a newline to the end (since input() strips 94 | # it off). 95 | lines[line_num] = input() + '\n' 96 | 97 | 98 | def handle_delete(args, lines): 99 | """Delete a line in the file.""" 100 | 101 | if len(args) == 1: 102 | # Get the line number to delete 103 | line_num = get_num_arg(args[0]) 104 | 105 | else: 106 | print("usage: d line_num") 107 | return 108 | 109 | # Make sure we're in range 110 | if line_num < 0 or line_num >= len(lines): 111 | print("no such line") 112 | return 113 | 114 | # Delete the line 115 | lines.pop(line_num) 116 | 117 | 118 | def handle_append(args, lines): 119 | """Append a line in the file.""" 120 | 121 | if len(args) == 1: 122 | # Get the line number to append at. 123 | line_num = get_num_arg(args[0]) 124 | 125 | else: 126 | print("usage: a line_num") 127 | return 128 | 129 | # +1 because we want to line_num adding lines one _after_ the 130 | # specified line. 131 | line_num += 1 132 | 133 | done = False 134 | 135 | # We're going to loop until the user enters a single `.` on a line 136 | while not done: 137 | 138 | # Read a line of input 139 | line = input() 140 | 141 | # Check if we're done 142 | if line == '.': 143 | done = True 144 | continue # Jump back to the `while` 145 | 146 | # Otherwise, insert the line, adding a newline to the end (since 147 | # input() strips it off). 148 | lines.insert(line_num, line + '\n') 149 | 150 | # And now on to the next line 151 | line_num += 1 152 | 153 | 154 | # Main 155 | 156 | 157 | # Parse the command line 158 | 159 | if len(sys.argv) == 2: 160 | filename = sys.argv[1] 161 | 162 | elif len(sys.argv) == 1: 163 | # We'll use this as a sentinel value later if we need to prompt for 164 | # a filename when writing the file. 165 | filename = None 166 | 167 | else: 168 | print("usage: lineedit.py [filename]", file=sys.stderr) 169 | sys.exit(1) 170 | 171 | 172 | # Read the file (if specified); set up the lines list 173 | if filename is not None: 174 | lines = read_file(filename) 175 | else: 176 | lines = [] 177 | 178 | done = False 179 | 180 | # Main loop 181 | 182 | while not done: 183 | command = input("> ").strip() 184 | 185 | # If the user entered a blank line, just give them another prompt 186 | if command == '': 187 | continue 188 | 189 | # Grab the arguments after the command 190 | args = command.split(" ")[1:] 191 | 192 | # Quit 193 | if command == 'q': 194 | done = True 195 | 196 | # Write (save) the file 197 | elif command[0] == 'w': 198 | handle_write(args, lines) 199 | 200 | # List lines 201 | elif command[0] == 'l': 202 | handle_list(args, lines) 203 | 204 | # Edit a line 205 | elif command[0] == 'e': 206 | handle_edit(args, lines) 207 | 208 | # Delete a line 209 | elif command[0] == 'd': 210 | handle_delete(args, lines) 211 | 212 | # Append lines 213 | elif command[0] == 'a': 214 | handle_append(args, lines) 215 | 216 | else: 217 | print("unknown command") 218 | -------------------------------------------------------------------------------- /src/bgpython_part_A0300_valref.md: -------------------------------------------------------------------------------- 1 | # Appendix C: Assignment Behavior {#assignment-behavior} 2 | 3 | In this book, we've talked about how Python variables work, but let's 4 | dig into it a little more here. 5 | 6 | When we have data of some kind, like a number or a list or a string, 7 | that's stored in memory. And we can assign it to a variable name so that 8 | we can have a way to refer to it. 9 | 10 | That variable name is a reference to the data. 11 | 12 | So if everything's a reference, that must mean that if we do this, 13 | there's only one string, right? Just two names for the same string? 14 | 15 | ``` {.py} 16 | s = "Beej!" 17 | t = s 18 | ``` 19 | 20 | Yes! That's exactly what that means. `s` and `t` both refer to the same 21 | string in memory. That means if you changed the string, both `s` and `t` 22 | would show the changes because they're both two names for the same 23 | string. 24 | 25 | _But you can't change the string! It's immutable!_ 26 | 27 | It's the same with numbers: 28 | 29 | ``` {.py} 30 | x = -3490 31 | y = x 32 | ``` 33 | 34 | Both `x` and `y` refer to the same number in memory. If you changed the 35 | number, both `x` and `y` would show the change. 36 | 37 | _But you can't change the number! It's immutable!_ 38 | 39 | Let's try a list: 40 | 41 | ``` {.py} 42 | c = [1, 2, 3] 43 | d = c 44 | ``` 45 | 46 | Just like with strings and numbers, both variables `c` and `d` refer to 47 | the same thing in memory. But the difference is that the list _is_ 48 | mutable! We _can_ change it, and we'd see the change in `c` and `d`. 49 | 50 | ``` {.py} 51 | c = [1, 2, 3] 52 | d = c 53 | 54 | c[1] = 99 55 | print(d[1]) # 99 56 | ``` 57 | 58 | Of course, you can reassign a variable to point at anything else at any time. 59 | 60 | ## How This Relates To Functions 61 | 62 | All this adds up to Python's call-by-sharing evaluation strategy. 63 | 64 | When you call a function, all the arguments passed to the function are 65 | assigned into the parameters in the parameter list. 66 | 67 | That assignment, even though it doesn't use the `=` assignment operator, 68 | behaves in the same way, nonetheless. 69 | 70 | In the following example, both `x` and `a` refer to the same object... 71 | right up to the moment we reassign `x` on line 4. At that point, `x` 72 | refers to a different list, but `a` still refers to the original. 73 | 74 | ``` {.py .numberLines} 75 | def foo(x): 76 | x[1] = 99 # x still refers to the same list as a 77 | 78 | x = [4, 5, 6] # x refers to a different list than a, now 79 | 80 | a = [1, 2, 3] 81 | foo(a) 82 | ``` 83 | 84 | ## Is Any of This True? 85 | 86 | Yes, believe it! 87 | 88 | We can verify it in the [REPL](#repl) with the built-in `id()` function 89 | and the `is` operator. 90 | 91 | The `id()` function will give us the location in memory where a thing 92 | (like a string or number) that a variable refers to is stored. If two 93 | variables return the same ID, they must be pointing to the same thing in 94 | memory. 95 | 96 | Let's try in the REPL: 97 | 98 | ``` {.py} 99 | >>> s = "Beej!" 100 | >>> t = s 101 | >>> id(s) 102 | 140156808252976 103 | >>> id(t) 104 | 140156808252976 105 | ``` 106 | 107 | The exact number doesn't matter (it will vary), but what matters is that 108 | they're identical. Both `s` and `t` refer to the entity in memory at 109 | that location, namely the string `"Beej!"`. 110 | 111 | You could compare those numbers to determine if both variables point to 112 | the same thing: 113 | 114 | ``` {.py} 115 | >>> id(s) == id(t) 116 | True 117 | ``` 118 | 119 | but there's a shorthand for that with the `is` operator: 120 | 121 | ``` {.py} 122 | >>> s is t 123 | True 124 | ``` 125 | 126 | Note that it's typically only want you assign from one variable to 127 | another that they refer to the same thing. If you assign to them 128 | independently, they typically won't: 129 | 130 | ``` {.py} 131 | >>> s = "Beej!" 132 | >>> t = "Beej!" 133 | >>> s is t 134 | False 135 | ``` 136 | 137 | In the above example, there are two strings in memory with value 138 | `"Beej!"`. 139 | 140 | I recognize that I said "typically" a bunch up there, and that should 141 | rightfully raise a bunch of "Beej is hand-waving" red flags. 142 | 143 | The actual details get a bit more gritty, but if you want to stop with 144 | what we've said up there, you're good. 145 | 146 | "No, keep going down the rabbit hole!" 147 | 148 | Okay then! 149 | 150 | ## Python Compiler Optimizations 151 | 152 | If you take this example from the REPL, above: 153 | 154 | ``` {.py} 155 | >>> s = "Beej!" 156 | >>> t = "Beej!" 157 | >>> s is t 158 | False 159 | ``` 160 | 161 | and you put it in a python program, like `test.py`: 162 | 163 | ``` {.py} 164 | s = "Beej!" 165 | t = "Beej!" 166 | print(s is t) 167 | ``` 168 | 169 | and run it from the command line, you'd think you'd get `False`, just 170 | like in the REPL. Wrong! 171 | 172 | ``` {.default} 173 | $ python test.py 174 | True 175 | ``` 176 | 177 | What gives? Why is it `False` in the first case and `True` in the 178 | second? Well, in the latter case, the Python interpreter is getting a 179 | little clever. Before it even runs your code, it analyzes it. It sees 180 | that you have two `"Beej!"` strings in there, so it just makes them the 181 | same one to save you memory. Since strings are immutable, you can't tell 182 | the difference. 183 | 184 | ## Internment 185 | 186 | In that same example, above: 187 | 188 | ``` {.py} 189 | >>> s = "Beej!" 190 | >>> t = "Beej!" 191 | >>> s is t 192 | False 193 | ``` 194 | 195 | what if we use a different string, like "Alice"? 196 | 197 | ``` {.py} 198 | >>> s = "Alice" 199 | >>> t = "Alice" 200 | >>> s is t 201 | True 202 | ``` 203 | 204 | `True`?? What's up with that? Why does Alice get special treatment? 205 | 206 | Or look at this: 207 | 208 | ``` {.py} 209 | >>> x = 257 210 | >>> y = 257 211 | >>> x is y 212 | False 213 | ``` 214 | 215 | which is fine---but then check this out, with 256 instead of 257: 216 | 217 | ``` {.py} 218 | >>> x = 256 219 | >>> y = 256 220 | >>> x is y 221 | True 222 | ``` 223 | 224 | `True`, again? 225 | 226 | We're getting into a deep language feature of Python called 227 | _internment_. Basically Python makes sure to only have one copy in 228 | memory of certain, specific values of data. 229 | 230 | For these values, all variables will refer to the same item in memory. 231 | 232 | They are: 233 | 234 | * Integers between -5 and 256 inclusive. 235 | * Strings that contain only letters, numbers, or underscores. 236 | * The `None` object. 237 | * The `True` and `False` objects. 238 | 239 | This is why `"Beej!"` isn't interned (because it contains punctuation), 240 | and why `"Alice"` _is_ interned. 241 | 242 | You can intern your own strings with `sys.intern()` for dictionary 243 | lookup optimization, but that's something 99.99999% of the Python 244 | programming populace will never bother doing. 245 | -------------------------------------------------------------------------------- /src/bgpython_part_A0400_bases.md: -------------------------------------------------------------------------------- 1 | # Appendix D: Number Bases 2 | 3 | ## How to Count like a Boss 4 | 5 | You all have figured out by now how much I love numbers. So I'm going to 6 | keep talking about them! Yay! 7 | 8 | At some point, you might have heard that computers run on a bunch of 1s 9 | and 0s. That they're binary. But what does that mean? 10 | 11 | As humans, we're used to numbers. We use them all the time. 7 Dwarves. 12 | Speed limit 55. 99 bottles of [your favorite beverage] on the wall. 13 | 14 | We use the digits 0-9. And we use them repeatedly. 15 | 16 | Computers, though, only have two digits: 0 and 1. 17 | 18 | That seems kind of limiting. What happens if you want to go higher than 19 | 1, to say, 2? 20 | 21 | Fortunately, there is a workaround. When computers want to go higher 22 | than 1, they do the same thing we humans do when we want to go higher 23 | than 9: we all add another _place_. 24 | 25 | As a human counting apples on a table, you start with 0, 1, and keep 26 | going... 7, 8, 9... and then you're out of digits for the _ones place_, 27 | which represents the number of individual items (1s of items) we've seen 28 | so far. 29 | 30 | So you add another place, the _tens place_, which represents the number 31 | of 10s of items we've seen so far. 32 | 33 | If we count to 27 apples, a number made up of 2 and 7, we know we have 2 34 | 10s of apples, and 7 1s of apples. Let that sink in. Every value in the 35 | 10s place is worth 10 apples. And every value in the 1s place is worth 1 36 | apple. 37 | 38 | Therefore for 27 apples, we have 39 | 40 | $2\times10+7\times1=27$ 41 | 42 | apples. 43 | 44 | Let's do the same thing with $100. With that, I'm saying there's a 1 in 45 | the 100s place, a 0 in the 10s place, and a 0 in the 1s place. This 46 | means the value is 47 | 48 | $1\times100+0\times10+0\times1$ 49 | 50 | or $100. 51 | 52 | Computers go through this same process when they run out of digits, 53 | except they run out of digits a lot sooner, since they only have two of 54 | them. 55 | 56 | Let's count apples in binary. 57 | 58 | 0, 1... oh no! We're out of digits. We have to add another place. Except 59 | this time, in binary, it's not the 10s place... it's the 2s place. 60 | 61 | 0, 1, 10, 11... out of digits again! We have to add another place. This 62 | time it's the 4s place: 63 | 64 | 0, 1, 10, 11, 100. 65 | 66 | Let's look at that last number. In binary, that's saying we have 1 4, 0 67 | 2s, and 0 1s. For a total value of 4. For 5, we just have to put a 1 in 68 | the 1s place: 101. 69 | 70 | In fact, we can take any number and digest it that way. Take the human 71 | number 7. That's made up of one 4, one 2, and one 1. $4+2+1=7$. So in 72 | binary, we need a 1 in the 4s place, the 2s place, and the 1s place. 73 | Which looks like this in binary: 111. 74 | 75 | We've written the number 7 in two "languages". In human language, it 76 | looks like $7$. In computer language it looks like $111$. 77 | 78 | But, and this is important: _human 7 and computer 111 are the same 79 | number_. They're just written in a different language, of sorts. 80 | 81 | ## Number Bases 82 | 83 | These "number languages" actually have names for convenience. Human 84 | numbers are called Jeff, and computer numbers, Alice. 85 | 86 | I'm kidding. That's completely false---they're not named that. 87 | 88 | Human numbers are called _decimal_ numbers. 89 | 90 | Computer 1-and-0 numbers are called _binary_ numbers. 91 | 92 | We also refer to these numbers by something called their _base_. This 93 | basically means "the number of digits this number system has". 94 | 95 | In decimal, we have 10 digits: 0-9. So decimal is a _base 10_ numbering 96 | system. 97 | 98 | In binary, we have 2 digits: 0 and 1. So binary is a _base 2_ numbering 99 | system. 100 | 101 | As we saw in the previous section, 7 in decimal (base 10) is the same as 102 | 111 in binary (base 2). 103 | 104 | And we also saw that as we counted up in any numbering system, when we 105 | ran out of digits, we had to add another place to create higher-valued 106 | numbers. 107 | 108 | There are other number bases in use. In fact, for any number you can 109 | think of, you can use that as a base for a numbering system. Base 3. 110 | Base 3490. Base 27. Whatever you want. Of course, a base 3490 numbering 111 | system will have 3490 different digits, so I don't know what you're 112 | going to use for those... you'll have to come up with a lot of new 113 | shapes to draw numbers with. 114 | 115 | Base 8, AKA _octal_, isn't that common any longer, but used to be used 116 | by programmers consistently. Since it's base 8, it has the digits 0-7 117 | available to use. When you use them up, you have to add another place, 118 | the 8s place. 119 | 120 | Notice how when you add a second place when you're adding up, the value 121 | of that place is the base! 122 | 123 | When you run out of digits with decimal (base 10), you add the 10s 124 | place. 125 | 126 | When you run out of digits with binary (base 2), you add the 2s place. 127 | 128 | When you run out of digits with octal (base 8), you add the 8s place. 129 | 130 | More generally, the place value is the base to the power of how many 131 | digits you are from the right of the number, just counting up. 132 | 133 | With the decimal number 1234, we have 1 in the thousands place, 2 in the 134 | hundreds, 3 in the tens place, and 4 in the ones place. But the place 135 | values 1000, 100, 10, and 1 are all powers of 10: 136 | 137 | $10^0=1$ 138 | $10^1=10$ 139 | $10^2=100$ 140 | $10^3=1000$ 141 | 142 | And it's the same in binary. If we have the binary number 1011, that's 1 143 | in the 8s place, 0 in the 4s place, 1 in the 2s place, and 1 in the 1s 144 | place. 145 | 146 | $2^0=1$ 147 | $2^1=2$ 148 | $2^2=4$ 149 | $2^3=8$ 150 | 151 | So the base is also tied into the value that any place in a number 152 | represents. Which makes sense, since we have to add a new place when we 153 | run out of digits, and if we have digits 0-9, that next place must 154 | represent the number of 10s, because that's what comes after 9. 155 | 156 | ## Hexadecimal, Base 16 157 | 158 | Hexadecimal, or _hex_ for short, is a really common number base for 159 | programmers. And it's an interesting one to look at because, at base 16, 160 | it has more digits than base 10. With base 10, we have 0-9. But with 161 | base 16, we have 0 through... what? 162 | 163 | Since we need more symbols to represent digits in base 16 than then 164 | normal arabic numerals we use for 0-9, we've gone ahead and co-opted the 165 | first part of the latin alphabet. We have 10 digits in 0-9, and we 166 | grabbed 6 more "digits" with A-F to get up to 16 total digits. 167 | 168 | That means when counting apples on the table in hex, we count: 169 | 170 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F, 10, 11, 12 ... 171 | 172 | (Upper and lower case don't matter when writing numbers in hex.) 173 | 174 | Look what happened when we got to F and ran out of digits: we added a 175 | new place. What value does that place have? 176 | 177 | Since this is base 16, it has to be the 16s place. 178 | 179 | TODO 180 | 181 | ## Specifying the Number Base in Python 182 | 183 | When you write down a number in your code, you have to tell Python what 184 | the base is, or it'll assume you mean decimal (base 10). 185 | 186 | For example, if I write 187 | 188 | ``` {.py} 189 | x = 10101011 190 | ``` 191 | 192 | Is that binary? I mean, it looks like it! 193 | 194 | But it's not! Because we didn't indicate otherwise, Python assumes this 195 | is the decimal number ten million one hundred one thousand eleven. 196 | 197 | We have some options 198 | ## Writing Numbers Down 199 | 200 | Throughout all this, it's important to remember that number bases are 201 | just other languages for representing the same number. You might write 202 | number in code as 203 | 204 | 205 | 206 | -------------------------------------------------------------------------------- /src/bgpython_part_0100_intro.md: -------------------------------------------------------------------------------- 1 | # Intro 2 | 6 | 7 | _**This is an alpha-quality book. There are mistakes, oh yes. When you 8 | find them, please drop an issue in GitHub, or a pull request, or email 9 | me at [`beej@beej.us`](mailto:beej@beej.us). When the number of defects 10 | gets low enough, I'll offer a print version.**_ 11 | 12 | Hey, everyone! Have you been thinking about learning to program? Have 13 | you also been thinking of how to do it in the easy-to-approach Python 14 | programming language? 15 | 16 | Yes? Then this is the book for you. We're going to start with the 17 | absolute basics and build up from there, building up to being an 18 | intermediate developer and problem-solver! Python is the language we'll 19 | be using to make this happen. 20 | 21 | But by the end of the book, you will have developed programming 22 | techniques that _transcend_ languages. After picking up Python, maybe 23 | try another language like JavaScript, Go, or Rust. They all have their 24 | own features to explore and learn. 25 | 26 | ## Audience 27 | 28 | Beginning programmers. If you have limited experience or no experience, 29 | this book is targeted at you! 30 | 31 | Attitude Prerequisite: be inquisitive, curious, have an eye for puzzles 32 | and problem solving, and be willing to take on difficult challenges. 33 | 34 | Technical Prerequisite: be a computer user. You know what files are, how 35 | to move them and delete them, what subdirectories (folders) are, can 36 | install software, and how to type. 37 | 38 | Are you a seasoned developer looking to start with Python? I'm sorry but 39 | this is not likely to be the book you're looking for. It will progress 40 | too slowly for your tastes. Just jump straight into [fl[the official 41 | Python documentation|https://docs.python.org/3/]]. 42 | 43 | ## Platform and Tools 44 | 45 | I make an effort in this book to cover Mac, Windows, and Unix variants 46 | (via Arch Linux). 47 | 48 | We'll cover installing Python 3 and the Visual Studio Code editor. (Both 49 | are free.) If you already have a code editor you prefer using (Vim, 50 | Emacs, Sublime, Atom, PyCharm, etc.) feel free to use that. This book's 51 | 100% certified editor agnostic! 52 | 53 | 54 | ## Official Homepage and Books For Sale 55 | 56 | The official location of this document is: 57 | 58 | * [`http://beej.us/guide/bgpython/`](http://beej.us/guide/bgpython/) 59 | 60 | There you will also find example code. 61 | 62 | 70 | 71 | ## Email Policy 72 | 73 | I'm generally available to help out with [ix[email to Beej]] email 74 | questions so feel free to write in, but I can't guarantee a response. I 75 | lead a pretty busy life and there are times when I just can't answer a 76 | question you have. When that's the case, I usually just delete the 77 | message. It's nothing personal; I just won't ever have the time to give 78 | the detailed answer you require. 79 | 80 | As a rule, the more complex the question, the less likely I am to 81 | respond. If you can narrow down your question before mailing it and be 82 | sure to include any pertinent information (like platform, compiler, 83 | error messages you're getting, and anything else you think might help me 84 | troubleshoot), you're much more likely to get a response. For more 85 | pointers, read ESR's document, [fl[How To Ask Questions The Smart 86 | Way|http://www.catb.org/~esr/faqs/smart-questions.html]]. 87 | 88 | If you don't get a response, hack on it some more, try to find the 89 | answer, and if it's still elusive, then write me again with the 90 | information you've found, and hopefully, it will be enough for me to help 91 | out. 92 | 93 | Now that I've badgered you about how to write and not write me, I'd just 94 | like to let you know that I _fully_ appreciate all the praise the guide 95 | has received over the years. It's a real morale boost, and it gladdens 96 | me to hear that it is being used for good! `:-)` Thank you! 97 | 98 | 99 | ## Mirroring 100 | 101 | [ix[mirroring]] You are more than welcome to mirror this site, whether 102 | publicly or privately. If you publicly mirror the site and want me to 103 | link to it from the main page, drop me a line at 104 | [`beej@beej.us`](beej@beej.us). 105 | 106 | 107 | ## Note for Translators 108 | 109 | [ix[translations]] If you want to translate the guide into another 110 | language, write me at [`beej@beej.us`](beej@beej.us) and I'll link to 111 | your translation from the main page. Feel free to add your name and 112 | contact info to the translation. 113 | 114 | This source markdown document uses UTF-8 encoding. 115 | 116 | Please note the license restrictions in the [Copyright, Distribution, 117 | and Legal](#legal) section, below. 118 | 119 | If you want me to host the translation, just ask. I'll also link to it 120 | if you want to host it; either way is fine. 121 | 122 | 123 | ## Copyright, Distribution, and Legal {#legal} 124 | 125 | Beej's Guide to Python Programming is Copyright © 2019 Brian "Beej 126 | Jorgensen" Hall. 127 | 128 | With specific exceptions for source code and translations, below, this 129 | work is licensed under the Creative Commons Attribution- Noncommercial- 130 | No Derivative Works 3.0 License. To view a copy of this license, visit 131 | 132 | [`https://creativecommons.org/licenses/by-nc-nd/3.0/`](https://creativecommons.org/licenses/by-nc-nd/3.0/) 133 | 134 | or send a letter to Creative Commons, 171 Second Street, Suite 300, San 135 | Francisco, California, 94105, USA. 136 | 137 | One specific exception to the "No Derivative Works" portion of the 138 | license is as follows: this guide may be freely translated into any 139 | language, provided the translation is accurate, and the guide is 140 | reprinted in its entirety. The same license restrictions apply to the 141 | translation as to the original guide. The translation may also include 142 | the name and contact information for the translator. 143 | 144 | The source code presented in this document is hereby granted to the 145 | public domain, and is completely free of any license restriction. 146 | 147 | Educators are freely encouraged to recommend or supply copies of this 148 | guide to their students. 149 | 150 | Unless otherwise mutually agreed by the parties in writing, the author 151 | offers the work as-is and makes no representations or warranties of any 152 | kind concerning the work, express, implied, statutory or otherwise, 153 | including, without limitation, warranties of title, merchantability, 154 | fitness for a particular purpose, noninfringement, or the absence of 155 | latent or other defects, accuracy, or the presence of absence of errors, 156 | whether or not discoverable. 157 | 158 | Except to the extent required by applicable law, in no event will the 159 | author be liable to you on any legal theory for any special, incidental, 160 | consequential, punitive or exemplary damages arising out of the use of 161 | the work, even if the author has been advised of the possibility of such 162 | damages. 163 | 164 | Contact [`beej@beej.us`](mailto:beej@beej.us) for more information. 165 | 166 | 167 | ## Dedication 168 | 169 | Thanks to everyone who has helped in the past and future with me getting 170 | this guide written. And thank you to all the people who produce the Free 171 | software and packages that I use to make the Guide: GNU, Linux, 172 | Slackware, vim, Python, Inkscape, pandoc, many others. And finally a big 173 | thank-you to the literally thousands of you who have written in with 174 | suggestions for improvements and words of encouragement. 175 | 176 | I dedicate this guide to some of my biggest heroes and inspirators in the 177 | world of computers: Donald Knuth, Bruce Schneier, W. Richard Stevens, 178 | and The Woz, my Readership, and the entire Free and Open Source Software 179 | Community. 180 | 181 | 182 | ## Publishing Information 183 | 184 | This book is written in Markdown using the vim editor on an Arch Linux 185 | box loaded with GNU tools. The cover "art" and diagrams are produced 186 | with Inkscape. The Markdown is converted to HTML and LaTex/PDF by 187 | Python, Pandoc and XeLaTeX, using Liberation fonts. The toolchain is 188 | composed of 100% Free and Open Source Software. 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/bgpython_part_A0200_repl.md: -------------------------------------------------------------------------------- 1 | # Appendix B: The REPL {#repl} 2 | 3 | ## What is the REPL? 4 | 5 | The REPL, pronounced _REP-əl_, is short for the _Read-Evaluate-Print 6 | Loop_. 7 | 8 | Great. I mean, truly, that's awesome. 9 | 10 | But what does any of that mean?! 11 | 12 | Check this out. If you run `python` on the command line (or whatever 13 | your OS's variant is), you'll end up with a prompt that looks like this: 14 | 15 | ``` {.default} 16 | $ python 17 | Python 3.8.0 (default, Oct 23 2019, 18:51:26) 18 | [GCC 9.2.0] on linux 19 | Type "help", "copyright", "credits" or "license" for more information. 20 | >>> 21 | ``` 22 | 23 | And it just sits there, waiting for you to type something. 24 | 25 | Actually, it's _reading_. The _Read_ part of REPL. 26 | 27 | So type something and hit `RETURN`! 28 | 29 | ``` {.py} 30 | >>> print("Hello, world!") 31 | Hello, world! 32 | ``` 33 | 34 | What happened after you hit `RETURN` was that Python _Evaluated_ your 35 | expression, and the _Printed_ the result. And then it printed another 36 | `>>>` prompt, because it's doing this in a _Loop_! The REPL! 37 | 38 | This is a method you could use to quickly test out Python commands to 39 | see how they work. It's not commonly used for development, but it is 40 | there if you find it convenient. 41 | 42 | Any time you see the `>>>` prompt, it's waiting for another Python 43 | command. 44 | 45 | ``` {.py} 46 | >>> a = 20 47 | >>> b = 30 48 | >>> c = a + b 49 | >>> print(c) 50 | 50 51 | ``` 52 | 53 | What about multi-line commands? Let's try printing numbers in a loop: 54 | 55 | ``` {.py} 56 | >>> for i in range(5): 57 | ... 58 | ``` 59 | 60 | Wait! What's that `...` prompt? That means Python is waiting for more. 61 | The fact that the previous line ended in a `:` indicates that a block is 62 | to follow it. So Python is waiting for a properly-indented block. (Hit 63 | `RETURN` on a blank line to exit the block. If you get stuck in `...` 64 | mode, just keep hitting `RETURN` until you get back out to the `>>>` 65 | prompt.) 66 | 67 | ``` {.py} 68 | >>> for i in range(5): 69 | ... print(i) 70 | ... 71 | 0 72 | 1 73 | 2 74 | 3 75 | 4 76 | ``` 77 | 78 | ## Calculator 79 | 80 | You can use the Python REPL as a quick and dirty calculator. 81 | 82 | ``` {.py} 83 | >>> 50 + 20 * 10 84 | 250 85 | >>> import math 86 | >>> math.factorial(10) 87 | 3628800 88 | >>> math.sqrt(2) 89 | 1.4142135623730951 90 | ``` 91 | 92 | ## Getting Help 93 | 94 | Python has a powerful built-in help system. 95 | 96 | You can ask for help on any expression; help will be returned on the 97 | type of that expression. 98 | 99 | For example, if we ask for help on a variable of type string, we get 100 | help on what we can do with that variable: 101 | 102 | ``` {.py} 103 | >>> s = "Hello!" 104 | >>> help(s) 105 | ``` 106 | 107 | This will output all kinds of stuff. 108 | 109 | ``` {.default} 110 | Help on class str in module builtins: 111 | 112 | class str(object) 113 | | str(object='') -> str 114 | | str(bytes_or_buffer[, encoding[, errors]]) -> str 115 | | 116 | | Create a new string object from the given object. If encoding or 117 | | errors is specified, then the object must expose a data buffer 118 | | that will be decoded using the given encoding and error handler. 119 | ``` 120 | 121 | _Et cetera_. The `:` prompt at the bottom is the prompt for the _pager_. 122 | You can hit `RETURN` to go to the next line, or `SPACE` to go to the 123 | next page. Up and Down arrow keys also work. Type `q` to get out of the 124 | pager and return to normal. 125 | 126 | The first thing you might notice are a bunch of functions that have 127 | double underscores around them, like this: 128 | 129 | ``` {.default} 130 | | __add__(self, value, /) 131 | | Return self+value. 132 | ``` 133 | 134 | Those double underscores, AKA _dunderscores_ or just _dunders_, indicate 135 | that this is an internal or special functions. As a beginner, you can 136 | ignore them. As you get more advanced, you might want to see how to make 137 | use of them. 138 | 139 | So hit `SPACE` a bunch of times until you're past the dunders. After 140 | that, you start getting to the documentation for the more common 141 | functions, like this one: 142 | 143 | ``` {.default} 144 | | count(...) 145 | | S.count(sub[, start[, end]]) -> int 146 | | 147 | | Return the number of non-overlapping occurrences of substring sub in 148 | | string S[start:end]. Optional arguments start and end are 149 | | interpreted as in slice notation. 150 | ``` 151 | 152 | We see there's a description there of what the method does, as well as 153 | an important description of what each parameter to the function means. 154 | But let's look at this line in particular: 155 | 156 | ``` {.default} 157 | | S.count(sub[, start[, end]]) -> int 158 | ``` 159 | 160 | That's not Python. It's documentation, and it has its own way of being 161 | read. 162 | 163 | Generally: 164 | 165 | The `S.` refers to this string that we're operating on. For example, if 166 | I say: 167 | 168 | ``` {.py} 169 | "fee fie foe foo".count("fo") 170 | ``` 171 | 172 | the string `"fee fie foe foo"` is represented by `S` in the documentation. 173 | 174 | The stuff after the `->` indicates the type of the return value. This 175 | function returns an integer. 176 | 177 | Now, what about that `start` and `end` stuff in the middle with the 178 | square brackets? 179 | 180 | In documentation, square brackets indicate _optional_ parameters to the 181 | function. Notice in my example, above, I didn't pass `start` or `end`... 182 | they're in square brackets, so they're optional. 183 | 184 | Looking in more detail, you see the brackets are nested. This means you 185 | can make the call with a `sub` and a `start`... and _then_ the `end` 186 | becomes optional. There's no way to call it with just a `sub` and an 187 | `end`, but no `start`. The optional `end` is optional if-and-only-if the 188 | optional `start` had already been specified. 189 | 190 | Finally, you can ask for help on a specific function or method: 191 | 192 | ``` {.py} 193 | help("".count) # Get help specifically on string.count method 194 | ``` 195 | 196 | ## `dir()`---Quick and Dirty Help 197 | 198 | The `dir()` built-in function is like `help()`, except extraordinarily 199 | terse. It just returns a list of the methods on a particular thing. 200 | 201 | For instance, we can get that list of methods and data attached to a 202 | dictionary by asking for `dir({})` (or by passing any `dict` to 203 | `dir()`). 204 | 205 | ``` {.py} 206 | >>> dir({}) 207 | ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', 208 | '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', 209 | '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', 210 | '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', 211 | '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', 212 | '__setattr__', '__setitem__', '__sizeof__', '__str__', 213 | '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 214 | 'pop', 'popitem', 'setdefault', 'update', 'values'] 215 | ``` 216 | 217 | Not as good as `help()`, but it might get you the quick answer if you're 218 | like, "What do I call to get the values out of a dictionary, again? Oh, 219 | that's right. `values()`! Eureka!" 220 | 221 | One place this is also useful is if you're using a poorly-documented 222 | piece of software^[Which should serve as a not-so-gentle reminder that 223 | you should document your code.]. Asking for `dir()` on an object can 224 | give you insight on how to use it. 225 | 226 | Though if you do this, beware that programmers change their undocumented 227 | interfaces _all the time_ without telling people. There's a school of 228 | thought that says if something's undocumented, you shouldn't use it at 229 | all, lest it be silently changed or dropped some day in the distant 230 | future. And that school has a point. 231 | 232 | 233 | ## Getting out of the REPL 234 | 235 | Oh, sure, Beej, put this section at the end of the chapter. Well, I just 236 | wanted to make sure you read the middle bit. `:)` 237 | 238 | Since the REPL is reading from the keyboard as a "file", you should send 239 | an EOF ([flw[End Of File|End-of-file]]) character to indicate that it 240 | should be done. 241 | 242 | On Mac and Linux and BSD and Ultrix and MINIX and SunOS and IRIX and 243 | HP-UX and Solaris and GNU Hurd and Plan 9 From Bell Labs and any Unix 244 | variant, EOF is indicated from the keyboard with `CTRL-d`. 245 | 246 | On Windows and Windows and Windows and Windows and any Windows variant 247 | and MS-DOS, EOF is indicated from the keyboard with `CTRL-z`. Followed 248 | by `RETURN`. 249 | 250 | Additionally, on any system, you can type `exit()` at the `>>>` prompt 251 | and it'll quit out. 252 | -------------------------------------------------------------------------------- /website/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Beej's Guide to Python Programming 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |
20 |
Like the guide? Pitch in!
21 |
22 | 23 | 24 | 25 |
26 |
27 |
28 | 29 |

30 | 31 | 54 | 55 |

56 |

Beej's Guide to Python Programming—for Beginners!

57 | 58 | (
Click here for other guides!) 59 | 60 |

61 | Hello, one and all! This is my little how-to guide on 62 | Python Programming for Absolute Beginners! It's a complete 63 | work in progress! 64 | 65 |

66 | Seriously--it's not even remotely done. And the parts that 67 | are there are alpha quality. 68 | 69 |

70 | If you find errors (and you will), please mail beej@beej.us or 72 | file 74 | a bug report or make 76 | a pull request. (Please restrict PRs to typos and small 77 | changes.) 78 | 79 |

What's Here for Readers:

80 | 83 | 84 | 93 | 94 | HTML: 95 | 105 |

106 | 107 | PDF: 108 |

118 | 119 | Examples: 120 |

121 |

125 | 126 | 140 | 141 | 159 | 160 |

What's Here for Translators and Writers:

161 | 162 |

Clone the whole thing from GitHub and follow the 164 | README. 165 | 166 |


167 | Contact Beej: 168 |
169 |
170 |
171 | 172 | 173 | 174 | -------------------------------------------------------------------------------- /src/bgpython_part_0200_whatis.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # What is Programming, Anyway? 6 | 7 | ## Objectives 8 | 9 | * Be able to explain what a programmer does 10 | * Be able to explain what a program is 11 | * Be able to summarize the four big steps in solving problems 12 | 13 | ## More Terminology Than You Wanted 14 | 15 | Let's say you're entirely new to this stuff. You have a computer, you 16 | know how to use it, but you don't know how to make it do exactly what 17 | you want it to. 18 | 19 | It's the difference between a _user_ and a _programmer_, right? 20 | 21 | But what does it mean to _program_ a computer? 22 | 23 | In a nutshell, you have a goal (what you want to compute), and 24 | programming is the way you get there. 25 | 26 | A _program_ is a description of a series of steps to complete that 27 | computation, blah blah blah... 28 | 29 | Okay---instead think of a sequence of steps that you have to do to do 30 | _anything_. Like baking cookies for example. 31 | 32 | > _**Fun Computing Fact:** everyone likes yummy cookies._ 33 | 34 | You often have that sequence of steps, that recipe, written down on a 35 | piece of paper. By itself, it's definitely not cookies. But when you 36 | apply a number of kitchen implements and ingredients and an oven to it, 37 | pretty soon you have some cookies. 38 | 39 | Of course, in that case, _you_ have to do the work. With programming, the 40 | computer does the work for you. But you have to write down the recipe. 41 | 42 | The recipe _is_ the program. Programming is the act of writing down that 43 | recipe so the computer can execute it. And get those cookies baked. 44 | 45 | Mmmm. Cookies. 46 | 47 | Some programs are simple. "Add up these 12 numbers" is an example. It's 48 | a breeze for the computer to do that. 49 | 50 | But others are more complicated. They can extend into millions of lines 51 | long, or even tens of millions, written by large teams over many years. 52 | Think triple-A video game titles or the Linux operating system. 53 | 54 | When you're first starting, large programs like that seem impossible. 55 | But here's the secret: each of those large programs is made up of 56 | smaller, well-designed building blocks. And each of those building 57 | blocks is made up of smaller of the same. 58 | 59 | And when you start learning to be a programmer, you start with the 60 | smallest, most basic blocks, and you build up from there. 61 | 62 | And you keep building! Writing software is a lifelong learning process. 63 | There are _always_ new things to learn, new technologies, new languages, 64 | new techniques. It's a craft to be developed and perfected over a 65 | lifetime. Sure, at first you won't have that many tools in your toolkit. 66 | But every moment you spend working on software gives you more experience 67 | solving problems and gives you more methods to attack them. 68 | 69 | ## What's This Talk About Problem Solving? 70 | 71 | In other words, I know what I want to see... how do I get there with the 72 | tools I know? 73 | 74 | There's a [fl[great scene in the movie Apollo 75 | 13|https://www.youtube.com/watch?v=ry55--J4_VQ]] [ix[Apollo 13]] that I 76 | love. The CO~2~ scrubbers on the command module are spent, and the team 77 | wants to use the scrubbers from the lunar module to replace them. But 78 | the former are round, and the latter are square and won't fit. Of 79 | course, the spacecraft has limited resources to repair it---just 80 | miscellaneous stuff on board that was meant for the mission. 81 | 82 | On the ground, the team has all those items at hand, and they dump them 83 | on a table. A staff member holds up a square scrubber and a round 84 | scrubber and tells everyone, "We gotta find a way to make _this_ fit 85 | into the hole for _this_." He then gestures toward the table, adding, 86 | "Using nothing but _that_." 87 | 88 | And that's what programming is like, except, obviously and fortunately 89 | no lives are at stake. (Typically.) 90 | 91 | You have a limited set of programming techniques at your disposal, and 92 | you have the goal that you want to achieve. How can you get to that goal 93 | using only those tools? It's a puzzle! 94 | 95 | ## So How To Solve It? {#problem-solving} 96 | 97 | > _**Fun Programming Fact:** Most devs have no idea how to solve a 98 | > problem when they're first presented with it. They have to 99 | > systematically attack it._ 100 | 101 | Programmers fully intend to use a well-reasoned approach to solving 102 | arbitrary problems. In reality, they often run off in high spirits and 103 | dive right into coding before they've done some of the very important 104 | preliminary work, but since they love programming so much they don't 105 | seem to mind wasting time. 106 | 107 | (And it's not a waste of time because every second you're 108 | programming, you're learning!) 109 | 110 | But lots of _bosses_ do consider unplanned development to be a waste of 111 | time. Time is also known as "money" to the company that hired you. And 112 | the only thing more precious to a company than money is more money. 113 | 114 | Imagine that you said, "I want to build an airplane!" and ran off an 115 | bought a pile of tools and metal and rivets and levers and started 116 | bolting it together immediately. You might find after a while that, oh 117 | you should have figured out how big the engine was before you built the 118 | fuselage, but, hey, no problem, you have time. You can just rebuild the 119 | fuselage. So you do. But now the canopy doesn't fit. So you have to 120 | rebuild it again, and so on. 121 | 122 | You could have saved a lot of time by actually _planning_ what you were 123 | going to do before you started building. 124 | 125 | It's the same with software. 126 | 127 | > _**Fun Programming Proverb:** Hours of debugging can save you minutes 128 | > of planning._ 129 | 130 | There are several problem-solving frameworks out there. These are 131 | blueprints for how to approach a programming problem and solve it, even 132 | if you have no idea how to solve it when you first see it. 133 | 134 | One of my favorite problem-solving frameworks was popularized by 135 | mathematician George Pólya in 1945 in his book _How To Solve It_. It was 136 | originally written for solving math problems, but it's surprisingly 137 | effective at solving just about anything. The Four Main Steps are: 138 | 139 | 1. **Understanding the Problem**. Get clarity on all parts of the 140 | problem. Break it down into subproblems, and break those subproblems 141 | down. If you don't understand the problem, any solutions you come up 142 | with will be solving the wrong problem! You know you understand the 143 | problem when you can explain it to someone completely. 144 | 145 | 2. **Devising a Plan**. How are you going to attack this with the tools 146 | you have at your disposal and the techniques you know? You know 147 | you're done making a plan when you're able to easily convert your 148 | plan into code. 149 | 150 | Often when planning you realize there's something about the problem 151 | you don't fully understand. Just for a bit, pop back to Step 1 until 152 | it's clear, then come back to planning. 153 | 154 | 3. **Carrying out the plan** Convert your plan into code and get it 155 | working. 156 | 157 | Often in this phase, you find that there was either something you 158 | didn't understand or something the plan didn't account for. Drop 159 | back a step or two until it's resolved, then come back here. 160 | 161 | 4. **Looking Back**. Look back on the code you got working, and consider 162 | what went right and what went wrong. What would you do differently 163 | next time? What techniques did you learn while writing the code? Was 164 | there any place you could have structured things better, or anyplace 165 | you could have removed redundant code? 166 | 167 | What's neat about this is that developers apply the steps of problem-solving to the _entire program_, and they also apply it to the smaller 168 | problems _within the program_. A big computing problem is always 169 | composed of many subproblems! The problem-solving framework is 170 | used within the problem-solving framework! 171 | 172 | An example of a real-life problem might be "build a house". But that's 173 | made up of subproblems, like "build a foundation" and "frame the walls" 174 | and "add a roof". And those are made up of subproblems, like "grade the 175 | lot" and "pour concrete". 176 | 177 | In programming, we break down problems into smaller and smaller 178 | subproblems until we know how to solve them with the techniques we know. 179 | And if we don't know a technique to solve it, we go and learn one! 180 | 181 | Being a developer is the same as being a problem solver. The problems 182 | ain't easy, but that's why it pays the big bucks. 183 | 184 | So you should expect that any time you see a programming problem in this 185 | book, on a programming challenge website, at school, or work, that 186 | the answer will not be obvious. You're going to have to work hard and 187 | spend a lot of time to get through the first problem-solving steps 188 | before you'll even be ready to start coding. 189 | 190 | ## What is Python? 191 | 192 | Python is a _programming language_. This means it's vaguely readable by 193 | humans, and completely readable by a machine. (As long as you don't make 194 | the tiniest mistake!) This is a good combination, since people are bad 195 | at reading the actual `1011010100110` code that machines use. We use 196 | these _higher-level_ languages to help us get by. 197 | 198 | In this case, another piece of software called the Python _interpreter_ 199 | takes the code we write in the Python language and runs it, performing 200 | the operations we've requested. 201 | 202 | So before we begin, we need to install the Python interpreter. This is 203 | one of the most annoyingly painful parts of the whole book, but luckily 204 | it only has to be done once. 205 | 206 | Okay, gang! Let's get it... over with! 207 | 208 | ...I really need to work on the end of that inspirational speech. 209 | 210 | ## Summary 211 | 212 | * A programmer is a problem solver. They then write programs that 213 | implement a solution to that problem. 214 | * A program is a series of instructions that can be carried out by a 215 | computer to solve the problem. 216 | * The main problem-solving steps are: Understand the Problem, Devise a 217 | Plan, Carry out the Plan, Look Back. 218 | -------------------------------------------------------------------------------- /src/bgpython_part_A0500_shelltools.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Accelerating Beyond IDLE 6 | 7 | ## Objectives 8 | 9 | * Install a real IDE 10 | * Learn about the _command line_. 11 | * Get your terminal/shell up and running, and explain how it's used 12 | 13 | ## What with the What Now? 14 | 15 | We've been using IDLE to edit code, and that's fine to get started. But 16 | it's a little bit underpowered for getting to the next level when it 17 | comes to programming. 18 | 19 | In this section, we'll look at some tools that professional programmers 20 | use to get the job done. 21 | 22 | ## The Terminal 23 | 24 | Back in the day, people accessed mainframes through dedicated pieces of 25 | hardware called _terminals_. These looked like old TV sets with 26 | keyboards attached. Screens were typically monochrome, either white, 27 | green, or amber colored, with pixely text. 28 | 29 | Barely anyone has a real terminal anymore, but they have programs called 30 | _terminal emulators_ that pretend to be terminals. In fact, real 31 | terminals are so rare, and terminal emulators are so common, if you hear 32 | someone say "terminal", they're certainly talking about a terminal 33 | emulator. 34 | 35 | So a terminal emulator is a program you run that gives you a text window 36 | to interact with your computer. 37 | 38 | You know how you point, click, and drag with the mouse to command your 39 | computer to do things? We're going to do the same things, except we're 40 | going type commands in to get them done, instead. 41 | 42 | ### The Shell 43 | 44 | Sometimes you'll hear people talk about the "shell" and "terminal" 45 | interchangeably. 46 | 47 | Technically, though, the terminal is what you launch, and then the 48 | terminal immediately launches another program, the _shell_, that you 49 | interact with. 50 | 51 | > The terminal is pretty dumb on its own. All it does it handle input 52 | > from the keyboard and output to its "screen". It's the middleperson 53 | > between you and the shell. The shell is the real brains of the outfit. 54 | 55 | The shell is a program that takes your typed-in commands and executes 56 | them, and tells you if something went wrong. 57 | 58 | The most popular shell program on Unix-likes and Macs is the _Bourne 59 | Again Shell_ (Bash)^[This is a bit of a pun around the original _Bourne 60 | Shell_ from back in the day. Bash improves on it a bit.], although the 61 | _Z-shell_ (Zsh) is growing in popularity. Bash is known by a `$` prompt 62 | (sometimes prefixed with other information about which directory you're 63 | in, or something similar). Zsh uses a `%` prompt. 64 | 65 | There are multitudes of shells, but we'll just assuming you're going to 66 | use Bash or Zsh (with a hat-tip to Windows's built-in shells), and 67 | they're compatible enough for our purposes. 68 | 69 | ### Windows Terminals and Shells 70 | 71 | For Windows, there are plenty of options, some of which you have 72 | installed already. 73 | 74 | * **CMD**: classic shell with origins way back in the MS-DOS days. 75 | * **PowerShell**: a new, more powerful shell. 76 | * **Bash via Git**: the famous [fl[Git|https://git-scm.com/]] software 77 | package has a bash shell called, appropriately, "gitbash". 78 | * **Bash via WSL**: if you install WSL (below), it uses bash, as well. 79 | 80 | Unless you're going with one of the bash options, you should use 81 | PowerShell because it's newer, better, and maintained. 82 | 83 | Almost all of the bash commands we use in this guide also work in 84 | PowerShell and CMD. 85 | 86 | Hitting the Windows key and running `cmd` will bring up the CMD prompt. 87 | (Type `exit` to get out.) 88 | 89 | Hitting the Windows key and running `PowerShell` will bring up the 90 | PowerShell prompt. (Type `exit` to get out.) 91 | 92 | ### Windows gitbash 93 | 94 | [fl[Git|https://git-scm.com/downloads]] is a _source code control 95 | system_. And it's great. Tons of people use it. You should install it. 96 | 97 | When you install it, it installs a bash shell called gitbash that you 98 | can use. 99 | 100 | ### Windows WSL 101 | 102 | The Windows Subsystem for Linux is an awesome piece of kit. It 103 | unobtrusively puts a little Linux install on your Windows machine. And 104 | then you can use the Linux command line. 105 | 106 | To install [fl[Follow the WSL setup 107 | instructions|https://learn.microsoft.com/en-us/windows/wsl/install]] 108 | 109 | There's a recommendation in there to also [install Windows 110 | Terminal](https://learn.microsoft.com/en-us/windows/terminal/install), 111 | an alternate terminal to the existing ones. It's a good choice. 112 | 113 | After installing, update the system: 114 | 115 | ``` {.default} 116 | sudo apt update 117 | sudo apt -y upgrade 118 | ``` 119 | 120 | and Python should be there; running this should get you the version 121 | number: 122 | 123 | ``` {.default} 124 | python3 --version 125 | ``` 126 | 127 | You can open a File Explorer window with: 128 | 129 | ``` {.default} 130 | iexplore.exe . 131 | ``` 132 | 133 | (Yes, that's a period after a space at the end.) 134 | 135 | ### Mac 136 | 137 | Macs come with a terminal built-in. Run the `Terminal` app and you'll be 138 | presented with a bash shell prompt. 139 | 140 | ### Linux/Unix-likes 141 | 142 | All Unix-likes come with a variety of terminals and shells. Google for 143 | your distribution. 144 | 145 | ## Installing an IDE 146 | 147 | There are a lot of IDEs out there, but a good free one is VS Code. 148 | 149 | Visit the [fl[Visual Studio Code|https://code.visualstudio.com/]] 150 | website for downloads. 151 | 152 | Let's get it installed! 153 | 154 | ### Windows VS Code 155 | 156 | When you install: 157 | 158 | * Make sure "Add Path" is checked 159 | * Check "Register Code as an editor for supported file types" 160 | * Recommended: Check "Add 'Open with Code' option" 161 | 162 | If you're using WSL, first run VS Code from _outside_ WSL, and install 163 | the "Remote WSL" extension. Then you should be able to run it from 164 | inside WSL. 165 | 166 | ### Mac 167 | 168 | Just install it. No special instructions. 169 | 170 | ### Linux and other Unix-likes 171 | 172 | Linux and Unix-like users can use their package manager to install VS 173 | Code. Google for `ubuntu vscode install`, substituting the name of your 174 | distro for `ubuntu`. 175 | 176 | If you already have a code editor you prefer using (Vim, Emacs, Sublime, 177 | Atom, PyCharm, etc.) feel free to use that, no problem! 178 | 179 | ## Running VS Code 180 | 181 | In the shell of your choice, if all has gone well, you should be able to 182 | type: 183 | 184 | ``` {.default} 185 | code 186 | ``` 187 | 188 | and have it launch VS Code. 189 | 190 | Once launched, click the icon on the left bar of VS Code to manage 191 | extensions. 192 | 193 | * Install Python extension 194 | * Install Pylint extension 195 | 196 | ## Using a Barebones Editor and Terminal 197 | 198 | Not all development environments are integrated. Some programmers use 199 | standalone editors and debuggers to get the work done. 200 | 201 | A typical cycle is: 202 | 203 | 1. Edit code in your editor 204 | 2. Run the code from the command line 205 | 3. Check the output, prepare to debug 206 | 4. Repeat! 207 | 208 | ### Start with the Terminal 209 | 210 | We're going to do this old-school. Programmers have been using the 211 | command line for over 10,000 years, and it has staying power for a 212 | reason. 213 | 214 | Launch a terminal and bring up a shell. You can use another shell if you 215 | want, but I'll be talking bash/zsh here. 216 | 217 | At the prompt, type the following commands, one per line: 218 | 219 | ``` {.default} 220 | cd 221 | mkdir bgpython 222 | cd bgpython 223 | ls -la 224 | ``` 225 | 226 | These commands do four amazing things: 227 | 228 | 1. `cd` means _change directory_. (A directory is the same as a folder.) 229 | On a line by itself, it means "change to my _home directory_". 230 | 231 | 2. `mkdir` means _make directory_. We're creating a new folder called 232 | `bgpython`. 233 | 234 | 3. `cd bgpython` to change into that directory. 235 | 236 | 4. `ls -la` to get a long directory listing (i.e. all the files in that 237 | folder.) 238 | 239 | At this point you should see something like this: 240 | 241 | ``` {.default} 242 | $ ls -la 243 | total 0 244 | drwxr-xr-x 2 beej staff 64 Nov 18 23:14 . 245 | drwxr-xr-x+ 123 beej staff 3936 Nov 18 23:14 .. 246 | ``` 247 | 248 | This is showing you all the files you have. Namely, there are two of 249 | them: `.` and `..`. These mean "this directory" and "parent directory", 250 | respectively. (You know how folders can be inside other folders? The 251 | outer folder is called the "parent" folder, which is what the parent 252 | directory is. If you want to get back to your home directory from here, 253 | you can type `cd ..`.) 254 | 255 | > _You should think of the shell as "being" in a certain directory at 256 | > any particular time. You can `cd` into directories, or `cd ..` back 257 | > into the parent, or `cd` to get to your home directory from anywhere. 258 | > It's like the folder you have open that has focus in a GUI._ 259 | 260 | (The remaining information on each line tells you the permissions on the 261 | file, who owns it, how big it is, when it was modified, and so on. We 262 | can worry about that later.) 263 | 264 | Other than those there are no other files. We'll soon fix that! Let's 265 | add a Python program and run it! 266 | 267 | ## Launching Your Code Editor 268 | 269 | Usually launching an editor to edit a file is as simple as typing the 270 | editor name directly followed by the filename on the command line. 271 | 272 | For example, to launch VS Code to edit the file `hello.py`: 273 | 274 | ``` {.default} 275 | code hello.py 276 | ``` 277 | 278 | But wait--isn't VS Code a full-fledged IDE? Yes, it is. Another popular 279 | editor is Vim: 280 | 281 | ``` {.default} 282 | vim hello.py 283 | ``` 284 | 285 | But in any case, you're in the editor and ready to type code. 286 | 287 | This is your canvas! This is where the magic happens! 288 | 289 | > If you get in Vim and have no idea how to get out, hit the `ESC` key 290 | > and then type `:q!` and hit `RETURN`---this will exit without saving. 291 | > If you want to save and exit, hit `ESC` then type `ZZ` in caps. 292 | > 293 | > Vim is a complex editor that is hard to learn. But after you learn it, 294 | > I maintain it's the fastest editor on the planet. I'm using it to type 295 | > this very sentence right now. 296 | > 297 | > To learn it, I recommend OpenVim's [interactive Vim 298 | > tutorial](https://www.openvim.com/) and this reference of [Vim 299 | > commands from beginner to 300 | > expert](https://thevaluable.dev/vim-commands-beginner/). 301 | 302 | Type [flx[the following|hello.py]] into your editor (the line numbers, 303 | below, are for reference only and shouldn't be typed in): 304 | 305 | ``` {.py .numberLines} 306 | print("Hello, world!") 307 | print("My name's Beej and this is (possibly) my first program!") 308 | ``` 309 | 310 | Pull down `File`→`Save` to save the file. 311 | 312 | 313 | ## Running the Program! 314 | 315 | Pop back into your terminal window and type `ls -la` to get a directory 316 | listing: 317 | 318 | ``` {.default} 319 | $ ls -la 320 | total 8 321 | drwxr-xr-x 3 beej staff 96 Nov 18 23:27 . 322 | drwxr-xr-x+ 123 beej staff 3936 Nov 18 23:14 .. 323 | -rw-r--r-- 1 beej staff 87 Nov 18 23:27 hello.py 324 | ``` 325 | 326 | There it is! `hello.py` clocking in at 87 bytes (characters, roughly) in 327 | size. 328 | 329 | Let's run this puppy. Remember how the program is just a recipe for 330 | doing a thing---what do you think our `hello.py` program does? Guess! 331 | 332 | Then type this to run it (if `python` doesn't work, try `python3` or 333 | `py` depending on your system): 334 | 335 | ``` {.default} 336 | python hello.py 337 | ``` 338 | 339 | and hit `RETURN`! _[Angelic Chorus!]_ 340 | 341 | ``` {.default} 342 | $ python hello.py 343 | Hello, world! 344 | My name's Beej and this is (possibly) my first program! 345 | ``` 346 | 347 | You just wrote some instructions and the computer carried it out! 348 | 349 | Next up: write a Quake III clone! 350 | 351 | Okay, so maybe there might be a small number of _in between_ things that 352 | I skimmed over, but, as Obi-Wan Kenobi once said, "You've taken your 353 | first step into a larger world." 354 | 355 | ## Exercises 356 | 357 | Remember to use the [four problem-solving steps](#problem-solving) to 358 | solve these problems: understand the problem, devise a plan, carry it 359 | out, look back to see what you could have done better. 360 | 361 | 1. Make another program called `dijkstra.py` that prints out your three 362 | favorite [fl[Edsger Dijkstra 363 | quotes|https://en.wikiquote.org/wiki/Edsger_W._Dijkstra]]. 364 | 365 | ## Summary 366 | 367 | * Move around the directory hierarchy using the shell 368 | * Edit some source code in an editor 369 | * Run that program from the command line 370 | 371 | -------------------------------------------------------------------------------- /src/bgpython_part_A0100_math.md: -------------------------------------------------------------------------------- 1 | # Appendix A: Basic Math for Programmers 2 | 3 | I know what you're thinking: _screw this_. 4 | 5 | Or maybe your language is even more blue. 6 | 7 | But bear with me for just a moment. Knowing a few basic mathematical 8 | operations can really help you be a better developer, as well as help 9 | you understand code that you come across. 10 | 11 | This section isn't so much about application of these functions, but is 12 | more about their definition. It'll be up to you to use them as you see 13 | fit. 14 | 15 | It's a short one---just plow through it. 16 | 17 | **Fun Math Fact**: Math is cool. 18 | 19 | ## Arithmetic 20 | 21 | Addition, subtraction, multiplication, and---don't fall asleep on me 22 | already---division. The _Big Four_ of grade school math. 23 | 24 | And, as a quick terminology summary: 25 | 26 | * The addition of two numbers produces a _sum_. 27 | * The subtraction of two numbers produces a _difference_. 28 | * The multiplication of two numbers produces a _product_. 29 | * The division of two numbers produces a _quotient_ (and potentially a 30 | _remainder_.) 31 | 32 | I'm not going to talk about what the operators do, and will presume you 33 | remember that from however-many years ago. 34 | 35 | But I do want to talk about which comes first, because you might have 36 | forgotten. 37 | 38 | What do I mean by that? 39 | 40 | Take this expression: $1+2\times3$ 41 | 42 | If we first add $1+2$, we get $3$. And then we multiply it by $3$ and we 43 | get the answer of $9$. 44 | 45 | But wait! If we first multiply $2\times3$ and get $6$, then we add $1$ 46 | we get an answer of $7$! 47 | 48 | So... which is it? $9$ or $7$? 49 | 50 | What we need to know is the _order of operations_ or the _precedence_ of 51 | one operator over another. Which happens first, $+$ or $\times$? 52 | 53 | For arithmetic, here is the rule: do all the multiplications and 54 | divisions _before_ any additions and subtractions. 55 | 56 | So: $1+2\times3=7$ 57 | 58 | We'll revisit order of operations as we continue on. 59 | 60 | In Python, we can just use the operators `+`, `-`, `*`, and `/` for 61 | addition, subtraction, multiplication, and division, respectively. 62 | 63 | ## Division in Python 64 | 65 | Hey---didn't we just talk about this? 66 | 67 | Yes, kinda. There are a few _details_ that are of interest. 68 | 69 | Let's divide! 70 | 71 | ``` {.py} 72 | print(3 / 2) # 1.5, as expected 73 | ``` 74 | 75 | What we've done there is a floating point division. That is, it produces 76 | a floating point result with a decimal point. Indeed, even this does the 77 | same: 78 | 79 | ``` {.py} 80 | print(2 / 1) # 2.0 81 | ``` 82 | 83 | So that's the "normal" way of division, right? No big surprises there. 84 | 85 | But what if I told you [fl[_there is no 86 | spoon_|https://www.youtube.com/watch?v=XO0pcWxcROI]]? 87 | 88 | Okay, that's a bit much. Never mind that analogy. 89 | 90 | But there is another kind of division, one that produces an integer 91 | result only. _Integer division_ uses the `//` operator (two slashes 92 | instead of just one). It returns an integer result in all cases, even if 93 | normally there would be a fraction. 94 | 95 | It does this by simply dropping the part after the decimal point like a 96 | hot potato. 97 | 98 | ``` {.py} 99 | print(9 / 5) # 1.8 100 | print(9 // 5) # 1 101 | ``` 102 | 103 | When you want an integer quotient, this is the fast way to do it. 104 | 105 | ## Modulo, AKA Remainder 106 | 107 | So when you do an integer division, it just cuts off the fractional part 108 | after the decimal point. What happens to it? Where does it go? If a tree 109 | falls in the forest and no one is around to hear it...? 110 | 111 | Well, it's gone forever, but there's a way to see what it _would_ have 112 | been in the form of a _remainder_. 113 | 114 | The remainder tells us how much is "left over" after the integer 115 | division has taken place. 116 | 117 | In the following examples, we'll use $\div$ to represent integer 118 | division. 119 | 120 | For example, if we take 40 and divide it into 4 parts, $40\div4$, we get 121 | a result of $10$, exactly. Because it's exact, there are no leftovers. 122 | So the remainder is zero. 123 | 124 | But if we take 42 and divide it into 4 parts... well, $42\div4=10$ 125 | still. But it's not exact. If we do the inverse and multiply 126 | $10\times4$, we only get $40$, not $42$. There are $2$ left over. The 127 | remainder is $2$! 128 | 129 | We do this with the _modulo_ operator, or _mod_ for short. 130 | 131 | $42\div4=10$ 132 | 133 | $42\bmod4=2$ The remainder! 134 | 135 | And in Python, the `%` operator is the modulo operator. 136 | 137 | ``` {.py} 138 | print(42 // 4) # 10 139 | print(42 % 4) # 2 140 | ``` 141 | 142 | Mod has the neat property of rolling numbers over "odometer style" 143 | because the result of the mod can never be larger than the divisor. 144 | 145 | As an example: 146 | 147 | ``` {.py} 148 | for i in range(10): 149 | print(i % 4) # i modulo 4 150 | ``` 151 | 152 | outputs: 153 | 154 | ``` {.default} 155 | 0 156 | 1 157 | 2 158 | 3 159 | 0 160 | 1 161 | 2 162 | 3 163 | 0 164 | 1 165 | ``` 166 | 167 | ## Negative numbers 168 | 169 | I'm not going to talk much about these here, but negative numbers are 170 | numbers less than 0, and they're indicated by a minus sign in front of 171 | the number. $-7$ means "$7$ less than zero". 172 | 173 | And I _know_ that's a crazy concept. Like how can you have $-12$ apples 174 | on the table? It doesn't seem particularly rooted in reality. 175 | 176 | But a closer to home example might be me saying, "I owe you $-35!" which 177 | means, in effect, _you_ owe _me_ $35! 178 | 179 | Remember some simple rules: 180 | 181 | If you multiply a negative number by a negative number, the result is 182 | positive. 183 | 184 | $-3\times-7=21$ 185 | 186 | If you multiply a negative number by a positive number, the result is 187 | negative. 188 | 189 | $-3\times7=-21$ 190 | 191 | If you add a negative number to a positive number, it's just like 192 | subtracting the negative number from the positive number: 193 | 194 | $-7+100=93$ 195 | 196 | or, equivalently: 197 | 198 | $100+(-7)=100-7=93$ 199 | 200 | ## Absolute Value 201 | 202 | Think of this as how far from zero on the number line any number is. 203 | 204 | 10 is 10 away from zero. The absolute value of 10 is 10. 205 | 206 | 2.8 is 2.8 away from zero. The absolute value of 2.8 is 2.8. 207 | 208 | Well, that seems a bit bland. Why bother? 209 | 210 | Here's why: 211 | 212 | -17 is 17 away from zero. The absolute value of -17 is 17. 213 | 214 | The rules are: 215 | 216 | * The absolute value of a positive number is itself. 217 | * The absolute value of a negative number is its negation, the positive 218 | version of itself. 219 | 220 | In summary, if it's negative, the absolute value makes it positive. 221 | 222 | In mathematical notation, we represent the absolute value with vertical 223 | bars: 224 | 225 | $x = -17$ If $x$ is $-17$... 226 | 227 | $|x| = 17$ ...the absolute value of $x$ is $17$. 228 | 229 | $|12| = 12$ The absolute value of $12$ is $12$. 230 | 231 | $|-13.8| = 13.8$ The absolute value of $-13.8$ is $13.8$. 232 | 233 | In Python we compute absolute value with the `abs()` built-in function: 234 | 235 | ``` {.py} 236 | print(abs(-17)) # 17 237 | print(abs(28.2)) # 28.2 238 | ``` 239 | 240 | In terms of precedence, you can think of absolute value kind of like 241 | parentheses. Do the stuff inside the absolute value first, then take the 242 | absolute value of the result. 243 | 244 | ## The Power of Exponents 245 | 246 | Let's say you wanted to multiply the number $7$ by itself, say, 8 times. 247 | We could write this: 248 | 249 | $7\times7\times7\times7\times7\times7\times7\times7$ 250 | 251 | I _guess_ that's not entirely awful. 252 | 253 | So let's go all out. I want to multiply $7$ by itself $3490$ times. I'm 254 | going to head to the pub while you work on that. Let me know when you're 255 | done. 256 | 257 | Luckily for you, mathematicians are lazy. They hate writing more than 258 | they have to, so they invent new notations out of thin air to avoid the 259 | extra work. Just like how I keep packing the kitchen trash down so I 260 | don't have to run it outside. 261 | 262 | Actually, no, it's not really like that at all^[Apart from the lazy 263 | factor, that is.]. 264 | 265 | So what they invented is another way of saying "3490 7s multiplied by 266 | each other" that didn't involve an endless line of $7$s and $\times$s. 267 | And it looks like this: 268 | 269 | $7^{3490}$ (read "7 to the 3490th power") 270 | 271 | That means 3490 $7$s multiplied together. We call this _raising to a 272 | power_ or _exponentiation_. 273 | 274 | Or, put another way: 275 | 276 | $7^8=7\times7\times7\times7\times7\times7\times7\times7$ 277 | 278 | We have all kinds of fun facts! Ready? 279 | 280 | In the example $7^8$, the number $7$ is what we call the _base_ and the 281 | number $8$ we call the _exponent_. 282 | 283 | If you want to write that code in Python, you'd write: 284 | 285 | ``` {.py} 286 | print(7**8) # prints 5764801 287 | ``` 288 | 289 | The exponent must be non-negative, so zero or larger. 290 | 291 | But wait---zero? How do you multiply a number by itself zero times? It's 292 | a valid question, and one that has baffled philosophers for time 293 | immemorial. But not mathematicians! Like power-crazed numerical 294 | dictators, they cried, "We made this up, and we declare that _anything_ 295 | to the zeroth power is $1$. End of story!" 296 | 297 | So there we have it. $n^0=1$ for all $n$. 298 | 299 | But their unquenchable thirst for power didn't end there. Mathematicians 300 | also decided that there was such a thing as _negative exponents_. 301 | 302 | If you have a negative exponent, you need to invert the fraction before 303 | applying the exponent. The following is true: 304 | 305 | $4^{-3}=\left(\cfrac{1}{4}\right)^3$ 306 | 307 | $\left(\cfrac{3}{4}\right)^{-8}=\left(\cfrac{4}{3}\right)^8$ 308 | 309 | And in case you're wondering how to raise a fraction to an exponent, you 310 | just apply the exponent to the numerator and denominator: 311 | 312 | $\left(\cfrac{4}{3}\right)^8=\cfrac{4^8}{3^8}=\cfrac{65536}{6561}$ 313 | 314 | We also have some shorthand names for certain exponents. 315 | 316 | $n^2$ we say "$n$ squared". Anything raised to the power of $2$ is that 317 | number _squared_. 318 | 319 | $n^3$ we say "$n$ cubed". Anything raised to the third power is that 320 | number _cubed_. 321 | 322 | In casual writing, you'll often see the caret character `^` used to 323 | indicate an exponent. So if someone writes `14^4`, that's the same as 324 | $14^4$. 325 | 326 | Lastly, precedence. We know that multiplication and division happen 327 | before addition and subtraction, but what about exponentiation? 328 | 329 | Here's the rule: exponentiation happens before arithmetic. 330 | 331 | With $2+3^4$, we first compute $3^4=81$, _then_ add $2$ for a total of 332 | $83$. 333 | 334 | ## Parentheses 335 | 336 | So what if you _want_ to change the order of operations, like some kind 337 | of mathematical rebel? 338 | 339 | What if you have $1+2^3$ and you want $1+2$ to happen before the 340 | exponentiation? 341 | 342 | You can use parentheses to indicate that operation should occur first, 343 | like this: 344 | 345 | $(1+2)^3$ 346 | 347 | With that, we first compute $1+2=3$, and then we raise that to the 3rd 348 | power for a result of $27$. 349 | 350 | You could also do this: 351 | 352 | $2^{(3+4)}$ 353 | 354 | Remember: parentheses first! $3+4=7$, so we want to compute $2^7$ which 355 | is $128$. (Good software developers have all the powers of $2$ 356 | memorized up through $2^{16}$. Well, crazy ones do, anyway.) 357 | 358 | Python uses parentheses, as well. The above expression could be written 359 | in Python like this: 360 | 361 | ``` {.py} 362 | 2**(3+4) 363 | ``` 364 | 365 | Easy peasy. 366 | 367 | One final note: if an exponent is a long expression without parentheses, 368 | it turns out the parentheses are _implied_. The following equation is 369 | true: 370 | 371 | $2^{(3+4)}=2^{3+4}$ 372 | 373 | ## Square root 374 | 375 | Square root is a funny one. It's the opposite of _squaring_, which if 376 | you remember from the earlier section means _raising to a power of 2_. 377 | But, again, _square root_ is the opposite of that. 378 | 379 | It's asking the question, "For a given number, which number do I have to 380 | multiply by itself to get that number?" 381 | 382 | Let's examplify! 383 | 384 | But before we do, the weird lightning bolt line thing is the 385 | mathematical symbol for the square root of a number. 386 | 387 | Let's ask for the square root of 9: 388 | 389 | $\sqrt9=?$ 390 | 391 | That's asking, what number multiplied by itself makes $9$? Or, in other 392 | words, what number raised to the 2nd power makes $9$? Or, in other 393 | words, what number squared makes $9$? 394 | 395 | Hopefully by now you've determined that: 396 | 397 | $3\times3=9$ 398 | 399 | and, equivalently: 400 | 401 | $3^2=9$ 402 | 403 | so therefore: 404 | 405 | $\sqrt9=3$ 406 | 407 | What about some other ones? 408 | 409 | $\sqrt{16}=?$ 410 | $\sqrt{25}=?$ 411 | $\sqrt{36}=?$ 412 | $\sqrt{12180100}=?$ 413 | 414 | Note that all of those have integer answers. If the square root of 415 | number is an integer, we call that number a _perfect square_. 16, 25, 416 | 36... these are all perfect squares. 417 | 418 | But you can take the square root of any number. 419 | 420 | $\sqrt{17}=4.123105625617661$ approximately. 421 | 422 | 17 is _not_ a perfect square, since it doesn't have an integer result. 423 | 424 | Now, you might be imagining what would happen if you tried to take the 425 | square root of a negative number: 426 | 427 | $\sqrt{-100}=?$ 428 | 429 | After a bit of thought, you might realize that no number multiplied by 430 | itself can ever result in $-100$ so... what can you do? 431 | 432 | You might think, "We're doomed," but not mathematicians! They simply 433 | defined: 434 | 435 | $\sqrt{-1}=i$ 436 | 437 | and invented an entire mathematical system around what they called 438 | [flw[_imaginary numbers_|Imaginary_number]] that actually have some 439 | amazing applications. But that's something you can look up on your own. 440 | 441 | In addition to square roots, there are also cube roots. This is asking, 442 | "What number cubed results in this number?" This is indicated by a small 443 | $3$ above the root symbol: 444 | 445 | $\sqrt[3]{27}=x$ 446 | 447 | which is the inverse of the equation: 448 | 449 | $x^3=27$ 450 | 451 | Can you figure out what $x$ is? 452 | 453 | What about how to do this in Python? 454 | 455 | For square roots, the preferred way is to use the `sqrt()` function in 456 | the `math` module that you `import`: 457 | 458 | ``` {.py} 459 | import math 460 | 461 | print(math.sqrt(12180100)) # prints 3490.0 462 | ``` 463 | 464 | What about cube roots? Well, for that, we're going to jump back to 465 | exponents and learn a little secret. You can raise numbers to _fractional_ 466 | powers. Now, there are a lot of rules around this, but the short of it 467 | is that these equations are true: 468 | 469 | $\sqrt{x}=x^\frac{1}{2}$\ \ \ \ $\sqrt[3]{x}=x^\frac{1}{3}$\ \ \ \ $\sqrt[4]{x}=x^\frac{1}{4}$ 470 | 471 | and so on. Raising a number to the $\frac{1}{3}$ power is the same as 472 | taking the cube root! 473 | 474 | Like if we wanted to compute the cube root of $4913$, we could compute: 475 | 476 | $\sqrt[3]{4913}=4913^\frac{1}{3}=17$ 477 | 478 | And you can do that in Python with the regular exponent operator `**`: 479 | 480 | ``` {.py} 481 | print(4913**(1/3)) # prints 16.999999999999996 482 | ``` 483 | 484 | Hey---wait a second, that's not $17$! What gives? 485 | 486 | Well, turns out that floating point numbers in computers aren't exact. 487 | They're close, but not exact. It's something developers need to be aware 488 | of and either live with or work around. 489 | 490 | Lastly, because square root, cube root, and all the other roots are just 491 | exponents in disguise, they have the same precedence as exponents: they 492 | happen _before_ arithmetic. 493 | 494 | ## Factorial 495 | 496 | Factorial is a fun function. 497 | 498 | Basically if I ask for something like "5 factorial", that means that I 499 | want to know the answer when we multiply 5 by all integers less than 5, 500 | down to 1. 501 | 502 | So I'd want to know: 503 | 504 | $5\times4\times3\times2\times1$ 505 | 506 | the answer to which is $120$. 507 | 508 | But writing "factorial" is kind of clunky, so we have special notation 509 | for it: the exclamation point: $!$. "5 factorial" is written $5!$. 510 | 511 | Another example: 512 | 513 | $6!=6\times5\times4\times3\times2\times1=720$ 514 | 515 | As you can imagine, factorial grows very quickly. 516 | 517 | $40!=815915283247897734345611269596115894272000000000$ 518 | 519 | In Python, you can compute factorial by using the `factorial()` function 520 | in the `math` module. 521 | 522 | ``` {.py} 523 | import math 524 | 525 | print(math.factorial(10)) # prints 3628800 526 | ``` 527 | 528 | Factorial, being multiplication in disguise, has the same precedence as 529 | multiplication. You do it before addition and subtraction. 530 | 531 | ## Scientific notation 532 | 533 | For really large floating point numbers, you might see things like this 534 | appear: 535 | 536 | ``` {.py} 537 | print(2.7**100) # 1.3689147905858927e+43 538 | ``` 539 | 540 | What's that `e+43` at the end? 541 | 542 | This is what we call _scientific notation_. It's a shorthand way of 543 | writing really large (or small) numbers. 544 | 545 | The number above, `1.3689147905858927e+43`, is saying this, 546 | mathematically: 547 | 548 | $1.3689147905858927\times10^{43}$ 549 | 550 | Now, $10^{43}$ is a _big_ number. So multiplying $1.3689$ etc. by it 551 | results in a very large number, indeed. 552 | 553 | But that's not all. We can use it to represent very small numbers, too. 554 | 555 | ``` {.py} 556 | print(3.6/5000000000000000000) # 7.2e-19 557 | ``` 558 | 559 | `7.2e-19` means: 560 | 561 | $7.2\times10^{-19}$ 562 | 563 | And $10^{-19}$ is a very small number (very close to 0---remember that 564 | $10^{-19}=\frac{1}{10^{19}}$), so multiplying $7.2$ by it results in a 565 | very small number as well. 566 | 567 | Remember this: 568 | 569 | * If you see `e-something` at the end, it's a very small number (close 570 | to 0). 571 | * If you see `e+something` at the end, it's a very large number (far 572 | from 0). 573 | 574 | ## Logarithms 575 | 576 | Hang on, because things are about to get weird. 577 | 578 | Well, not _too_ weird, but pretty weird. 579 | 580 | Logarithms, or _logs_ for short, are kind of the opposite of exponents. 581 | 582 | But not opposite in the same way square roots are opposite. 583 | 584 | That's convenient, right? 585 | 586 | With logs, we say "log base x of y". For example, "log base 2 of 32", 587 | which is written like this: 588 | 589 | $\log_2{32}$ 590 | 591 | What that is asking is "2 to the what power is 32?" Or, in math: 592 | 593 | These are both true: 594 | 595 | $x=\log_2{32}$ 596 | 597 | $2^x=32$ 598 | 599 | So what's the answer? Some trial and error might lead you to realize 600 | that $2^5=32$, so therefore: 601 | 602 | $x=\log_2{32}=5$ 603 | 604 | There are three common bases that you see for logs, though the base can 605 | be any number: 2, 10, and $e$. 606 | 607 | Base 2 is super common in programming. In fact, it's so common that if 608 | you ever see someone write $\log{n}$ in a computing context, you should 609 | assume they mean $\log_2{n}$. 610 | 611 | $e$ is the base of the [flw[_natural logarithm_|Natural_logarithm]], 612 | common in math, and uncommon in computing. 613 | 614 | So what are they good for? 615 | 616 | A common place you see logarithms in programming is when using Big-O 617 | notation to indicate [flw[computational complexity|Big_O_notation]]. 618 | 619 | To compute the log of a number in Python, use the `log()` function in 620 | the `math` module. You specify the base as the second argument. (If 621 | unspecified, it computes the natural log, base $e$.) 622 | 623 | For example, to compute $\log_2{999}$: 624 | 625 | ``` {.py} 626 | import math 627 | 628 | print(math.log(999, 2)) # Prints 9.96434086779242 629 | 630 | # Because 2^9.96434086779242 == 999. Ish. 631 | ``` 632 | 633 | The big thing to remember there is that as a number gets large, the log 634 | of the number remains small. Here are some examples: 635 | 636 | |_x_|log~2~_x_| 637 | |-------:|-------------------------------:| 638 | |1|0.0000| 639 | |10|3.3219| 640 | |100|6.6439| 641 | |1000|9.9658| 642 | |10000|13.2877| 643 | |100000|16.6096| 644 | |1000000|19.9316| 645 | |10000000|23.2535| 646 | 647 | The log-base-2 of a big number tends to be a much smaller number. 648 | 649 | ## Rounding 650 | 651 | When we round a number, we are looking for the _nearest number_ of a 652 | certain number of decimal places, dropping all the decimal places 653 | smaller than that. 654 | 655 | By default, we mean zero decimal places, i.e. we want to round to the 656 | nearest whole number. 657 | 658 | So if I said, "Round 2.3 to the nearest whole number," you'd answer "2", 659 | because that's the closest whole number to 2.3. 660 | 661 | And if I said, "Round 2.8 to the nearest whole number," you'd answer 662 | "3", because that's the closest whole number to 2.8. 663 | 664 | When we round to a higher number, we call that _rounding up_. 665 | 666 | The other direction is _rounding down_. 667 | 668 | But what if I said "Round 2.5 to the nearest whole number?" It's 669 | perfectly between 2 and 3! In those cases, conventionally, lots of us 670 | learned to round up. So the answer would be 3. 671 | 672 | We can also force a number to round a certain direction by declaring 673 | which way to round. 674 | 675 | "Divide _x_ by 3, then round up." 676 | 677 | In Python, we have a few options for rounding. 678 | 679 | We can use the built-in `round()` function. 680 | 681 | But it behaves a little bit differently than we might be used to. 682 | Notably, numbers like 1.5, 2.5, and 3.5 that are equally close to two 683 | whole numbers _always round to the nearest even number_. This is 684 | commonly known as _round half to even_ or _banker's rounding_. 685 | 686 | ``` {.py} 687 | round(2.8) # 3 688 | round(-2.2) # -2 689 | round(-2.8) # -3 690 | 691 | round(1.5) # 2 692 | round(2.5) # 2 693 | round(3.5) # 4 694 | ``` 695 | 696 | You can also tell `round()` how many decimal places you want to round 697 | to: 698 | 699 | ``` {.py} 700 | round(3.1415926, 4) # 3.1416 701 | ``` 702 | 703 | Note that Python has [fl[additional weird rounding 704 | behavior|https://docs.python.org/3/tutorial/floatingpoint.html#tut-fp-issues]] 705 | due to the limited precision of floating point numbers. 706 | 707 | For always rounding up or down, use the functions `ceil()` and `floor()` 708 | from the `math` module. 709 | 710 | `ceil()` returns the next integer greater than this number, and 711 | `floor()` returns the previous integer smaller than this number. 712 | 713 | This makes perfect sense for positive numbers: 714 | 715 | ``` {.py} 716 | import math 717 | 718 | math.ceil(2.1) # 3 719 | math.ceil(2.9) # 3 720 | math.ceil(3.0) # 3 721 | math.ceil(3.1) # 4 722 | 723 | math.floor(2.1) # 2 724 | math.floor(2.9) # 2 725 | math.floor(3.0) # 3 726 | math.floor(3.1) # 3 727 | ``` 728 | 729 | But with negative numbers, it behaves differently than you might expect: 730 | 731 | ``` {.py} 732 | import math 733 | 734 | round(2.3) # 2 735 | math.floor(2.3) # 2 736 | 737 | round(-2.3) # -2 738 | math.floor(-2.3) # -3 (!!) 739 | 740 | round(2.8) # 3 741 | math.ceil(2.8) # 3 742 | 743 | round(-2.8) # -3 744 | math.ceil(-2.8) # -2 (!!) 745 | ``` 746 | 747 | While `round()` heads to the nearest integer, `floor()` goes to the 748 | _next smallest_ integer. With negative numbers, that's the next one 749 | farther away from zero. And the reverse is true for `ceil()`. 750 | 751 | If you want a round up function that works on positive and negative 752 | numbers, you could write a helper function like this: 753 | 754 | ``` {.py} 755 | import math 756 | 757 | def round_up(x): 758 | return math.ceil(x) if x >=0 else math.floor(x) 759 | 760 | round_up(2.0) # 2 761 | round_up(2.1) # 3 762 | round_up(2.8) # 3 763 | 764 | round_up(-2.0) # -2 765 | round_up(-2.1) # -3 766 | round_up(-2.8) # -3 767 | ``` 768 | 769 | I'll leave `round_down()` as an exercise. `:)` 770 | 771 | If you want to always round up to the next whole number instead of doing 772 | banker's rounding, you can use this trick: add 0.5 to the number and 773 | then convert it to an `int()`. (If the number is negative, subtract 0.5 774 | from it.) 775 | 776 | ``` {.py} 777 | int(0.5 + 0.5) # 1 778 | int(1.5 + 0.5) # 2 779 | int(2.5 + 0.5) # 3 780 | 781 | int(2.8 + 0.5) # 3 782 | int(2.2 + 0.5) # 2 783 | 784 | int(-2.2 - 0.5) # -2 785 | int(-2.8 - 0.5) # -3 786 | ``` 787 | 788 | ## Large Numbers 789 | 790 | Python is really good with large integer numbers. In fact, it has what 791 | we call _arbitrary precision integer math_. That means we can hold 792 | numbers as big as memory can handle, which is large. 793 | 794 | So if you ask Python to compute $100000!$, it will do it. The result, 795 | incidentally, is 465,575 digits long. No problem. 796 | 797 | But the same is _not_ true for floating point numbers (numbers with a 798 | decimal point). Python generally uses 64 bits to represent floating 799 | point numbers, which means there's a limit to the amount of 800 | precision---you can only have 16 digits in your number, though you can 801 | raise that number to huge positive or negative powers to get very large 802 | or very small numbers. 803 | 804 | Floating point is brilliant because it can represent really huge and 805 | really small numbers, but the number of digits you have at your 806 | disposal is limited. 807 | -------------------------------------------------------------------------------- /src/bgpython_part_0900_dicts.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Dictionaries 6 | 7 | ## Objective 8 | 9 | * Understand what dictionaries are 10 | * Initialize a dictionary 11 | * Access individual elements in a dictionary 12 | * Check to see if a key is in a dictionary 13 | * Iterate over dictionaries with `for` 14 | * Use common dictionaries built-in functions 15 | * Construct new dictionaries with dictionary comprehensions 16 | * Build Dictionaries of Dictionaries 17 | * Understand that Dictionaries are Mutable 18 | 19 | ## Chapter Project Specification {#dicts-proj-spec} 20 | 21 | Let's store some genealogical^[This is all about family trees. Did you 22 | know I'm related to Queen Elizabeth II (by marriage)? I'm her mother’s 23 | sister’s husband’s father’s father’s sister’s daughter’s husband’s 24 | wife’s (_drama!_) sister’s husband’s father’s brother’s son’s son’s 25 | daughter’s son’s daughter’s son. For realsies! I'm willing to bet that 26 | you're related to Queen Elizabeth II, as well. That makes us cousins!] 27 | data! 28 | 29 | We'll have several data records associated with each person: 30 | 31 | ``` {.default} 32 | Beej Jorgensen: 33 | Born: 1993 [yes, I'm 29, is my story I'm sticking to] 34 | Mother: Mom Jorgensen 35 | Father: Dad Jorgensen 36 | Siblings: [Brother Jorgensen, Sister Jorgensen, Little Sister Jorgensen] 37 | 38 | Mom Jorgensen: 39 | Born: 1970 40 | Mother: Grandma Jorgensen 41 | Father: Grandpa Jorgensen 42 | Siblings: [Auntie Jorgensen] 43 | 44 | Dad Jorgensen: 45 | Born: 1965 46 | Mother: Granny Jorgensen 47 | Father: Grandad Jorgensen 48 | Siblings: [Uncle Jorgensen] 49 | ``` 50 | 51 | (Ok, I hear you saying, "Wait, your mom and dad were both Jorgensens? 52 | That's suspicious. I mean, I'm not saying, but I'm just saying." Hold 53 | your tongue! I can assure you they're not related^[Except via Queen 54 | Elizabeth II, like the rest of us.].) 55 | 56 | We want to write an app that will allow you to print out the birthdays 57 | of the parents of any given person. 58 | 59 | Example: 60 | 61 | ``` {.default} 62 | Enter a name (or q to quit): Beej Jorgensen 63 | Parents: 64 | Mom Jorgensen (1970) 65 | Dad Jorgensen (1965) 66 | ``` 67 | 68 | So we'll need some way to store that, and some way to look through the 69 | data to get information about other people who are referenced. 70 | 71 | Yes, we could use lists of people and just search through, but there's a 72 | less clunky way we might go about doing this. 73 | 74 | This chapter is all about how we might store such data. 75 | 76 | 77 | ## What are Dictionaries? 78 | 79 | Problem-solving step: **Understanding the Problem**. 80 | 81 | Remember how with lists, we could key an index number into the list and 82 | get a value out, and how we could use that same index number to store 83 | something in that "slot"? 84 | 85 | Was really convenient for storing collections of information that you 86 | could index by number, right? 87 | 88 | Well, what if you wanted to refer to a slot with something that _wasn't_ 89 | a number? What if you wanted to use, say, a string, like so? 90 | 91 | ``` {.py} 92 | x = [1, 2, 3] 93 | 94 | x["beej"] = 3490 # Not going to work with a list 95 | ``` 96 | 97 | That makes Python unhappy because `"beej"` isn't an integer. And with 98 | lists, it wants integers. 99 | 100 | But we can get around that limitation with _dictionaries_, or _dicts_ 101 | for short. 102 | 103 | Declaring a dictionary is a little bit different, but here's a simple 104 | example to start: 105 | 106 | ``` {.py} 107 | d = {} # Squirrely braces, not square brackets! 108 | 109 | d["beej"] = 3490 110 | 111 | print(d["beej"]) # 3490 112 | ``` 113 | 114 | So very similar in usage, though the initial declaration of the variable 115 | is different than a list. 116 | 117 | With dicts, the value in the square brackets is called the _key_, and 118 | the value stored there is the _value_. 119 | 120 | ``` {.py} 121 | # key value 122 | # | | 123 | # --+-- ---+--- 124 | d["goats"] = "awesome" 125 | ``` 126 | 127 | You use the key to lookup a value in the dictionary, or to set a value 128 | in the dictionary. 129 | 130 | The key can be any immutable type (e.g. integers, floats, strings). The 131 | value can be any type. 132 | 133 | ## Initializing a Dictionary 134 | 135 | Problem-solving step: **Understanding the Problem**. 136 | 137 | As we saw above, you can initialize an empty dictionary like so: 138 | 139 | ``` {.py} 140 | d = {} 141 | ``` 142 | 143 | But you can also pre-initialize the dictionary with a number of keys and 144 | values: 145 | 146 | ``` {.py} 147 | d = { 148 | "name": "Beej", 149 | "age": 29, # ish 150 | "favorite OS": "windows", 151 | "no really, favorite OS": "linux" 152 | } 153 | ``` 154 | 155 | That's the equivalent of setting them by hand, albeit less verbose: 156 | 157 | ``` {.py} 158 | d = {} 159 | d["name"] = "Beej" 160 | d["age"] = 29 # ish 161 | # etc. 162 | ``` 163 | 164 | If you come from a web background, you might have come across 165 | [flw[JSON|JSON]]-format data. The Python dictionary is very similar in 166 | format, though not exactly the same. 167 | 168 | ## Speed Demon 169 | 170 | Problem-solving step: **Understanding the Problem**. 171 | 172 | Like lists, dicts are really fast at looking up information. In fact, on 173 | average, it takes the same amount of time to get a value out of a 174 | dictionary, _regardless of how many items are in the dictionary_^[Time 175 | complexity enthusiasts will recognize this as $O(1)$, or _constant_ 176 | time.]. 177 | 178 | Keep this fact in mind going forward. We'll get into more complex 179 | problems that can make use of this feature to keep your programs running 180 | quickly. _Vroom!_ 181 | 182 | ## Does this `dict` have this key? 183 | 184 | Problem-solving step: **Understanding the Problem**. 185 | 186 | If you have a dictionary, it's nice to be able to check to see if a key 187 | even exists. 188 | 189 | The secret to doing this is the `in` statement that will return a 190 | Boolean indicating if a key is in a dict. 191 | 192 | ``` {.py} 193 | d = { 194 | "a": 10, 195 | "b": 20 196 | } 197 | 198 | if "a" in d: 199 | print(f'key a\'s value is: {d["a"]}') 200 | 201 | if "x" not in d: 202 | print('There is no key "x" in "d"') 203 | ``` 204 | 205 | There is also a way to get an item out of a dictionary using the 206 | `.get()` method. This method returns a default value of `None`^[If you 207 | haven't seen it before or need a refresher, this is a value that 208 | represents "no value". It's a placeholder (what we call a _sentinel 209 | value_) to indicate a no-value condition.] if the key doesn't exist. 210 | 211 | ``` {.py} 212 | val = d.get("x") 213 | 214 | if val is None: # Note: use "is", not "==" with "None"! 215 | print("Key x does not exist") 216 | else: 217 | print(f"Value of key x is {d[x]}") 218 | ``` 219 | 220 | You can also detect a non-existent key with `try`/`catch`. But that's a story 221 | for another time. 222 | 223 | ## Iterating over Dictionaries 224 | 225 | Problem-solving step: **Understanding the Problem**. 226 | 227 | Remember how you could iterate over all the elements in a list with 228 | `for`? Well, we can do the same thing with dictionaries. Except, in this 229 | case, we'll be looping over the _keys_ in the dictionary, not the 230 | values. 231 | 232 | Let's try it! 233 | 234 | ``` {.py} 235 | d = { 236 | "c": 10, 237 | "b": 20, 238 | "a": 30 239 | } 240 | 241 | for k in d: 242 | print(f"key {k} has value {d[k]}") 243 | ``` 244 | 245 | This gives us the output: 246 | 247 | ``` {.default} 248 | key c has value 10 249 | key b has value 20 250 | key a has value 30 251 | ``` 252 | 253 | Note that in the latest version of Python, the keys come out in the same 254 | order they've been added to the dictionary. 255 | 256 | If you want them in another order, there are options: 257 | 258 | ``` {.py} 259 | d = { 260 | "c": 10, 261 | "b": 20, 262 | "a": 30 263 | } 264 | 265 | for k in sorted(d): # <--- Add the call to sorted() 266 | print(f"key {k} has value {d[k]}") 267 | ``` 268 | 269 | And now we have this, where the keys have been sorted alphabetically^[Or 270 | what we call _lexicographically sorted_. It's like alphabetical, but on 271 | steroids so that it can handle letters, numbers, punctuation and so on, 272 | all of which are all numbers deep down.]. 273 | 274 | ``` {.default} 275 | key a has value 30 276 | key b has value 20 277 | key c has value 10 278 | ``` 279 | 280 | Also, similar to how lists work, you can use the `.items()` method to 281 | get all keys and values out at the same time in a `for` loop, like this: 282 | 283 | ``` {.py} 284 | d = { 285 | "c": 10, 286 | "b": 20, 287 | "a": 30 288 | } 289 | 290 | for k, v in d.items(): 291 | print(f"key {k} has value {v}") 292 | ``` 293 | 294 | ## Common Built-in Dictionary Functionality 295 | 296 | Problem-solving step: **Understanding the Problem**. 297 | 298 | You have a number of tools in your toolkit for working with dicts in 299 | Python: 300 | 301 | |Method|Description| 302 | |----------|----------------------------------| 303 | |`.clear()`|Empty a dictionary, removing all keys/values| 304 | |`.copy()`|Return a copy of a dictionary| 305 | |`.get(key)`|Get a value from a dictionary, with a default if it doesn't exist| 306 | |`.items()`|Return a list-ish of the `(key,value)` pairs in the dictionary| 307 | |`.keys()`|Return a list-ish of the keys in the dictionary| 308 | |`.values()`|Return a list-ish of the values in the dictionary| 309 | |`.pop(key)`|Return the value for the given key, and remove it from the dictionary| 310 | |`.popitem()`|Pop and return the most-recently-added `(key,value)` pair| 311 | 312 | We already saw use of `.get()`, earlier, but it can also be modified 313 | to return a default value if the key doesn't exist in the dictionary. 314 | 315 | ``` {.py} 316 | d = { 317 | "c": 10, 318 | "b": 20, 319 | "a": 30 320 | } 321 | 322 | v = d.get("x", -99) # Return -99 if key doesn't exist 323 | 324 | print(v) # Prints -99, since key `x` doesn't exist 325 | ``` 326 | 327 | We've already seen a use of `.items()`, above. If you want to see just 328 | all the keys or values, you can get an iterable back with the `.keys()` 329 | and `.values()`: 330 | 331 | ``` {.py} 332 | d = { 333 | "c": 10, 334 | "b": 20, 335 | "a": 30 336 | } 337 | 338 | for k in d.keys(): 339 | print(k) # Prints "c", "b", "a" 340 | 341 | for v in d.values(): 342 | print(v) # Prints 10, 20, 30 343 | ``` 344 | 345 | ## Dictionary Comprehensions 346 | 347 | Problem-solving step: **Understanding the Problem**. 348 | 349 | Remember [list comprehensions](#list-comprehensions)? If you don't, pop 350 | over there for a quick refresher, because this is the same thing except 351 | for dictionaries. 352 | 353 | You can create a dictionary from any iterable that you can go over in a 354 | `for` loop. This is a pretty powerful way of creating a dictionary if 355 | you have the data in another, iterable, form, like a list or an input 356 | stream of something. 357 | 358 | Let's go through a list and store the list value as the key, and the 359 | list value times 10 as the value in the dictionary. And let's ignore the 360 | number 20 in the list, just for fun. 361 | 362 | ``` {.py} 363 | a = [10, 20, 30] 364 | d = { x: x*10 for x in a if x != 20 } 365 | 366 | print(d) # {10: 100, 30: 300} 367 | ``` 368 | 369 | If you have a list of key/value pairs, you can read those into a 370 | dictionary pretty easily, too. 371 | 372 | ``` {.py} 373 | a = [["alice", 20], ["beej", 30], ["chris", 40]] 374 | d = { k: v for k, v in a } 375 | 376 | print(d) # {'alice': 20, 'beej': 30, 'chris': 40} 377 | ``` 378 | 379 | ## Dictionaries of Dictionaries 380 | 381 | Problem-solving step: **Understanding the Problem**. 382 | 383 | Here's the deal: the value that you store for a given key can be 384 | _anything_! 385 | 386 | Well, not like a whale, or Mars, but any data type. So you can store 387 | strings, ints, floats, lists, or even other dictionaries as the value 388 | for the dictionary key! 389 | 390 | Check out this _nested declaration_, and check out how we drill down 391 | through the dictionary layers to get to the data: 392 | 393 | ``` {.py} 394 | d = { 395 | "a": { 396 | "b": "Mars! Ha!" 397 | } 398 | } 399 | 400 | print(d) # {'a': {'b': 'Mars! Ha!'}} 401 | print(d["a"]) # {'b': 'Mars! Ha!'} 402 | print(d["a"]["b"]) # Mars! Ha! 403 | ``` 404 | 405 | Nesting dictionaries like this can be a really powerful method of 406 | storing data. 407 | 408 | ## Dictionaries are Mutable 409 | 410 | Problem-solving step: **Understanding the Problem**. 411 | 412 | When you make an assignment copy of a dictionary, it's copying the 413 | _reference_ to the same dictionary, not making a new one. Just like with 414 | `lists` and anything else. 415 | 416 | And this is important, because dictionaries are mutable, just like lists. 417 | 418 | ``` {.py} 419 | d = { "beej": 3490 } 420 | 421 | e = d # both e and d refer to the same dict! 422 | 423 | e["beej"] = 3491 # modify it 424 | 425 | print(d["beej"]) # 3491 426 | ``` 427 | 428 | If you want to make a copy of a dictionary use one of the following: 429 | 430 | ``` {.py} 431 | d = { "beej": 3490 } 432 | 433 | e = d.copy() # make a copy, the preferred way 434 | e = dict(d) # make a copy 435 | ``` 436 | 437 | That first way is preferred because it's easier to read, and easy to 438 | read code is _Happy Code_™. 439 | 440 | ## The Chapter Project 441 | 442 | Pop back up top and [refresh on the spec](#dicts-proj-spec) if you need 443 | to. Let's break it down! 444 | 445 | Problem-solving step: **Understanding the Problem**. 446 | 447 | The goal of the project is to, for a given person, print out their 448 | parents' names as well as their year of birth. 449 | 450 | Pretty straightforward, but the devil's in the details! 451 | 452 | Problem-solving step: **Devising a Plan**. 453 | 454 | If we look at a sample record, we can see that a dict lends itself quite 455 | well to the data, with keys `born`, `mother`, `siblings`, etc. 456 | 457 | ``` {.default} 458 | Beej Jorgensen: 459 | Born: 1990 [yes, I'm 29, is my story I'm sticking to] 460 | Mother: Mom Jorgensen 461 | Father: Dad Jorgensen 462 | Siblings: [Brother Jorgensen, Sister Jorgensen, Little Sister Jorgensen] 463 | ``` 464 | 465 | Not only that, but we can store all of _those_ dicts in another 466 | container dict which uses the person's name as the key! 467 | 468 | ``` {.py} 469 | tree = { # Outer dict holds records for all the people 470 | "Beej Jorgensen": { # Inner dict holds details for each person 471 | "born": 1990, 472 | "mother": "Mom Jorgensen", 473 | "father": "Dad Jorgensen", 474 | "siblings": [ 475 | "Brother Jorgensen", 476 | "Sister Jorgensen", 477 | "Little Sister Jorgensen" 478 | ] 479 | } 480 | } 481 | ``` 482 | 483 | So that looks like a reasonable approach to storing data. We can just 484 | add the other people to the dictionary at that outermost layer. 485 | 486 | Not only that, but we now have part of the problem solved. If the user 487 | enters "Beej Jorgensen", all we have to do is look that directly up in 488 | the outer dict, and then we can print out my parents' names! 489 | 490 | Of course, we're still missing out on printing their birth years, but 491 | let's tackle the smaller problem first, and _then_ figure out how to 492 | extract that missing data. 493 | 494 | We'll come back to the "Understanding the Problem" step in a while to 495 | revisit that. 496 | 497 | Problem-solving step: **Carrying out the Plan** 498 | 499 | We'll start with a simple tree of just a single person. Let's keep it as 500 | simple as possible, and then go from there. 501 | 502 | ``` {.py .numberLines} 503 | tree = { 504 | "Beej Jorgensen": { 505 | "born": 1990, 506 | "mother": "Mom Jorgensen", 507 | "father": "Dad Jorgensen", 508 | "siblings": [ 509 | "Brother Jorgensen", 510 | "Sister Jorgensen", 511 | "Little Sister Jorgensen" 512 | ] 513 | } 514 | } 515 | 516 | ``` 517 | 518 | And now let's add some code to get the person's name, or quit if they 519 | enter "`q`": 520 | 521 | ``` {.py .numberLines startFrom=14} 522 | done = False 523 | 524 | while not done: 525 | name = input("Enter a name (or q to quit): ") 526 | 527 | if name == "q": 528 | done = True 529 | continue # Jump back to the top of the loop 530 | 531 | ``` 532 | 533 | And, finally, let's print out the person's name and their parents' names: 534 | 535 | ``` {.py .numberLines startFrom=23} 536 | record = tree[name] # Look up the record in the outer dict 537 | 538 | mother_name = record["mother"] # Extract parent names from inner dict 539 | father_name = record["father"] 540 | 541 | print("Parents:") 542 | print(f' {mother_name}') 543 | print(f' {father_name}') 544 | ``` 545 | 546 | Giving this a run, we get some good output! 547 | 548 | ``` {.default} 549 | $ python3 familytree.py 550 | Enter a name (or q to quit): Beej Jorgensen 551 | Parents: 552 | Mom Jorgensen 553 | Dad Jorgensen 554 | Enter a name (or q to quit): q 555 | ``` 556 | 557 | We're still missing the parents' birth years, but, as I said, we'll 558 | tackle that later. 559 | 560 | What happens if we run it with an unknown name? Remember that when 561 | you're testing your code, you should think like a villain and enter the 562 | most unexpected things you can. 563 | 564 | Let's try it with someone it doesn't know. 565 | 566 | ``` {.default} 567 | $ python3 familytree.py 568 | Enter a name (or q to quit): Arch Stanton 569 | Traceback (most recent call last): 570 | File "familytree.py", line 23, in 571 | record = tree[name] # Look up the record in the outer dict 572 | KeyError: 'Arch Stanton' 573 | ``` 574 | 575 | Well, that's ugly. It would be much nicer to print out some kind of 576 | error message. 577 | 578 | What does the spec say we should do? 579 | 580 | ...nothing! It says nothing about this case! That's not useful! The spec 581 | is missing information! 582 | 583 | True, it is. 584 | 585 | Turns out, this is a really common thing when programming. Your boss 586 | asks you to implement a thing but doesn't fully define what that thing 587 | is. It's not that your boss is bad at this; it's just that writing down 588 | the exact spec and not leaving anything out is _hard_. 589 | 590 | I promise you that if I asked you to write out the rules to 591 | Tic-Tac-Toe^[That's Noughts and Crosses, to some of you.], I'd find 592 | something you left out. ("You never said my 'X' could only take up one 593 | grid square!") 594 | 595 | The right thing to do at this point is to go back to the creator of the 596 | specification and ask exactly what should happen in this case. 597 | 598 | Problem-solving step: **Understanding the Problem** (again) 599 | 600 | "Hey, specification writer! What do we do if the person doesn't exist in 601 | the data?" 602 | 603 | Answer: print out an error message like this: 604 | 605 | ``` {.default} 606 | No record for "Arch Stanton" 607 | ``` 608 | 609 | All right! 610 | 611 | Problem-solving step: **Devising a Plan** (again) 612 | 613 | We're using this code to get a person's record: 614 | 615 | ``` {.py} 616 | record = tree[name] # Look up the record in the outer dict 617 | ``` 618 | 619 | but as we see, that throws an exception if `name` isn't a valid key in 620 | the dict. 621 | 622 | How can we handle that? There are a couple of ways. One of them involves 623 | _catching_ the exception, but we'll talk more about that in a later 624 | chapter. 625 | 626 | Something we can do that we discussed earlier in this chapter is to use 627 | the `.get()` method on the dict. This will return the record, or `None` 628 | if the key doesn't exist in the dict. Then we can test for that and 629 | print out some error messages. 630 | 631 | Problem-solving step: **Carrying out the Plan** (again) 632 | 633 | ``` {.py .numberLines startFrom=23} 634 | record = tree.get(name) # Look up the record in the outer dict 635 | 636 | if record is None: # Use "is" when comparing against "None" 637 | print(f'No record for "{name}"') 638 | continue 639 | 640 | mother_name = record["mother"] # Extract parent names from inner dict 641 | father_name = record["father"] 642 | 643 | print("Parents:") 644 | print(f' {mother_name}') 645 | print(f' {father_name}') 646 | ``` 647 | 648 | Now we're getting pretty close. But we still are missing one big piece: 649 | the birth years of the parents. 650 | 651 | Getting their names was cake: it was right there in the record for the 652 | person we're looking up. But their birth years aren't in there. 653 | 654 | How do we get them? 655 | 656 | Problem-solving step: **Devising a Plan** (again) 657 | 658 | We have the names of the parents. That's it. 659 | 660 | How do we go from a name to a birth year? 661 | 662 | Looks like "Beej Jorgensen" has a birth year listed in his record... 663 | 664 | We should add records for "Mom Jorgensen" and "Dad Jorgensen" and then 665 | they can have their own birth years. 666 | 667 | But the question still remains: how can we go from the user-entered 668 | "Beej Jorgensen" to the birth years for his parents? 669 | 670 | What we're doing here is trying to tie one piece of data ("Beej 671 | Jorgensen") to other pieces of data (1965 and 1970, his parents' birth 672 | years.) 673 | 674 | This is _super_ common in programming. "How do I get from x to y?" We 675 | need to find the path. 676 | 677 | So let's see... we have Beej Jorgensen there, with his parents' names 678 | listed. 679 | 680 | That's a start. But given his parents' names, how do you get his 681 | parents' birthdays? 682 | 683 | Yes! You just take their names and look them up in the dictionary! 684 | 685 | Except we haven't added them yet. Let's do that now. (Note that program 686 | line numbers, below, are reset at this point.) 687 | 688 | ``` {.py .numberLines} 689 | tree = { 690 | "Beej Jorgensen": { 691 | "born": 1990, 692 | "mother": "Mom Jorgensen", 693 | "father": "Dad Jorgensen", 694 | "siblings": [ 695 | "Brother Jorgensen", 696 | "Sister Jorgensen", 697 | "Little Sister Jorgensen" 698 | ] 699 | }, 700 | "Mom Jorgensen": { 701 | "born": 1970, 702 | "mother": "Grandma Jorgensen", 703 | "father": "Grandpa Jorgensen", 704 | "siblings": ["Auntie Jorgensen"] 705 | }, 706 | "Dad Jorgensen": { 707 | "born": 1965, 708 | "mother": "Granny Jorgensen", 709 | "father": "Grandad Jorgensen", 710 | "siblings": ["Uncle Jorgensen"] 711 | } 712 | } 713 | 714 | ``` 715 | 716 | And then the main loop logic (unchanged from before): 717 | 718 | ``` {.py .numberLines startFrom=26} 719 | done = False 720 | 721 | while not done: 722 | name = input("Enter a name (or q to quit): ") 723 | 724 | if name == "q": 725 | done = True 726 | continue # Jump back to the top of the loop 727 | 728 | record = tree.get(name) # Look up the record in the outer dict 729 | 730 | if record is None: # Use "is" when comparing against "None" 731 | print(f'No record for "{name}"') 732 | continue 733 | 734 | mother_name = record["mother"] # Extract parent names from inner dict 735 | father_name = record["father"] 736 | 737 | ``` 738 | 739 | Except now, when we print out the parents, we have to look up the 740 | mother's and father's record. 741 | 742 | ``` {.py .numberLines startFrom=44} 743 | # Get the parent records 744 | mother_record = tree.get(mother_name) 745 | father_record = tree.get(father_name) 746 | 747 | # Get the birth year of the mother; note if missing 748 | if mother_record is not None: 749 | mother_born_date = mother_record["born"] 750 | else: 751 | mother_born_date = "missing record" 752 | 753 | # Get the birth year of the father; note if missing 754 | if father_record is not None: 755 | father_born_date = father_record["born"] 756 | else: 757 | father_born_date = "missing record" 758 | 759 | print("Parents:") 760 | print(f' {mother_name} ({mother_born_date})') 761 | print(f' {father_name} ({father_born_date})') 762 | ``` 763 | 764 | That's it! 765 | 766 | Problem-solving step: **Looking Back** 767 | 768 | What could we do better? What are the shortcomings of this app? 769 | 770 | Look at the dictionary structure we used to store the data. How could 771 | that be better? Think of all the cases that exist in family trees. Sure, 772 | we covered the common case, but what about kids who were adopted? How do 773 | we model that? Divorces? Second marriages? It turns out that modeling a 774 | family tree is far more complex than you might originally anticipate. 775 | 776 | What if two people have the same name? In a real family tree, it's 777 | entirely likely there could be multiple Tom Jones^[It's not unusual.] in 778 | the family tree. But since we're using the name as the key in the dict, 779 | and keys have to be unique, we're in trouble. Ergo, the name can't be 780 | the key---something unique must be. 781 | 782 | One option there is to use a [flw[UUID|Universally_unique_identifier]] 783 | as the key, and map that UUID to names somehow. Maybe you have _another_ 784 | dict that, for a given name, stores a list of UUIDs that represent 785 | people who have that name. Then we could ask the user, "Did you mean the 786 | Beej Jorgensen who was born in 1971, 1982, 1997, or 2003?" if there were 787 | multiple Beej Jorgensens. (You can create a random UUID by importing the 788 | `uuid` package and running `uuid.uuid4()`.) 789 | 790 | Lots of options for improvement, here! 791 | 792 | ([flx[Solution|familytree.py]].) 793 | 794 | ## Exercises 795 | 796 | **Remember: to get your value out of this book, you have to do these 797 | exercises.** After 20 minutes of being stuck on a problem, you're 798 | allowed to look at the solution. 799 | 800 | Use any knowledge you have to solve these, not only what you learned in 801 | this chapter. 802 | 803 | **Always** use the [four problem-solving steps](#problem-solving) to 804 | solve these problems: understand the problem, devise a plan, carry it 805 | out, look back to see what you could have done better. 806 | 807 | 808 | 1. For the following dictionary, print out all the keys of the 809 | dictionary: 810 | 811 | ``` {.py} 812 | d = { 813 | "key1": "value1", 814 | "key2": "value1", 815 | "key3": "value1", 816 | "key4": "value1", 817 | } 818 | ``` 819 | 820 | ([flx[Solution|ex_printkeys.py]].) 821 | 822 | 2. For the dictionary in problem 1, print out all the keys and values. 823 | 824 | ([flx[Solution|ex_printkeysvals.py]].) 825 | 826 | 3. Given a list of names, write code that converts that list of names 827 | into a dictionary that groups names by their first letter. In the 828 | dict, the key should be the first letter of the names, and the value 829 | should be a list of names that start with that letter. 830 | 831 | For example, the list: 832 | 833 | ``` {.py} 834 | ["Chris", "Annie", "Beej", "Aaron", "Charlie"] 835 | ``` 836 | 837 | should build into the dictionary (key order doesn't matter): 838 | 839 | ``` {.py} 840 | { 841 | "C": ["Chris", "Charlie"], 842 | "A": ["Annie", "Aaron"], 843 | "B": ["Beej"] 844 | } 845 | ``` 846 | 847 | I don't want you to manually convert the list to a dictionary; I want 848 | you to write code that does it for _any_ list of names. 849 | 850 | After you construct the dictionary, print out the keys and values in 851 | any order. 852 | 853 | ([flx[Solution|ex_list2dict.py]].) 854 | 855 | 4. Change step 5 to print out the keys in sorted order. And the lists in 856 | sorted order after that. 857 | 858 | ([flx[Solution|ex_list2dictsort.py]].) 859 | 860 | ## Summary 861 | 862 | That was a heckuva chapter, wasn't it? 863 | 864 | When we learned about lists, we learned that you could index data by 865 | number. But now with dicts, we can index data by any constant data type 866 | at all, including numbers and strings. 867 | 868 | This gives us more flexibility in how we store data and how we look it 869 | up. 870 | 871 | We learned about accessing and setting elements in a dictionary, how to 872 | determine if a key is in a dictionary, and how to iterate over 873 | dictionaries with `for`. 874 | 875 | Not only that, but dicts have a bunch of built-in functionality you can 876 | reference to manipulate and access the data stored within them. 877 | 878 | Finally, we saw that since dictionary values can be any type of data, 879 | you can have dictionaries of dictionaries, even! The only limit is your 880 | imagination. 881 | 882 | What other kinds of data can you store in dictionaries? 883 | -------------------------------------------------------------------------------- /src/bgpython_part_0500_vars.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | # Data and Processing Data 6 | 7 | ## Objective 8 | 9 | * Understand what data is and how it is used 10 | * Understand what variables are and how they are used 11 | * Utilize variables to store information 12 | * Print the value of variables on the screen 13 | * Do basic math 14 | * Store input from the keyboard in variables 15 | * Learn about integer versus string data types 16 | * Convert between data types 17 | * Write a program that inputs two values and prints the sum 18 | 19 | **For this chapter, we want to write a program that reads two numbers 20 | from the keyboard and prints out the sum of the two numbers.** 21 | 22 | ## Data, Variables, and Math 23 | 24 | Problem-solving step: **Understanding the Problem**. 25 | 26 | _Data_ is the general term we use to describe information stored in the 27 | computer. In the case of programming, we're interested in values that we'll do 28 | things with. Like add together. Or turn into a video game. 29 | 30 | Really, data is information. Can you glean information from a symbol? 31 | Then it's data. Sometimes it's a symbol like "8", or maybe a string of 32 | symbols like "cat". 33 | 34 | Your goal as a software developer is to write programs that manipulate 35 | data. You have to manipulate the input data in such a way that the 36 | desired output is achieved. And you have to solve the problem of how to 37 | do that. 38 | 39 | In a program, data is commonly stored in what we call _variables_. If 40 | you've taken any algebra, you are familiar with a letter that holds the 41 | place of a value, such as the "slope-intercept" form of the equation of 42 | a line: 43 | 44 | > $y = mx + b$ 45 | 46 | or of a parabola: 47 | 48 | > $y = x^2$ 49 | 50 | But beware! In programming code, variables don't behave like 51 | mathematical equations. Similar, but different. 52 | 53 | Enter the following code in a new program in your editor, save it, and 54 | give it a run. (This is just like you did with the program in the 55 | earlier chapter. You can name this one anything you'd like. If you need 56 | inspiration, [flx[`vartest.py`|vartest.py]] seems good to me.) 57 | 58 | ``` {.py .numberLines} 59 | x = 34 # Variable x is assigned value 34 60 | print(x) 61 | x = 90 # Variable x is assigned value 90 62 | print(x) 63 | ``` 64 | 65 | In Python, _variables refer to values_^[More generally speaking, 66 | variables refer to objects, but since all we have for now is numeric 67 | values, let's just go with values.]. We're saying on line 1 of the code, 68 | above, "The variable `x` refers to the value `34`." Another way to think 69 | of this that might be more congruent with other languages is that `x` is 70 | a bucket that you can put a value in. 71 | 72 | Then Python moves to the next line of code and runs it, printing `34` to 73 | the screen. And then on line 3, we put something different in that 74 | bucket. We store `90` in `x`. The `34` is gone--this type of bucket only 75 | holds one thing^[Later we'll learn that other types of buckets can hold 76 | more than one thing.]. 77 | 78 | So the output will be: 79 | 80 | ``` {.default} 81 | 34 82 | 90 83 | ``` 84 | 85 | You can see how the variable `x` can be used and reused to store 86 | different values. 87 | 88 | > _We're using `x` and `y` for variable names, but they can be made up 89 | > of any letter or group of letters, or digits, and can also include 90 | > underscores (`_`). The only rule is they can't start with a digit!_ 91 | > 92 | > _These are all valid variable names (and, of course, you can make up 93 | > any name you want!):_ 94 | > 95 | > ``` {.py} 96 | > y 97 | > a1b2 98 | > foo 99 | > Bar 100 | > FOOBAZ12 101 | > Goats_Rock 102 | > ``` 103 | 104 | You can also do basic math on numeric variables. Add to the code above: 105 | 106 | ``` {.py .numberLines} 107 | x = 34 # Variable x is assigned value 34 108 | print(x) 109 | x = 90 # Variable x is assigned value 90 110 | print(x) 111 | 112 | y = x + 40 # y is assigned x + 40 which is 90 + 40, or 130 113 | print(x) # Still 90! Nothing changed here 114 | print(y) # Prints "130" 115 | ``` 116 | 117 | On line 6, we introduced a new variable, `y`, and gave it the value of 118 | "whatever `x`'s value is plus `40`". 119 | 120 | > _What are all these `#` marks in the file? We call those_ hash _marks, 121 | > and in the case of Python, they mean the rest of the line is a_ 122 | > comment _and will be ignored by the Python interpreter._ 123 | 124 | One last important point about variables: when you do an assignment like 125 | we did, above, on line 6: 126 | 127 | ``` {.py} 128 | y = x + 40 # y is assigned 130 129 | ``` 130 | 131 | When you do this, `y` refers to the value `130` even if `x` changes 132 | later. The assignment happens once, when that line is executed, with the 133 | value in `x` at that snapshot in time, and that's it. 134 | 135 | Let's expand the example farther to demonstrate: 136 | 137 | ``` {.py .numberLines} 138 | x = 34 # Variable x is assigned value 34 139 | print(x) 140 | x = 90 # Variable x is assigned value 90 141 | print(x) 142 | 143 | y = x + 40 # y is assigned x + 40 which is 90 + 40, or 130 144 | print(x) # Still 90! Nothing changed here 145 | print(y) # Prints "130" 146 | 147 | x = 1000 148 | print(y) # Still 130! 149 | ``` 150 | 151 | Even though we had `y = x + 40` higher up in the code, `x` was `90` at 152 | the time that code executed, and `y` is set to `130` until we assign 153 | into it again. Changing `x` to `1000` did **not** magically change `y` 154 | to `1040`. 155 | 156 | > _**Fun Tax Fact**: The [flw[1040|Form_1040]] is nearly my 157 | > least-favorite tax form._ 158 | 159 | For more math fun, you have the following operators at your disposal 160 | (there are more, but this is enough to start): 161 | 162 | |Function|Operator| 163 | |:-:|:-:| 164 | |Add|`+`| 165 | |Subtract|`-`| 166 | |Multiply|`*`| 167 | |Divide|`/`| 168 | |Integer Divide^[Integer division truncates the part of the number after the decimal point.]|`//`| |Modulo (remainder)|`%`| 169 | |Exponent|`**`| 170 | 171 | You can also use parentheses similar to how you do in algebra to force 172 | part of an expression to evaluate first. [flw[Normal mathematical order 173 | of operations rules apply|Order_of_operations]]. 174 | 175 | ``` {.py} 176 | 8 + 4 / 2 # 8 + 4 / 2 == 8 + 2 == 10 177 | (8 + 4) / 2 # (8 + 4) / 2 == 12 / 2 == 6 178 | ``` 179 | 180 | And you thought all that algebra wouldn't be useful... _pshaw!_ 181 | 182 | There's a common pattern in programming where you want to, say, add 5 to 183 | a variable. Whatever value it has now, we want to make it 5 more than 184 | that. 185 | 186 | We can do this like so: 187 | 188 | ``` {.py} 189 | x = 10 190 | 191 | x = x + 5 # x = 10 + 5 = 15 192 | ``` 193 | 194 | This pattern is so common, there's a piece of shorthand^[We call 195 | shorthand like this _syntactic sugar_ because it makes things that much 196 | sweeter for the developers.] that we can use instead. 197 | 198 | These two lines are identical: 199 | 200 | ``` {.py} 201 | x = x + 10 202 | x += 10 203 | ``` 204 | 205 | As are these: 206 | 207 | ``` {.py} 208 | x = x / 10 209 | x /= 10 210 | ``` 211 | 212 | Here are a few of the arithmetic assignment expressions available in 213 | Python: 214 | 215 | |Operator|Meaning|Usage|Longhand Equivalent| 216 | |:-:|:-|:-| 217 | |`+=`|Add and assign|`x += y`|`x = x + y`| 218 | |`-=`|Subtract and assign|`x -= y`|`x = x - y`| 219 | |`*=`|Multiply and assign|`x *= y`|`x = x * y`| 220 | |`/=`|Divide and assign|`x /= y`|`x = x / y`| 221 | |`%=`|Modulo and assign|`x %= y`|`x = x % y`| 222 | 223 | These are very frequently used by devs. If you have `x = x + 2`, use `x 224 | += 2`, instead! 225 | 226 | 227 | ## Assigning from One Variable to Another {#var-assignment} 228 | 229 | Let's check out this code: 230 | 231 | ``` {.py} 232 | x = 1000 233 | y = x 234 | ``` 235 | 236 | Something interesting happens here that I want you to make note of. It's 237 | not going to be super useful right now, but it will be later when we get 238 | to more intermediate types of data. 239 | 240 | When you do this, both `x` and `y` refer to the same `1000`. 241 | 242 | That's a weird sentence. 243 | 244 | But think of it this way. Somewhere in the computer memory is the value 245 | 1000. And both `x` and `y` refer to that single value. 246 | 247 | If you do this: 248 | 249 | ``` {.py} 250 | x = 1000 251 | y = 1000 252 | ``` 253 | 254 | Now there are _two_ 1000 values. `x` points to one, and `y` points to 255 | the other. 256 | 257 | Finally, adding on to the original example: 258 | 259 | ``` {.py} 260 | x = 1000 261 | y = x 262 | y = 1000 263 | ``` 264 | 265 | What happens there is that first there is one 1000, and `x` refers to 266 | it. 267 | 268 | Then we assign `x` into `y`, and now both `x` and `y` refer to the same 269 | 1000. 270 | 271 | But then we assign a _different_ 1000 to `y`, so now there are two 272 | 1000s, referred to by `x` and `y`, respectively. 273 | 274 | (The details of this are rather more nuanced than I've mentioned here. 275 | See [Appendix C](#assignment-behavior) if you're crazy enough.) 276 | 277 | Takeaway: variables are just names for items in memory. You can assign 278 | from one variable to another and have them both point to the same item. 279 | 280 | We're just putting that in your brain early so that we can revive it 281 | later. 282 | 283 | 284 | ## Your Mental Model of Computation 285 | 286 | Problem-solving step: **Understanding the Problem**. 287 | 288 | This is a biggie, so listen up for it. 289 | 290 | When you're programming, it's important to keep a mental model of what 291 | _should_ happen when this program runs. 292 | 293 | Let's take our example from before. Step through it in your head, one 294 | line at a time. Keep track of the _state_ of the system as you go: 295 | 296 | ``` {.py .numberLines} 297 | x = 34 # Variable x is assigned value 34 298 | print(x) 299 | x = 90 # Variable x is assigned value 90 300 | print(x) 301 | ``` 302 | 303 | Before we begin, `x` has no value. So represent that in your head as "x 304 | has no value; it's invalid". 305 | 306 | Then the first line runs. 307 | 308 | `x` is now 34. 309 | 310 | Then the second line runs. 311 | 312 | `x` is still 34, and we print it out. (So 34 is printed.) 313 | 314 | Then the third line runs. 315 | 316 | `x` is no longer 34. It is now 90. 34 is gone. 317 | 318 | Then the fourth line runs. 319 | 320 | `x` is still 90, and 90 gets printed out. 321 | 322 | Then we're out of code, so the program exits. And we have "34" and "90" 323 | on the screen from when they were printed. 324 | 325 | _That's_ keeping a mental model of computation. 326 | 327 | This is _the key_ to being able to debug. When your mental computing 328 | model shows different results than the actual program run, you have a 329 | bug somewhere. You have to dig through your code to find the _first_ 330 | place your mental model and the actual program run diverge. That's where 331 | your bug is. 332 | 333 | ## User Input 334 | 335 | Problem-solving step: **Understanding the Problem**. 336 | 337 | We want to get input from the user and store it in a variable so that we 338 | can do things with it. 339 | 340 | Remember that our goal in this chapter is to write a program that inputs 341 | two values from the user on the keyboard and prints the sum of those 342 | values. 343 | 344 | Python comes with a built-in _function_ that allows us to get user 345 | input. It's called, not coincidentally, `input()`. 346 | 347 | But wait---what is a function? 348 | 349 | A function is a chunk of code that does something for you when you 350 | _call_ it (that is when you ask it to). Functions accept _arguments_ 351 | that you can _pass in_ to cause the function to modify its behavior. 352 | Additionally, functions _return_ a value that you can get back from the 353 | function after it's done its work. 354 | 355 | So here we have the `input()` function^[`input()` is what we call a 356 | _built-in_ in Python. It comes with the language and we get to make use 357 | of it. Later we'll learn to write our own functions from scratch!], 358 | which reads from the keyboard when you call it. As an argument, you can 359 | pass in a _prompt_ to show the user when they are ready to type 360 | something in. And it returns whatever the user typed in. 361 | 362 | What do we do with that return value? We can store it in a variable! 363 | Let's try! 364 | 365 | Here's another program, [flx[`inputtest.py`|inputtest.py]]: 366 | 367 | ``` {.py .numberLines} 368 | # Take whatever `input()` returns and store it in `value`: 369 | 370 | value = input("Enter a value: ") 371 | print("You entered", value) 372 | ``` 373 | 374 | We can run it like this: 375 | 376 | ``` {.default} 377 | $ python3 inputtest.py 378 | Enter a value: 3490 379 | You entered 3490 380 | ``` 381 | 382 | Check it out! We entered the value `3490`, stored it in the variable 383 | `value`, and then printed it back out! We're getting there! 384 | 385 | But you can also call it like this: 386 | 387 | ``` {.default} 388 | $ python3 inputtest.py 389 | Enter a value: Goats rock! 390 | You entered Goats rock! 391 | ``` 392 | 393 | Hmmm. That's not a number. But it worked anyway! So are we all good to 394 | go? 395 | 396 | Yes... and no. We're about to discover something very important about 397 | data. 398 | 399 | ## Data Types 400 | 401 | Problem-solving step: **Understanding the Problem**. 402 | 403 | We started with numbers, earlier. That was pretty straightforward. The 404 | variable was assigned a value and then we could do math on it. 405 | 406 | But then we saw in the previous section that `input()` was returning 407 | whatever we typed in, including `Goats rock!` which is certainly not any 408 | number I've ever heard of. 409 | 410 | And, no, it's not a number, indeed. It's a sequence of characters, which 411 | we call a _string_. A string is something like a word, or a sentence, 412 | for example. 413 | 414 | Wait... there's another type of data besides numbers? Yes! Lots of types 415 | of data! We call them _data types_. 416 | 417 | Python associates a _type_ with every variable. This means it keeps 418 | track of whether a variable holds an integer, a _floating point_^[This 419 | is the way most computers represent numbers with a decimal point in 420 | them, such as $3.14159265358979$. When you see "floating point" or 421 | "float", think "number with a decimal point in it" as opposed to 422 | "integer".] number or a string of characters. 423 | 424 | Here are some examples and their associated types. When you store one of 425 | these values in a variable, the variable remembers the type of data 426 | stored within it. 427 | 428 | |Example Data|Type in English|Type name in Python| 429 | |:-|:-|:-| 430 | |`2`|Integer|`int`| 431 | |`3490`|Integer|`int`| 432 | |`-45`|Integer|`int`| 433 | |`0`|Integer|`int`| 434 | |`3.14159`|Floating Point|`float`| 435 | |`-13.8`|Floating Point|`float`| 436 | |`0.0`|Floating Point|`float`| 437 | |`"Hello!"`|String|`str`| 438 | |`"3490"`|String|`str`| 439 | |`""`|String (empty)|`str`| 440 | 441 | In the examples, above, strings are declared using double quotes (`"`), 442 | but they can also be done with single quotes, as long as the quotes 443 | match on both ends: 444 | 445 | ``` {.py} 446 | "Hello!" # is the same as 'Hello!' 447 | 'Hello!' # is the same as "Hello!" 448 | ``` 449 | 450 | Okay, that's all fine. But is `input()` returning a string or a number? 451 | We saw both happen when we tried it out, right? 452 | 453 | Actually, turns out, `input()` **always** returns a string. Period. Even 454 | if that's a string of numbers. Note that these things are **not** the 455 | same: 456 | 457 | ``` {.py} 458 | 3490 # int, a numeric value we can do math with 459 | "3490" # string, a sequence of characters 460 | ``` 461 | 462 | Sure, they look kinda the same, but they aren't the same _because they 463 | have different types_. You can do arithmetic on an `int`, but not on a 464 | string. 465 | 466 | Well, that's just great. The task for this chapter is to get two numbers 467 | from the keyboard and add them together, but the `input()` function only 468 | returns strings, and we can't add strings together numerically! 469 | 470 | How can we solve this problem? 471 | 472 | ## Converting Between Data Types 473 | 474 | Problem-solving step: **Understanding the Problem**. 475 | 476 | If we can't add strings mathematically, can we convert the string 477 | `"3490"` into the integer `3490` and then do math on that? 478 | 479 | Yes! 480 | 481 | In fact, we can convert back and forth between all kinds of data types! 482 | Watch us convert a string to a number and store it in a variable: 483 | 484 | ``` {.py} 485 | a = "3490" # a is a string "3490" 486 | b = int(a) # b is an integer 3490! 487 | 488 | print(b + 5) # 3495 489 | ``` 490 | 491 | How did that work? We called the built-in `int()` function and passed it 492 | a string `"3490"`. `int()` did all the hard work and converted that 493 | string to an integer and returned it. We then stored the returned value 494 | in `b`. And finally, we printed the value of `b+5` just to show that we 495 | could do math on it. 496 | 497 | Perfect! 498 | 499 | Here are some of the conversion functions available to you in Python: 500 | 501 | |Function|Effect| 502 | |:-:|:-| 503 | |`int()`|Convert argument to an integer and return it| 504 | |`float()`|Convert argument to a floating-point number and return it| 505 | |`str()`|Convert argument to a string and return it| 506 | 507 | 508 | So... given all that we know so far, how can we solve this chapter's 509 | problem: input two numbers from the keyboard and print the sum? 510 | 511 | 512 | ## Input Two Numbers and Print the Sum 513 | 514 | Problem-solving step: **Devising a Plan**. 515 | 516 | We know: 517 | 518 | * How to input strings from the keyboard 519 | * How to convert strings to numbers 520 | * How to add numbers together 521 | * How to print things out 522 | 523 | Now---how do we put all that together to write a program that inputs two 524 | numbers from the keyboard and prints their sum? 525 | 526 | This is the _Devising a Plan_ portion of problem-solving. We're not 527 | going to write code to make this happen. We're just going to write an 528 | outline of the individual steps the program must describe in a language 529 | called _pseudocode_ (which is English that looks kinda like code). 530 | 531 | Then when we're satisfied it'll work, we can code it up for realsies. 532 | 533 | So stop here and take a moment to consider what the step by step might 534 | be to get this done. 535 | 536 | Really, take a moment, because I'm about to give spoilers. Thinking 537 | about how to solve problems is 80% of what a software developer gets 538 | paid to do, so you might as well practice right now. 539 | 540 | What do we know? What tools do we have at our disposal? What resources? 541 | How do I put all those together to solve this problem, like solving a 542 | puzzle? 543 | 544 | Here's some pseudocode that would get the job done, it looks like: 545 | 546 | ``` {.default} 547 | read string from keyboard into variable x 548 | convert x to int and store it back in x again 549 | read string from keyboard into variable y 550 | convert y to int and store it back in y again 551 | print the sum of x + y 552 | ``` 553 | 554 | If we're satisfied that our plan is solid, it's time to move to the next 555 | phase. 556 | 557 | Problem-solving step: **Carrying out the Plan**. 558 | 559 | Now let's convert each of those lines to real Python. I'll throw in the 560 | pseudocode as comments so we can see how they compare. ([flx[Source 561 | code link|twosum.py]].) 562 | 563 | ``` {.py .numberLines} 564 | # Read string from keyboard into variable x 565 | x = input("Enter a number: ") 566 | 567 | # Convert x to int and store it back in x again 568 | x = int(x) 569 | 570 | # Read string from keyboard into variable y 571 | y = input("Enter another number: ") 572 | 573 | # Convert y to int and store it back in y again 574 | y = int(y) 575 | 576 | # Print the sum of x + y 577 | print("The sum of the two numbers is:", x + y) 578 | ``` 579 | 580 | Save that file as `twosum.py` and run it: 581 | 582 | ``` {.default} 583 | $ python3 twosum.py 584 | Enter a number: 9 585 | Enter another number: 8 586 | The sum of the two numbers is: 17 587 | ``` 588 | 589 | Too easy! Let's challenge it: 590 | 591 | ``` {.default} 592 | $ python3 twosum.py 593 | Enter a number: 235896423496865928659832536289 594 | Enter another number: 94673984675289643982463929238 595 | The sum of the two numbers is: 330570408172155572642296465527 596 | ``` 597 | 598 | Not even breaking a sweat! 599 | 600 | Nice. Now, I want you to _think like a villain_. What would a villain 601 | pass into our program for input that would cause it to break? 602 | 603 | * Negative numbers? 604 | * Zero? 605 | * Decimal numbers? 606 | * Non-numbers, like "goat"? 607 | 608 | Try all those things with your program. What happens when you try it? 609 | Which ones work and which ones don't? 610 | 611 | Notice that a big, spewing error message is really the _worst case 612 | scenario_ here. And it's not really that painful. Don't be afraid to 613 | try to break your code. The computer can handle it. Just run it again. 614 | 615 | Later, we'll learn techniques to catch errors like this so that the 616 | program doesn't bomb out, and prints a nice message to the user to 617 | please try again with valid input, thank you very much. 618 | 619 | > _Notice that when the program crashes, buried in all that output, is 620 | > the line number the program crashed on! Very, very useful! And the 621 | > last line tells you exactly what Python thinks went wrong._ 622 | 623 | The point is, if you're not sure how something will work, _**just try 624 | it**_. Experiment! Break things! Fix things! Again, the computer can 625 | absolutely handle it. It's just a big sandbox for you to play in. 626 | 627 | ## Wrapping it Up 628 | 629 | Problem-solving step: **Looking Back**. 630 | 631 | This grimly-named step is where we take a look at our code and decide if 632 | there was a better way to attack this problem. It's important to 633 | remember that _coding is a creative endeavor_. There are many ways to 634 | solve the same problem. 635 | 636 | Admittedly, right now, you don't have many tools in the toolkit, so your 637 | creativity is limited. But eventually, in the not-too-distant future, 638 | you'll know several ways to solve a problem, and you'll have to weigh 639 | the pros and cons of each, and be creative and choose one! 640 | 641 | What could be better? 642 | 643 | * We saw earlier that passing in floating point numbers (with a decimal 644 | point) bombed out. It would be nice if the program would add both 645 | floating-point. 646 | 647 | What else could we do? 648 | 649 | * What about other math operations? 650 | 651 | ## Exercises 652 | 653 | > _"You know how to get to Carnegie Hall?"_ 654 | > 655 | > _"Practice!"_ 656 | 657 | Zeus says, "**This book assumes you complete all of the exercises!**" 658 | and when Zeus speaks, people really should listen. 659 | 660 | I know, I know. You get to the exercises part of a book and you just 661 | skip ahead. I mean, it's not like I'm _grading_ you or anything. 662 | 663 | But there's only one way to get to be a better dev: practice and 664 | repetition. Going through this book without doing the exercises is like 665 | training for a marathon by reading about how to run. It's simply not 666 | going to get you there on its own. 667 | 668 | **Resist the urge to look at the solution until you've solved it!** Give 669 | yourself a time limit. "If I haven't solved this in 20 minutes, I can 670 | look at the solution." That 20 minute isn't wasted---it's invaluable 671 | problem-solving practice time. During that time, you're building a 672 | scaffold in your brain that can _hold_ the solution once you see it. 673 | 674 | If you just skip straight to the solution, look at it, and say, "Yup, 675 | makes sense, I got it," you're missing out on all that benefit. 676 | 677 | Don't shortchange yourself! Do the exercises! The more you do, the 678 | better dev you'll be! I'm getting off my soapbox now! 679 | 680 | Remember to use the [four problem-solving steps](#problem-solving) to 681 | solve these problems: understand the problem, devise a plan, carry it 682 | out, look back to see what you could have done better. 683 | 684 | Here they are: 685 | 686 | 1. Make a version of the two number sum code that works with `float`s 687 | instead of `int`s. Do the numbers always add correctly, or are they 688 | sometimes just a little bit off? Lesson: _floating point math isn't 689 | always exact_. Sometimes it's off by a tiny fraction. 690 | ([flx[Solution|ex_twosumfloat.py]].) 691 | 692 | 2. Have the program print out the sum and the difference between two 693 | integers. ([flx[Solution|ex_twosumdiff.py]].) 694 | 695 | 3. Allow the user to enter 3 numbers and perform the math on those. 696 | ([flx[Solution|ex_threesumdiff.py]].) 697 | 698 | 4. Write a program that allows the user to enter a value for $x$, and 699 | then computes and prints $x^2$. Remember `**` is the exponentiation 700 | operator in Python. `3**2` is `9`. ([flx[Solution|ex_xsquared.py]].) 701 | 702 | 5. Write a program that allows the user to enter `a`, `b`, and `c`, and 703 | the solves [flw[the quadratic formula|Quadratic_formula]] for those 704 | values. 705 | 706 | A refresher: with equations of the form: 707 | 708 | $ax^2+bx+c=0$ 709 | 710 | you can solve for $x$ with the quadratic formula: 711 | 712 | $x=\cfrac{-b\pm\sqrt{b^2-4ac}}{2a}$ 713 | 714 | This all looks terrifying! Can you feel your brain seizing up over 718 | it? Deer in the headlights? _That's OK_. This is how developers feel 719 | when confronted with a new problem. Really! All of us! But what we 720 | know is that we have a problem solving framework we can use to attack 721 | this problem regardless of how difficult it seems initially. 722 | 723 | Remember: Understand, Plan, then Code It Up. 724 | 725 | Take a deep breath. Shake off the fear! 726 | 727 | You can absolutely do this. It's not any harder than anything so far! 728 | Let's go! 729 | 730 | Your program should plug `a`, `b`, and `c` into the above formula and 731 | print out the result value in `x`. 732 | 733 | > Make sure $b^2\ge4ac$ or there won't be a solution and you'll get a 734 | > "domain error" when you try to take the square root of a negative 735 | > number. Some test values for $a$, $b$, and $c$ that work: `5`, `9`, 736 | > `3`, or `20`, `140`, `60`. 737 | 738 | What is that $\pm$ symbol after $-b$ in the equation? That's "plus or 739 | minus". It means there are actually two equations, one with $+$ and 740 | one with $-$: 741 | 742 | $x_{plus}=\cfrac{-b+\sqrt{b^2-4ac}}{2a}$\ \ \ \ \ \ $x_{minus}=\cfrac{-b-\sqrt{b^2-4ac}}{2a}$ 743 | 744 | Solve them both and print out both answers for a given $a$, $b$, and 745 | $c$. 746 | 747 | What about that square root of $b^2-4ac$? How do you compute that? 748 | Here's a demo program for computing the square root of `2`---use it 749 | to learn how to use the `math.sqrt()` function, and then apply it to 750 | this problem. 751 | 752 | ``` {.py .numberLines} 753 | import math # You need this for access to the sqrt() function 754 | 755 | x = math.sqrt(2) # Compute sqrt(2) 756 | print(x) # 1.4142135623730951 757 | ``` 758 | 759 | Code that up and, hey! You've written a program that solves quadratic 760 | equations! Take _that_, homework! ([flx[Solution|ex_quadratic.py]].) 761 | 762 | 6. Followup to the previous question: after computing `x`, go ahead and 763 | compute the value of 764 | 765 | $ax^2+bx+c$ 766 | 767 | and print it out. (You can use either the plus solution or the minus 768 | solution---doesn't matter since they're both solutions.) The result 769 | should be exactly `0`. Is it? Or is it just something really close to 770 | zero? Lesson: _floating point math isn't always exact_. Sometimes 771 | it's off by a tiny fraction. 772 | 773 | Sometimes you might get a number back that looks like this, with a 774 | funky `e-16` at the end (or `e-`something): 775 | 776 | `8.881784197001252e-16` 777 | 778 | That's a floating point number, but in [flw[scientific 779 | notation|Scientific_notation]]. That `e-16` is the same as 780 | $\times10^{-16}$. So the math equivalent is: 781 | 782 | $8.881784197001252\times10^{-16}$ 783 | 784 | Now, $10^{-16}$ is actually a really, really small number. So if you 785 | see something like `e-15` or `e-18` at the end of a float, think 786 | "that's a really small number, like close to zero." 787 | 788 | ([flx[Solution|ex_quadratic2.py]].) 789 | 790 | 7. Make up two more exercises and code them up. 791 | 792 | And don't worry--we'll get away from the math examples soon enough. 793 | It's just, for now, that's about all we know. More soon! 794 | 795 | ## Summary 796 | 797 | This chapter we covered: 798 | 799 | * Data and variables 800 | * Storing and printing data in variables 801 | * Doing basic math 802 | * Getting keyboard input 803 | * Data types, and conversions between them 804 | * String 805 | * Integer 806 | * Floating Point 807 | * Keeping the problem-solving framework in mind the whole time! 808 | 809 | It's a great start, but there's plenty more to come! 810 | 811 | --------------------------------------------------------------------------------