├── 02_Control_Flow └── exitExample.py ├── 03_Function ├── 3_Function.ipynb ├── Practice_Projects │ └── Collatz.py ├── guessTheNumber.py └── try_except_test.py ├── 04_List ├── 4_List.ipynb ├── Practice_Projects │ ├── character_picture_grid.py │ ├── comma_code1.py │ └── comma_code2.py ├── allMyCats2.py ├── magic8Ball2.py └── my_pets.py ├── 05_Dictionaries_and_Structuring_Data ├── 5_Dict.ipynb ├── Practice_Projects │ ├── Fantasy_Game_Inventory.py │ └── Fantasy_Game_Inventory_dragon.py ├── birthdays.py ├── character_count.py ├── pretty_character_count.py ├── ticTacToe.py └── total_brought.py ├── 06_Manipulating_Strings ├── 6_Strings.ipynb ├── Chapter_Projects │ ├── bulletPointAdder.py │ ├── bullet_point_adder_error.py │ └── pw.py ├── Practice_Projects │ └── table_printer.py └── picnic_table.py ├── 07_Pattern_Matching_with_Regular_Expressions ├── 7_regex.ipynb ├── Chapter_Project │ ├── phoneAndEmail.py │ └── phone_and_eamil.py └── Practice_Project │ ├── regex_strip.py │ └── strong_password_detection.py ├── 08_Reading_and_Writing_Files ├── 8_read_and_write.ipynb ├── Chapter_Project │ ├── mcb.pyw │ ├── randomQuizGenerator.py │ └── random_quiz_generator.py ├── __pycache__ │ └── my_cats.cpython-35.pyc ├── bacon.txt ├── hello.txt ├── my_cats.py ├── mydata.db └── sonnet29.txt ├── 09_Organizing_Files ├── 9_organizing_files.ipynb ├── Practice_Projects │ ├── deleting_unneeded_files.py │ ├── filling_in_the_gaps.py │ └── selective_copy.py ├── cats │ ├── catnames.txt │ └── zophie.jpg ├── example.zip ├── new.zip ├── some_new │ └── folders │ │ └── spam.txt └── spam.txt ├── 10_Debugging ├── .idea │ ├── 10_Debugging.iml │ ├── misc.xml │ ├── modules.xml │ └── workspace.xml ├── 10_debugging.ipynb ├── box_print.py ├── buggy_adding_program.py ├── coin_flip.py ├── error_example.py ├── error_info.txt ├── factorial_log.py └── my_program_log.txt ├── 11_Web_Scraping ├── 11_web.ipynb ├── Romeo_and_Juliet.txt ├── argv.py ├── bookcover.py ├── download_xkcd.py ├── example.html ├── geckodriver.log ├── lucky.py ├── mapit.py ├── xkcd │ ├── startup_opportunity.png │ ├── team_chat.png │ ├── things_you_learn.png │ ├── trash.png │ ├── tv_problems.png │ ├── ui_change.png │ ├── us_state_names.png │ ├── voice_commands.png │ ├── wifi.png │ └── xkcde.png └── xkcd_test │ └── voice_commands.png ├── 15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs ├── 15_time_task_program.ipynb ├── alarm.wav ├── calc_prod.py ├── countdown.py ├── hello.txt ├── multi_download_xkcd.py ├── multi_download_xkcd_time.py ├── multidownloadXkcd.py ├── single_download_xkcd_time.py ├── single_download_xkcd_time_2.py └── stopwatch.py ├── Automate_online-materials ├── allMyCats1.py ├── allMyCats2.py ├── backupToZip.py ├── birthdays.py ├── boxPrint.py ├── buggyAddingProgram.py ├── bulletPointAdder.py ├── calcProd.py ├── catlogo.png ├── catnapping.py ├── census2010.py ├── censuspopdata.xlsx ├── characterCount.py ├── coinFlip.py ├── combinePdfs.py ├── combinedminutes.pdf ├── countdown.py ├── demo.docx ├── dictionary.txt ├── dimensions.xlsx ├── downloadXkcd.py ├── duesRecords.xlsx ├── encrypted.pdf ├── encryptedminutes.pdf ├── errorExample.py ├── example.csv ├── example.html ├── example.xlsx ├── example.zip ├── excelSpreadsheets.zip ├── exitExample.py ├── factorialLog.py ├── fiveTimes.py ├── formFiller.py ├── freezeExample.xlsx ├── getDocxText.py ├── guessTheNumber.py ├── guests.txt ├── headings.docx ├── hello.py ├── helloFunc.py ├── helloFunc2.py ├── helloworld.docx ├── inventory.py ├── isPhoneNumber.py ├── littleKid.py ├── lucky.py ├── magic8Ball.py ├── magic8Ball2.py ├── mapIt.py ├── mcb.pyw ├── meetingminutes.pdf ├── meetingminutes2.pdf ├── merged.xlsx ├── mouseNow.py ├── mouseNow2.py ├── multidownloadXkcd.py ├── multipleParagraphs.docx ├── myPets.py ├── passingReference.py ├── phoneAndEmail.py ├── picnicTable.py ├── prettyCharacterCount.py ├── printRandom.py ├── produceSales.xlsx ├── pw.py ├── quickWeather.py ├── randomQuizGenerator.py ├── readCensusExcel.py ├── readDocx.py ├── removeCsvHeader.py ├── removeCsvHeader.zip ├── renameDates.py ├── resizeAndAddLogo.py ├── restyled.docx ├── sameName.py ├── sameName2.py ├── sameName3.py ├── sameName4.py ├── sampleChart.xlsx ├── sendDuesReminders.py ├── stopwatch.py ├── styled.xlsx ├── styles.xlsx ├── swordfish.py ├── textMyself.py ├── threadDemo.py ├── ticTacToe.py ├── torrentStarter.py ├── twoPage.docx ├── updateProduce.py ├── updatedProduceSales.xlsx ├── validateInput.py ├── vampire.py ├── vampire2.py ├── watermark.pdf ├── zeroDivide.py └── zophie.png └── README.md /02_Control_Flow/exitExample.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | while True: 4 | print ('Type exit to exit.') 5 | response = input() 6 | if response == 'exit': 7 | sys.exit() 8 | print ('You typed ' + response + '.') 9 | -------------------------------------------------------------------------------- /03_Function/3_Function.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## 3.4 print " 8 | ] 9 | }, 10 | { 11 | "cell_type": "code", 12 | "execution_count": 1, 13 | "metadata": { 14 | "collapsed": false 15 | }, 16 | "outputs": [ 17 | { 18 | "name": "stdout", 19 | "output_type": "stream", 20 | "text": [ 21 | "Hello\n", 22 | "World\n" 23 | ] 24 | } 25 | ], 26 | "source": [ 27 | "print ('Hello')\n", 28 | "print ('World')" 29 | ] 30 | }, 31 | { 32 | "cell_type": "code", 33 | "execution_count": 2, 34 | "metadata": { 35 | "collapsed": false 36 | }, 37 | "outputs": [ 38 | { 39 | "name": "stdout", 40 | "output_type": "stream", 41 | "text": [ 42 | "HelloWorld\n" 43 | ] 44 | } 45 | ], 46 | "source": [ 47 | "print ('Hello', end='')\n", 48 | "print ('World')" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "execution_count": 3, 54 | "metadata": { 55 | "collapsed": false 56 | }, 57 | "outputs": [ 58 | { 59 | "name": "stdout", 60 | "output_type": "stream", 61 | "text": [ 62 | "cats dogs mice\n" 63 | ] 64 | } 65 | ], 66 | "source": [ 67 | "print ('cats', 'dogs', 'mice')" 68 | ] 69 | }, 70 | { 71 | "cell_type": "code", 72 | "execution_count": 4, 73 | "metadata": { 74 | "collapsed": false 75 | }, 76 | "outputs": [ 77 | { 78 | "name": "stdout", 79 | "output_type": "stream", 80 | "text": [ 81 | "cats,dogs,mice\n" 82 | ] 83 | } 84 | ], 85 | "source": [ 86 | "print ('cats', 'dogs', 'mice', sep=',')" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "## 3.7 异常处理\n", 94 | "\n", 95 | "错误可以由try和except语句来处理。那些可能出错的语句被放在try子句中。如果错误发生,程序执行就转到接下来的except子句开始处。\n", 96 | "\n", 97 | "可以将前面除数为零的代码放在一个try子句中,让except子句包含代码,来处理该错误发生时应该做的事。" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 7, 103 | "metadata": { 104 | "collapsed": false 105 | }, 106 | "outputs": [ 107 | { 108 | "name": "stdout", 109 | "output_type": "stream", 110 | "text": [ 111 | "21.0\n", 112 | "3.5\n", 113 | "Error: Invalid argument.\n", 114 | "None\n", 115 | "42.0\n" 116 | ] 117 | } 118 | ], 119 | "source": [ 120 | "def spam(divideBy):\n", 121 | " try:\n", 122 | " return 42/divideBy\n", 123 | " except ZeroDivisionError:\n", 124 | " print ('Error: Invalid argument.')\n", 125 | "\n", 126 | "print (spam(2))\n", 127 | "print (spam(12))\n", 128 | "print (spam(0))\n", 129 | "print (spam(1))" 130 | ] 131 | }, 132 | { 133 | "cell_type": "markdown", 134 | "metadata": {}, 135 | "source": [ 136 | "在函数调用中的try语句块中,发生的所有错误都会被捕捉。请考虑以下程序,它的做法不一样,将spam()调用放在语句块中:\n", 137 | "\n", 138 | "print(spam(1))从未被执行是因为,一旦执行跳到except子句的代码,就不会回到try子句。它会继续照常向下执行。" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": 8, 144 | "metadata": { 145 | "collapsed": false 146 | }, 147 | "outputs": [ 148 | { 149 | "name": "stdout", 150 | "output_type": "stream", 151 | "text": [ 152 | "21.0\n", 153 | "3.5\n", 154 | "Error: Invalid argument\n" 155 | ] 156 | } 157 | ], 158 | "source": [ 159 | "def spam(divideBy):\n", 160 | " return 42 / divideBy\n", 161 | "\n", 162 | "try:\n", 163 | " print (span(2))\n", 164 | " print (spam(12))\n", 165 | " print (spam(0))\n", 166 | " print (spam(1))\n", 167 | "except ZeroDivisionError:\n", 168 | " print ('Error: Invalid argument')\n", 169 | " " 170 | ] 171 | }, 172 | { 173 | "cell_type": "code", 174 | "execution_count": 9, 175 | "metadata": { 176 | "collapsed": false 177 | }, 178 | "outputs": [ 179 | { 180 | "data": { 181 | "text/plain": [ 182 | "5" 183 | ] 184 | }, 185 | "execution_count": 9, 186 | "metadata": {}, 187 | "output_type": "execute_result" 188 | } 189 | ], 190 | "source": [ 191 | "10//2" 192 | ] 193 | }, 194 | { 195 | "cell_type": "code", 196 | "execution_count": 10, 197 | "metadata": { 198 | "collapsed": false 199 | }, 200 | "outputs": [ 201 | { 202 | "data": { 203 | "text/plain": [ 204 | "5.0" 205 | ] 206 | }, 207 | "execution_count": 10, 208 | "metadata": {}, 209 | "output_type": "execute_result" 210 | } 211 | ], 212 | "source": [ 213 | "10/2" 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "execution_count": null, 219 | "metadata": { 220 | "collapsed": true 221 | }, 222 | "outputs": [], 223 | "source": [] 224 | } 225 | ], 226 | "metadata": { 227 | "anaconda-cloud": {}, 228 | "kernelspec": { 229 | "display_name": "Python [conda env:py35]", 230 | "language": "python", 231 | "name": "conda-env-py35-py" 232 | }, 233 | "language_info": { 234 | "codemirror_mode": { 235 | "name": "ipython", 236 | "version": 3 237 | }, 238 | "file_extension": ".py", 239 | "mimetype": "text/x-python", 240 | "name": "python", 241 | "nbconvert_exporter": "python", 242 | "pygments_lexer": "ipython3", 243 | "version": "3.5.2" 244 | } 245 | }, 246 | "nbformat": 4, 247 | "nbformat_minor": 1 248 | } 249 | -------------------------------------------------------------------------------- /03_Function/Practice_Projects/Collatz.py: -------------------------------------------------------------------------------- 1 | 2 | def collatz(number): 3 | return number // 2 if (number % 2) == 0 else number * 3 + 1 4 | 5 | if __name__ == '__main__': 6 | print ('Enter a number greater than one: ') 7 | try: 8 | number = int(input()) 9 | while True: 10 | if number != 1: 11 | number = collatz(number) 12 | print (number) 13 | else: 14 | break 15 | except: 16 | print ('Error: Invalid Value. You did not enter an integer.') 17 | -------------------------------------------------------------------------------- /03_Function/guessTheNumber.py: -------------------------------------------------------------------------------- 1 | # This is a guess the number game. 2 | import random 3 | secret_number = random.randint(1, 20) 4 | print ('I am thinking of a number between 1 and 20.') 5 | 6 | # Ask the player to guess 6 times. 7 | for guesses_taken in range(1, 7): 8 | print ('take a guess.') 9 | guess = int(input()) 10 | 11 | if guess < secret_number: 12 | print ('Your guess is too low.') 13 | elif guess > secret_number: 14 | print ('Your guess is too high.') 15 | else: 16 | break # This condition is the correct guess! 17 | 18 | if guess == secret_number: 19 | print ('Good job! You guessd my number in ' + str(guesses_taken) + ' guesses!') 20 | else: 21 | print ('Nope. The number I was thinking of was ' + str(secret_number)) 22 | -------------------------------------------------------------------------------- /03_Function/try_except_test.py: -------------------------------------------------------------------------------- 1 | def spam(divideBy): 2 | try: 3 | return 42/divideBy 4 | except ZeroDivisionError: 5 | print ('Error: Invalid argument.') 6 | 7 | print (spam(2)) 8 | print (spam(12)) 9 | print (spam(0)) 10 | print (spam(1)) 11 | -------------------------------------------------------------------------------- /04_List/Practice_Projects/character_picture_grid.py: -------------------------------------------------------------------------------- 1 | grid = [['.', '.', '.', '.', '.', '.'], 2 | ['.', 'O', 'O', '.', '.', '.'], 3 | ['O', 'O', 'O', 'O', '.', '.'], 4 | ['O', 'O', 'O', 'O', 'O', '.'], 5 | ['.', 'O', 'O', 'O', 'O', 'O'], 6 | ['O', 'O', 'O', 'O', 'O', '.'], 7 | ['O', 'O', 'O', 'O', '.', '.'], 8 | ['.', 'O', 'O', '.', '.', '.'], 9 | ['.', '.', '.', '.', '.', '.']] 10 | 11 | 12 | def plot_grid(grid): 13 | num_rows = len(grid) 14 | num_cols = len(grid[0]) 15 | 16 | for col in range(num_cols): 17 | for row in range(num_rows): 18 | print (grid[row][col], end='') 19 | print() 20 | 21 | if __name__ == '__main__': 22 | plot_grid(grid) 23 | -------------------------------------------------------------------------------- /04_List/Practice_Projects/comma_code1.py: -------------------------------------------------------------------------------- 1 | def list2str(row_list): 2 | output = '' 3 | for i in range(len(row_list)): 4 | if i != len(row_list) - 1: 5 | output += str(row_list[i]) + ', ' 6 | else: 7 | output += 'and ' + str(row_list[i]) 8 | return output 9 | 10 | if __name__ == '__main__': 11 | row_list = ['apples', 'bananas', 'tofu', 'cats'] 12 | print (list2str(row_list)) 13 | 14 | -------------------------------------------------------------------------------- /04_List/Practice_Projects/comma_code2.py: -------------------------------------------------------------------------------- 1 | # using ast.literal_eval change the input(string) to a list in order to process 2 | 3 | import ast 4 | 5 | def list2str(row_list): 6 | output = '' 7 | for i in range(len(row_list)): 8 | if i != len(row_list) - 1: 9 | output += row_list[i] + ', ' 10 | else: 11 | output += 'and ' + row_list[i] 12 | return output 13 | 14 | if __name__ == '__main__': 15 | try: 16 | print ('Enter a list:') 17 | row_list = ast.literal_eval(input()) 18 | print (list2str(row_list)) 19 | except: 20 | print ('Error: Invalid Value. You did not enter a list.') 21 | -------------------------------------------------------------------------------- /04_List/allMyCats2.py: -------------------------------------------------------------------------------- 1 | cat_names = [] 2 | 3 | while True: 4 | print ('Input the cat ' + str(len(cat_names)+1) + ' or input nothing to break') 5 | name = input() 6 | if name == '': 7 | break 8 | else: 9 | cat_names = cat_names + [name] 10 | 11 | print ("Now you have cats: ") 12 | # for i in range(len(cat_names)): 13 | # print ("The cat " + str(i) + " is " + cat_names[i]) 14 | for name in cat_names: 15 | print (' ' + name) 16 | -------------------------------------------------------------------------------- /04_List/magic8Ball2.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | messages = ['It is certain', 4 | 'It is decidedly so', 5 | 'Yes definitely', 6 | 'Reply hazy try again', 7 | 'Ask again later', 8 | 'Concentrate and ask again', 9 | 'My reply is no', 10 | 'Outlook not so good', 11 | 'Very doubtful'] 12 | 13 | print (messages[random.randint(0, len(messages)-1)]) 14 | -------------------------------------------------------------------------------- /04_List/my_pets.py: -------------------------------------------------------------------------------- 1 | myPets = ['Zophie', 'Pooka', 'Fat-tail'] 2 | print ('Enter your pet\'s name: ') 3 | name = input() 4 | if name not in myPets: 5 | print ("You don't have such pet") 6 | else: 7 | print ("Your pet is " + name) 8 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/5_Dict.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "collapsed": true 7 | }, 8 | "source": [ 9 | "## 5.1 字典数据类型\n", 10 | "\n", 11 | "### 5.1.2 keys()、values()和items()方法\n", 12 | "\n", 13 | "有3个字典方法,它们将返回类似列表的值,分别对应于字典的键、值和键-值对:keys()、values()和items()。这些方法返回的值不是真正的列表,它们不能被修改,没有append()方法。但这些数据类型(分别是dict_keys、dict_values和dict_items)可以用于for循环。\n", 14 | "\n" 15 | ] 16 | }, 17 | { 18 | "cell_type": "code", 19 | "execution_count": 2, 20 | "metadata": { 21 | "collapsed": false 22 | }, 23 | "outputs": [ 24 | { 25 | "name": "stdout", 26 | "output_type": "stream", 27 | "text": [ 28 | "red\n", 29 | "42\n" 30 | ] 31 | } 32 | ], 33 | "source": [ 34 | "spam = {'color': 'red', 'age': 42}\n", 35 | "for v in spam.values():\n", 36 | " print (v)" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "execution_count": 3, 42 | "metadata": { 43 | "collapsed": false 44 | }, 45 | "outputs": [ 46 | { 47 | "name": "stdout", 48 | "output_type": "stream", 49 | "text": [ 50 | "color\n", 51 | "age\n" 52 | ] 53 | } 54 | ], 55 | "source": [ 56 | "for k in spam.keys():\n", 57 | " print (k)" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": 4, 63 | "metadata": { 64 | "collapsed": false 65 | }, 66 | "outputs": [ 67 | { 68 | "name": "stdout", 69 | "output_type": "stream", 70 | "text": [ 71 | "('color', 'red')\n", 72 | "('age', 42)\n" 73 | ] 74 | } 75 | ], 76 | "source": [ 77 | "for i in spam.items():\n", 78 | " print (i)" 79 | ] 80 | }, 81 | { 82 | "cell_type": "markdown", 83 | "metadata": {}, 84 | "source": [ 85 | "利用keys()、values()和items()方法,循环分别可以迭代键、值或键-值对。请注意,items()方法返回的dict_items值中,包含的是键和值的元组。\n", 86 | "\n", 87 | "如果希望通过这些方法得到一个真正的列表,就把类似列表的返回值传递给 list函数。" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": 5, 93 | "metadata": { 94 | "collapsed": false 95 | }, 96 | "outputs": [ 97 | { 98 | "data": { 99 | "text/plain": [ 100 | "dict_keys(['color', 'age'])" 101 | ] 102 | }, 103 | "execution_count": 5, 104 | "metadata": {}, 105 | "output_type": "execute_result" 106 | } 107 | ], 108 | "source": [ 109 | "spam.keys()" 110 | ] 111 | }, 112 | { 113 | "cell_type": "code", 114 | "execution_count": 6, 115 | "metadata": { 116 | "collapsed": false 117 | }, 118 | "outputs": [ 119 | { 120 | "data": { 121 | "text/plain": [ 122 | "['color', 'age']" 123 | ] 124 | }, 125 | "execution_count": 6, 126 | "metadata": {}, 127 | "output_type": "execute_result" 128 | } 129 | ], 130 | "source": [ 131 | "list(spam.keys())" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": {}, 137 | "source": [ 138 | "也可以利用多重赋值的技巧,在for循环中将键和值赋给不同的变量。" 139 | ] 140 | }, 141 | { 142 | "cell_type": "code", 143 | "execution_count": 7, 144 | "metadata": { 145 | "collapsed": false 146 | }, 147 | "outputs": [ 148 | { 149 | "name": "stdout", 150 | "output_type": "stream", 151 | "text": [ 152 | "Key: color Value: red\n", 153 | "Key: age Value: 42\n" 154 | ] 155 | } 156 | ], 157 | "source": [ 158 | "for k, v in spam.items():\n", 159 | " print ('Key: ' + k + ' Value: ' + str(v))" 160 | ] 161 | }, 162 | { 163 | "cell_type": "markdown", 164 | "metadata": {}, 165 | "source": [ 166 | "### 5.1.3 检查字典中是否存在键或值\n", 167 | "回忆一下,前一章提到,in和not in操作符可以检查值是否存在于列表中。也可以利用这些操作符,检查某个键或值是否存在于字典中。在交互式环境中输入以下代码:\n", 168 | "```\n", 169 | ">>> spam = {'name': 'Zophie', 'age': 7}\n", 170 | ">>> 'name' in spam.keys()\n", 171 | "True\n", 172 | ">>> 'Zophie' in spam.values()\n", 173 | "True\n", 174 | ">>> 'color' in spam.keys()\n", 175 | "False\n", 176 | ">>> 'color' not in spam.keys()\n", 177 | "True\n", 178 | ">>> 'color' in spam\n", 179 | "False\n", 180 | "```\n", 181 | "\n", 182 | "请注意,在前面的例子中,'color' in spam本质上是一个简写版本。相当于'color' in spam.keys()。这种情况总是对的:如果想要检查一个值是否为字典中的键,就可以用关键字in(或not in),作用于该字典本身。\n", 183 | "\n", 184 | "### 5.1.4 get()方法\n", 185 | "在访问一个键的值之前,检查该键是否存在于字典中,这很麻烦。好在,字典有一个get()方法,它有两个参数:要取得其值的键,以及如果该键不存在时,返回的备用值。\n", 186 | "\n", 187 | "在交互式环境中输入以下代码:\n", 188 | "```\n", 189 | ">>> picnicItems = {'apples': 5, 'cups': 2}\n", 190 | ">>> 'I am bringing ' + str(picnicItems.get('cups', 0)) + ' cups.'\n", 191 | "'I am bringing 2 cups.'\n", 192 | ">>> 'I am bringing ' + str(picnicItems.get('eggs', 0)) + ' eggs.'\n", 193 | "'I am bringing 0 eggs.'\n", 194 | "```\n", 195 | "因为picnicItems字典中没有'egg'键,get()方法返回的默认值是0。不使用get(),代码就会产生一个错误消息,就像下面的例子:\n", 196 | "```\n", 197 | ">>> picnicItems = {'apples': 5, 'cups': 2}\n", 198 | ">>> 'I am bringing ' + str(picnicItems['eggs']) + ' eggs.'\n", 199 | "Traceback (most recent call last):\n", 200 | " File \"\", line 1, in \n", 201 | " 'I am bringing ' + str(picnicItems['eggs']) + ' eggs.'\n", 202 | "KeyError: 'eggs'\n", 203 | "```\n", 204 | "### 5.1.5 setdefault()方法\n", 205 | "你常常需要为字典中某个键设置一个默认值,当该键没有任何值时使用它。代码看起来像这样:\n", 206 | "```\n", 207 | "spam = {'name': 'Pooka', 'age': 5}\n", 208 | "if 'color' not in spam:\n", 209 | " spam['color'] = 'black'\n", 210 | "```\n", 211 | "setdefault()方法提供了一种方式,在一行中完成这件事。传递给该方法的第一个参数,是要检查的键。第二个参数,是如果该键不存在时要设置的值。如果该键确实存在,方法就会返回键的值。在交互式环境中输入以下代码:\n", 212 | "```\n", 213 | ">>> spam = {'name': 'Pooka', 'age': 5}\n", 214 | ">>> spam.setdefault('color', 'black')\n", 215 | "'black'\n", 216 | ">>> spam\n", 217 | "{'color': 'black', 'age': 5, 'name': 'Pooka'}\n", 218 | ">>> spam.setdefault('color', 'white')\n", 219 | "'black'\n", 220 | ">>> spam\n", 221 | "{'color': 'black', 'age': 5, 'name': 'Pooka'}\n", 222 | "```\n", 223 | "第一次调用setdefault()时,spam变量中的字典变为{'color': 'black', 'age': 5, 'name': 'Pooka'}。该方法返回值'black',因为现在该值被赋给键'color'。当spam.setdefault('color', 'white')接下来被调用时,该键的值“没有”被改变成'white',因为spam变量已经有名为'color'的键。\n", 224 | "\n", 225 | "setdefault()方法是一个很好的快捷方式,可以确保一个键存在。下面有一个小程序,计算一个字符串中每个字符出现的次数。打开一个文件编辑器窗口,输入以下代码,保存为characterCount.py.\n" 226 | ] 227 | }, 228 | { 229 | "cell_type": "code", 230 | "execution_count": 8, 231 | "metadata": { 232 | "collapsed": false 233 | }, 234 | "outputs": [ 235 | { 236 | "name": "stdout", 237 | "output_type": "stream", 238 | "text": [ 239 | "I\n", 240 | "t\n", 241 | " \n", 242 | "w\n", 243 | "a\n", 244 | "s\n", 245 | " \n", 246 | "a\n", 247 | " \n", 248 | "b\n", 249 | "r\n", 250 | "i\n", 251 | "g\n", 252 | "h\n", 253 | "t\n", 254 | " \n", 255 | "c\n", 256 | "o\n", 257 | "l\n", 258 | "d\n", 259 | " \n", 260 | "d\n", 261 | "a\n", 262 | "y\n", 263 | " \n", 264 | "i\n", 265 | "n\n", 266 | " \n", 267 | "A\n", 268 | "p\n", 269 | "r\n", 270 | "i\n", 271 | "l\n", 272 | ",\n", 273 | " \n", 274 | "a\n", 275 | "n\n", 276 | "d\n", 277 | " \n", 278 | "t\n", 279 | "h\n", 280 | "e\n", 281 | " \n", 282 | "c\n", 283 | "l\n", 284 | "o\n", 285 | "c\n", 286 | "k\n", 287 | "s\n", 288 | " \n", 289 | "w\n", 290 | "e\n", 291 | "r\n", 292 | "e\n", 293 | " \n", 294 | "s\n", 295 | "t\n", 296 | "r\n", 297 | "i\n", 298 | "k\n", 299 | "i\n", 300 | "n\n", 301 | "g\n", 302 | " \n", 303 | "t\n", 304 | "h\n", 305 | "i\n", 306 | "r\n", 307 | "t\n", 308 | "e\n", 309 | "e\n", 310 | "n\n", 311 | ".\n" 312 | ] 313 | } 314 | ], 315 | "source": [ 316 | "message = 'It was a bright cold day in April, and the clocks were striking thirteen.'\n", 317 | "for cha in message:\n", 318 | " print (cha)" 319 | ] 320 | }, 321 | { 322 | "cell_type": "markdown", 323 | "metadata": {}, 324 | "source": [ 325 | "## 5.2 漂亮打印\n", 326 | "如果程序中导入pprint模块,就可以使用pprint()和pformat()函数,它们将“漂亮打印”一个字典的字。如果想要字典中表项的显示比print()的输出结果更干净,这就有用了。\n" 327 | ] 328 | }, 329 | { 330 | "cell_type": "code", 331 | "execution_count": 10, 332 | "metadata": { 333 | "collapsed": false 334 | }, 335 | "outputs": [ 336 | { 337 | "name": "stdout", 338 | "output_type": "stream", 339 | "text": [ 340 | "{' ': 13,\n", 341 | " ',': 1,\n", 342 | " '.': 1,\n", 343 | " 'A': 1,\n", 344 | " 'I': 1,\n", 345 | " 'a': 4,\n", 346 | " 'b': 1,\n", 347 | " 'c': 3,\n", 348 | " 'd': 3,\n", 349 | " 'e': 5,\n", 350 | " 'g': 2,\n", 351 | " 'h': 3,\n", 352 | " 'i': 6,\n", 353 | " 'k': 2,\n", 354 | " 'l': 3,\n", 355 | " 'n': 4,\n", 356 | " 'o': 2,\n", 357 | " 'p': 1,\n", 358 | " 'r': 5,\n", 359 | " 's': 3,\n", 360 | " 't': 6,\n", 361 | " 'w': 2,\n", 362 | " 'y': 1}\n" 363 | ] 364 | } 365 | ], 366 | "source": [ 367 | "import pprint\n", 368 | "message = 'It was a bright cold day in April, and the clocks were striking thirteen.'\n", 369 | "count = {}\n", 370 | "\n", 371 | "for character in message:\n", 372 | " count.setdefault(character, 0)\n", 373 | " count[character] = count[character] + 1\n", 374 | "\n", 375 | "pprint.pprint(count)" 376 | ] 377 | }, 378 | { 379 | "cell_type": "markdown", 380 | "metadata": {}, 381 | "source": [ 382 | "如果字典本身包含嵌套的列表或字典,pprint.pprint()函数就特别有用。\n", 383 | "\n", 384 | "如果希望得到漂亮打印的文本作为字符串,而不是显示在屏幕上,那就调用pprint.pformat()。下面两行代码是等价的:" 385 | ] 386 | }, 387 | { 388 | "cell_type": "code", 389 | "execution_count": 16, 390 | "metadata": { 391 | "collapsed": false 392 | }, 393 | "outputs": [ 394 | { 395 | "name": "stdout", 396 | "output_type": "stream", 397 | "text": [ 398 | "{'data': [{'lat': -34.43778387240597,\n", 399 | " 'lon': 150.04799169921876,\n", 400 | " 'type': 'locale'},\n", 401 | " {'lat': -34.96615974838191, 'lon': 149.89967626953126, 'type': 'poi'},\n", 402 | " {'lat': -34.72271328279892,\n", 403 | " 'lon': 150.46547216796876,\n", 404 | " 'type': 'locale'},\n", 405 | " {'lat': -34.67303411621243,\n", 406 | " 'lon': 149.96559423828126,\n", 407 | " 'type': 'poi'}]}\n", 408 | "{'data': [{'lat': -34.43778387240597,\n", 409 | " 'lon': 150.04799169921876,\n", 410 | " 'type': 'locale'},\n", 411 | " {'lat': -34.96615974838191, 'lon': 149.89967626953126, 'type': 'poi'},\n", 412 | " {'lat': -34.72271328279892,\n", 413 | " 'lon': 150.46547216796876,\n", 414 | " 'type': 'locale'},\n", 415 | " {'lat': -34.67303411621243,\n", 416 | " 'lon': 149.96559423828126,\n", 417 | " 'type': 'poi'}]}\n" 418 | ] 419 | } 420 | ], 421 | "source": [ 422 | "someDictionaryValue = {\"data\": [{\"type\": \"locale\", \"lat\": -34.43778387240597, \"lon\": 150.04799169921876},\n", 423 | "{\"type\": \"poi\", \"lat\": -34.96615974838191, \"lon\": 149.89967626953126},\n", 424 | "{\"type\": \"locale\", \"lat\": -34.72271328279892, \"lon\": 150.46547216796876},\n", 425 | "{\"type\": \"poi\", \"lat\": -34.67303411621243, \"lon\": 149.96559423828126}]}\n", 426 | "\n", 427 | "pprint.pprint(someDictionaryValue)\n", 428 | "print (pprint.pformat(someDictionaryValue))" 429 | ] 430 | }, 431 | { 432 | "cell_type": "markdown", 433 | "metadata": {}, 434 | "source": [ 435 | "## 5.3 使用数据结构对真实世界建模\n", 436 | "\n", 437 | "### 5.3.1 井字棋盘\n", 438 | "井字棋盘看起来像一个大的井字符号(#),有9个空格,可以包含X、O或空。要用字典表示棋盘,可以为每个空格分配一个字符串键.\n", 439 | "\n", 440 | "可以用字符串值来表示,棋盘上每个空格有什么:'X'、'O'或' '(空格字符)。因此,需要存储9个字符串。可以用一个字典来做这事。带有键'top-R'的字符串表示右上角,带有键'low-L'的字符串表示左下角,带有键'mid-M'的字符串表示中间,以此类推。\n", 441 | "\n", 442 | "这个字典就是表示井字棋盘的数据结构。将这个字典表示的棋盘保存在名为theBoard的变量中。" 443 | ] 444 | }, 445 | { 446 | "cell_type": "code", 447 | "execution_count": 18, 448 | "metadata": { 449 | "collapsed": false 450 | }, 451 | "outputs": [ 452 | { 453 | "name": "stdout", 454 | "output_type": "stream", 455 | "text": [ 456 | " | | \n", 457 | "-+-+-\n", 458 | " | | \n", 459 | "-+-+-\n", 460 | " | | \n" 461 | ] 462 | } 463 | ], 464 | "source": [ 465 | "theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',\n", 466 | " 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',\n", 467 | " 'low-L': ' ', 'low-M': ' ', 'low-R': ' '}\n", 468 | "\n", 469 | "def printBoard(board):\n", 470 | " print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])\n", 471 | " print('-+-+-')\n", 472 | " print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])\n", 473 | " print('-+-+-')\n", 474 | " print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])\n", 475 | "printBoard(theBoard)" 476 | ] 477 | }, 478 | { 479 | "cell_type": "code", 480 | "execution_count": 20, 481 | "metadata": { 482 | "collapsed": false 483 | }, 484 | "outputs": [ 485 | { 486 | "name": "stdout", 487 | "output_type": "stream", 488 | "text": [ 489 | "O|O|O\n", 490 | "-+-+-\n", 491 | "X|X| \n", 492 | "-+-+-\n", 493 | " | |X\n" 494 | ] 495 | } 496 | ], 497 | "source": [ 498 | "theBoard = {'top-L': 'O', 'top-M': 'O', 'top-R': 'O', 'mid-L': 'X', 'mid-M':\n", 499 | "'X', 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': 'X'}\n", 500 | "\n", 501 | "def printBoard(board):\n", 502 | " print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])\n", 503 | " print('-+-+-')\n", 504 | " print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])\n", 505 | " print('-+-+-')\n", 506 | " print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])\n", 507 | "printBoard(theBoard)" 508 | ] 509 | }, 510 | { 511 | "cell_type": "markdown", 512 | "metadata": {}, 513 | "source": [ 514 | "因为你创建了一个数据结构来表示井字棋盘,编写了printBoard()中的代码来解释该数据结构,所以就有了一个程序,对井字棋盘进行了“建模”。也可以用不同的方式组织数据结构(例如,使用'TOP-LEFT'这样的键来代替'top-L'),但只要代码能处理你的数据结构,就有了正确工作的程序。\n", 515 | "\n", 516 | "例如,printBoard()函数预期井字棋数据结构是一个字典,包含所有9个空格的键。假如传入的字典缺少'mid-L'键,程序就不能工作了。" 517 | ] 518 | }, 519 | { 520 | "cell_type": "code", 521 | "execution_count": 22, 522 | "metadata": { 523 | "collapsed": false 524 | }, 525 | "outputs": [ 526 | { 527 | "name": "stdout", 528 | "output_type": "stream", 529 | "text": [ 530 | "gold coin\n", 531 | "dagger\n", 532 | "gold coin\n", 533 | "gold coin\n", 534 | "ruby\n" 535 | ] 536 | } 537 | ], 538 | "source": [ 539 | "dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby']\n", 540 | "for i in dragonLoot:\n", 541 | " print (i)" 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": 24, 547 | "metadata": { 548 | "collapsed": false 549 | }, 550 | "outputs": [ 551 | { 552 | "name": "stdout", 553 | "output_type": "stream", 554 | "text": [ 555 | "gold coin 42\n", 556 | "rope 1\n" 557 | ] 558 | } 559 | ], 560 | "source": [ 561 | "inv = {'gold coin': 42, 'rope': 1}\n", 562 | "\n", 563 | "for k, v in inv.items():\n", 564 | " print (k, v)" 565 | ] 566 | }, 567 | { 568 | "cell_type": "code", 569 | "execution_count": null, 570 | "metadata": { 571 | "collapsed": true 572 | }, 573 | "outputs": [], 574 | "source": [] 575 | } 576 | ], 577 | "metadata": { 578 | "anaconda-cloud": {}, 579 | "kernelspec": { 580 | "display_name": "Python [conda env:py35]", 581 | "language": "python", 582 | "name": "conda-env-py35-py" 583 | }, 584 | "language_info": { 585 | "codemirror_mode": { 586 | "name": "ipython", 587 | "version": 3 588 | }, 589 | "file_extension": ".py", 590 | "mimetype": "text/x-python", 591 | "name": "python", 592 | "nbconvert_exporter": "python", 593 | "pygments_lexer": "ipython3", 594 | "version": "3.5.2" 595 | } 596 | }, 597 | "nbformat": 4, 598 | "nbformat_minor": 1 599 | } 600 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/Practice_Projects/Fantasy_Game_Inventory.py: -------------------------------------------------------------------------------- 1 | #Fantasy Game Inventory 2 | 3 | def display_inventory(inventory): 4 | total = 0 5 | print ('Inventory:') 6 | for k, v in inventory.items(): 7 | print (str(v)+' ' + k) 8 | total += v 9 | print ('Total number of items: '+ str(total)) 10 | 11 | 12 | 13 | if __name__ == '__main__': 14 | inventory = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} 15 | display_inventory(inventory) 16 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/Practice_Projects/Fantasy_Game_Inventory_dragon.py: -------------------------------------------------------------------------------- 1 | #Fantasy Game Inventory 2 | 3 | def display_inventory(inventory): 4 | total = 0 5 | print ('Inventory:') 6 | for k, v in inventory.items(): 7 | print (str(v)+' ' + k) 8 | total += v 9 | print ('Total number of items: '+ str(total)) 10 | 11 | def add_to_inventory(inventory, addedItems): 12 | for item in addedItems: 13 | inventory.setdefault(str(item), 0) 14 | inventory[item] += 1 15 | 16 | if __name__ == '__main__': 17 | inv = {'gold coin': 42, 'rope': 1} 18 | dragonLoot = ['gold coin', 'dagger', 'gold coin', 'gold coin', 'ruby'] 19 | add_to_inventory(inv, dragonLoot) 20 | display_inventory(inv) 21 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/birthdays.py: -------------------------------------------------------------------------------- 1 | birthdays = {'Alice': 'Apr 1', 'Bob': 'Dec 12', 'Carol': 'Mar 4'} 2 | 3 | while True: 4 | print ('Enter a name: (blank to quit)') 5 | name = input() 6 | if name == '': 7 | break 8 | if name in birthdays: 9 | print (birthdays[name] + ' is the birthdays name of ' + name) 10 | else: 11 | print ('I do not have birthday infomation for ' + name) 12 | print ('What is their birthday?') 13 | bday = input() 14 | birthdays[name] = bday 15 | print ('Birthday database updated.') 16 | 17 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/character_count.py: -------------------------------------------------------------------------------- 1 | message = 'It was a bright cold day in April, and the clocks were striking thirteen.' 2 | 3 | count = {} 4 | 5 | for character in message: 6 | count.setdefault(character, 0) 7 | count[character] = count[character] + 1 8 | 9 | print (count) 10 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/pretty_character_count.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | 3 | message = 'It was a bright cold day in April, and the clocks were striking thirteen.' 4 | 5 | count = {} 6 | 7 | for character in message: 8 | count.setdefault(character, 0) 9 | count[character] = count[character] + 1 10 | 11 | pprint.pprint (count) 12 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/ticTacToe.py: -------------------------------------------------------------------------------- 1 | theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 'mid-L': ' ', 'mid-M': ' ',\ 2 | 'mid-R': ' ', 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} 3 | 4 | def printBoard(board): 5 | print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) 6 | print('-+-+-') 7 | print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) 8 | print('-+-+-') 9 | print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) 10 | 11 | 12 | turn = 'X' 13 | for i in range(9): 14 | printBoard(theBoard) 15 | print ('Turn for ' + turn + '. Move on which space?') 16 | move = input() 17 | theBoard[move] = turn 18 | if turn == 'X': 19 | turn = '0' 20 | else: 21 | turn = 'X' 22 | printBoard(theBoard) 23 | -------------------------------------------------------------------------------- /05_Dictionaries_and_Structuring_Data/total_brought.py: -------------------------------------------------------------------------------- 1 | allGuests = { 2 | 'Alice': {'apple': 5, 'pretzels': 12}, 3 | 'Bob': {'ham sandwiches': 3, 'apples': 2}, 4 | 'Carol': {'cups': 3, 'apple pies': 1} 5 | } 6 | 7 | def totalBrought(guests, item): 8 | numBrought = 0 9 | for k, v in guests.items(): 10 | numBrought = numBrought + v.get(item, 0) 11 | return numBrought 12 | 13 | print('Number of things being brought:') 14 | print(' - Apples ' + str(totalBrought(allGuests, 'apples'))) 15 | print(' - Cups ' + str(totalBrought(allGuests, 'cups'))) 16 | print(' - Cakes ' + str(totalBrought(allGuests, 'cakes'))) 17 | print(' - Ham Sandwiches ' + str(totalBrought(allGuests, 'ham sandwiches'))) 18 | print(' - Apple Pies ' + str(totalBrought(allGuests, 'apple pies'))) 19 | -------------------------------------------------------------------------------- /06_Manipulating_Strings/6_Strings.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### 6.1.6 多行注释\n", 8 | "虽然井号字符(#)表示这一行是注释,但多行字符串常常用作多行注释。下面是完全有效的Python代码:\n", 9 | "```\n", 10 | "\"\"\"This is a test Python program.\n", 11 | "Written by Al Sweigart al@inventwithpython.com\n", 12 | "\n", 13 | "This program was designed for Python 3, not Python 2.\n", 14 | "\"\"\"\n", 15 | "\n", 16 | "def spam():\n", 17 | " \"\"\"This is a multiline comment to help\n", 18 | " explain what the spam() function does.\"\"\"\n", 19 | " print('Hello!')\n", 20 | "```\n", 21 | "\n", 22 | "### 6.1.8 字符串的in和not in操作符\n", 23 | "像列表一样,in和not in操作符也可以用于字符串。用in或not in连接两个字符串得到的表达式,将求值为布尔值True或False。在交互式环境中输入以下代码:\n", 24 | "```\n", 25 | ">>> 'Hello' in 'Hello World'\n", 26 | "True\n", 27 | ">>> 'Hello' in 'Hello'\n", 28 | "True\n", 29 | ">>> 'HELLO' in 'Hello World'\n", 30 | "False\n", 31 | ">>> '' in 'spam'\n", 32 | "True\n", 33 | ">>> 'cats' not in 'cats and dogs'\n", 34 | "False\n", 35 | "```\n", 36 | "## 6.2 有用的字符串方法\n", 37 | "### 6.2.2 isX字符串方法\n", 38 | "除了islower()和isupper(),还有几个字符串方法,它们的名字以is开始。这些方法返回一个布尔值,描述了字符串的特点。下面是一些常用的isX字符串方法:\n", 39 | "\n", 40 | "- isalpha()返回True,如果字符串只包含字母,并且非空;\n", 41 | "- isalnum()返回True,如果字符串只包含字母和数字,并且非空;\n", 42 | "- isdecimal()返回True,如果字符串只包含数字字符,并且非空;\n", 43 | "- isspace()返回True,如果字符串只包含空格、制表符和换行,并且非空;\n", 44 | "- .istitle()返回True,如果字符串仅包含以大写字母开头、后面都是小写字母的单词。\n", 45 | "\n", 46 | "### 6.2.4 字符串方法join()和split()\n", 47 | "如果有一个字符串列表,需要将它们连接起来,成为一个单独的字符串,join()方法就很有用。join()方法在一个字符串上调用,参数是一个字符串列表,返回一个字符串。返回的字符串由传入的列表中每个字符串连接而成。\n", 48 | "\n", 49 | "把list变为string的方法\n", 50 | "\n", 51 | "请注意,调用join()方法的字符串,被插入到列表参数中每个字符串的中间。例如,如果在', '字符串上调用join(['cats', 'rats', 'bats']),返回的字符串就是'cats, rats, bats'。\n", 52 | "\n", 53 | "要记住,join()方法是针对一个字符串而调用的,并且传入一个列表值(很容易不小心用其他的方式调用它)。" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 1, 59 | "metadata": { 60 | "collapsed": false 61 | }, 62 | "outputs": [ 63 | { 64 | "data": { 65 | "text/plain": [ 66 | "'cats, rats, bats'" 67 | ] 68 | }, 69 | "execution_count": 1, 70 | "metadata": {}, 71 | "output_type": "execute_result" 72 | } 73 | ], 74 | "source": [ 75 | "', '.join(['cats', 'rats', 'bats'])" 76 | ] 77 | }, 78 | { 79 | "cell_type": "code", 80 | "execution_count": 2, 81 | "metadata": { 82 | "collapsed": false 83 | }, 84 | "outputs": [ 85 | { 86 | "data": { 87 | "text/plain": [ 88 | "'my name is Simon'" 89 | ] 90 | }, 91 | "execution_count": 2, 92 | "metadata": {}, 93 | "output_type": "execute_result" 94 | } 95 | ], 96 | "source": [ 97 | "' '.join(['my', 'name', 'is', 'Simon'])" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "**split()方法做的事情正好相反:它针对一个字符串调用,返回一个字符串列表。**\n", 105 | "\n", 106 | "返回的是list!!!" 107 | ] 108 | }, 109 | { 110 | "cell_type": "code", 111 | "execution_count": 3, 112 | "metadata": { 113 | "collapsed": false 114 | }, 115 | "outputs": [ 116 | { 117 | "data": { 118 | "text/plain": [ 119 | "['My', 'name', 'is', 'Simon']" 120 | ] 121 | }, 122 | "execution_count": 3, 123 | "metadata": {}, 124 | "output_type": "execute_result" 125 | } 126 | ], 127 | "source": [ 128 | "'My name is Simon'.split()" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "默认情况下,字符串'My name is Simon'按照各种空白字符分割,诸如空格、制表符或换行符。这些空白字符不包含在返回列表的字符串中。也可以向split()方法传入一个分割字符串,指定它按照不同的字符串分割。" 136 | ] 137 | }, 138 | { 139 | "cell_type": "code", 140 | "execution_count": 4, 141 | "metadata": { 142 | "collapsed": false 143 | }, 144 | "outputs": [ 145 | { 146 | "data": { 147 | "text/plain": [ 148 | "['My', 'name', 'is', 'Simon']" 149 | ] 150 | }, 151 | "execution_count": 4, 152 | "metadata": {}, 153 | "output_type": "execute_result" 154 | } 155 | ], 156 | "source": [ 157 | "'MyABCnameABCisABCSimon'.split('ABC')" 158 | ] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "execution_count": 5, 163 | "metadata": { 164 | "collapsed": false 165 | }, 166 | "outputs": [ 167 | { 168 | "data": { 169 | "text/plain": [ 170 | "['My na', 'e is Si', 'on']" 171 | ] 172 | }, 173 | "execution_count": 5, 174 | "metadata": {}, 175 | "output_type": "execute_result" 176 | } 177 | ], 178 | "source": [ 179 | "'My name is Simon'.split('m')" 180 | ] 181 | }, 182 | { 183 | "cell_type": "markdown", 184 | "metadata": {}, 185 | "source": [ 186 | "一个常见的split()用法,是按照换行符分割多行字符串。\n", 187 | "\n", 188 | "向split()方法传入参数’\\n’,我们按照换行符分割变量中存储的多行字符串,返回列表中的每个表项,对应于字符串中的一行。" 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "execution_count": 6, 194 | "metadata": { 195 | "collapsed": false 196 | }, 197 | "outputs": [ 198 | { 199 | "data": { 200 | "text/plain": [ 201 | "['Dear Alice,',\n", 202 | " 'How have you been? I am fine.',\n", 203 | " 'There is a container in the fridge',\n", 204 | " 'that is labeled \"Milk Experiment\".',\n", 205 | " '\\u3000',\n", 206 | " 'Please do not drink it.',\n", 207 | " 'Sincerely,',\n", 208 | " 'Bob']" 209 | ] 210 | }, 211 | "execution_count": 6, 212 | "metadata": {}, 213 | "output_type": "execute_result" 214 | } 215 | ], 216 | "source": [ 217 | "spam = '''Dear Alice,\n", 218 | "How have you been? I am fine.\n", 219 | "There is a container in the fridge\n", 220 | "that is labeled \"Milk Experiment\".\n", 221 | " \n", 222 | "Please do not drink it.\n", 223 | "Sincerely,\n", 224 | "Bob'''\n", 225 | "\n", 226 | "spam.split('\\n')" 227 | ] 228 | }, 229 | { 230 | "cell_type": "markdown", 231 | "metadata": {}, 232 | "source": [ 233 | "### 6.2.5 用rjust()、ljust()和center()方法对齐文本\n", 234 | "rjust()和ljust()字符串方法返回调用它们的字符串的填充版本,通过插入空格来对齐文本。这两个方法的第一个参数是一个整数长度,用于对齐字符串。" 235 | ] 236 | }, 237 | { 238 | "cell_type": "code", 239 | "execution_count": 7, 240 | "metadata": { 241 | "collapsed": false 242 | }, 243 | "outputs": [ 244 | { 245 | "data": { 246 | "text/plain": [ 247 | "' Hello'" 248 | ] 249 | }, 250 | "execution_count": 7, 251 | "metadata": {}, 252 | "output_type": "execute_result" 253 | } 254 | ], 255 | "source": [ 256 | "'Hello'.rjust(10)" 257 | ] 258 | }, 259 | { 260 | "cell_type": "code", 261 | "execution_count": 8, 262 | "metadata": { 263 | "collapsed": false 264 | }, 265 | "outputs": [ 266 | { 267 | "data": { 268 | "text/plain": [ 269 | "' Hello'" 270 | ] 271 | }, 272 | "execution_count": 8, 273 | "metadata": {}, 274 | "output_type": "execute_result" 275 | } 276 | ], 277 | "source": [ 278 | "'Hello'.rjust(20)" 279 | ] 280 | }, 281 | { 282 | "cell_type": "code", 283 | "execution_count": 9, 284 | "metadata": { 285 | "collapsed": false 286 | }, 287 | "outputs": [ 288 | { 289 | "data": { 290 | "text/plain": [ 291 | "' Hello World'" 292 | ] 293 | }, 294 | "execution_count": 9, 295 | "metadata": {}, 296 | "output_type": "execute_result" 297 | } 298 | ], 299 | "source": [ 300 | "'Hello World'.rjust(20)" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 10, 306 | "metadata": { 307 | "collapsed": false 308 | }, 309 | "outputs": [ 310 | { 311 | "data": { 312 | "text/plain": [ 313 | "'Hello '" 314 | ] 315 | }, 316 | "execution_count": 10, 317 | "metadata": {}, 318 | "output_type": "execute_result" 319 | } 320 | ], 321 | "source": [ 322 | "'Hello'.ljust(10)" 323 | ] 324 | }, 325 | { 326 | "cell_type": "markdown", 327 | "metadata": {}, 328 | "source": [ 329 | "'Hello'.rjust(10)是说我们希望右对齐,将'Hello'放在一个长度为10的字符串中。'Hello'有5个字符,所以左边会加上5个空格,得到一个10个字符的字符串,实现'Hello'右对齐。\n", 330 | "\n", 331 | "rjust()和ljust()方法的第二个可选参数将指定一个填充字符,取代空格字符。" 332 | ] 333 | }, 334 | { 335 | "cell_type": "code", 336 | "execution_count": 11, 337 | "metadata": { 338 | "collapsed": false 339 | }, 340 | "outputs": [ 341 | { 342 | "data": { 343 | "text/plain": [ 344 | "'XXXXXXXXXXXXXXXHello'" 345 | ] 346 | }, 347 | "execution_count": 11, 348 | "metadata": {}, 349 | "output_type": "execute_result" 350 | } 351 | ], 352 | "source": [ 353 | "'Hello'.rjust(20, 'X')" 354 | ] 355 | }, 356 | { 357 | "cell_type": "code", 358 | "execution_count": 12, 359 | "metadata": { 360 | "collapsed": false 361 | }, 362 | "outputs": [ 363 | { 364 | "data": { 365 | "text/plain": [ 366 | "'Hello---------------'" 367 | ] 368 | }, 369 | "execution_count": 12, 370 | "metadata": {}, 371 | "output_type": "execute_result" 372 | } 373 | ], 374 | "source": [ 375 | "'Hello'.ljust(20, '-')" 376 | ] 377 | }, 378 | { 379 | "cell_type": "code", 380 | "execution_count": 13, 381 | "metadata": { 382 | "collapsed": false 383 | }, 384 | "outputs": [ 385 | { 386 | "data": { 387 | "text/plain": [ 388 | "' Hello '" 389 | ] 390 | }, 391 | "execution_count": 13, 392 | "metadata": {}, 393 | "output_type": "execute_result" 394 | } 395 | ], 396 | "source": [ 397 | "# center \n", 398 | "'Hello'.center(20)" 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": 14, 404 | "metadata": { 405 | "collapsed": false 406 | }, 407 | "outputs": [ 408 | { 409 | "data": { 410 | "text/plain": [ 411 | "'=======Hello========'" 412 | ] 413 | }, 414 | "execution_count": 14, 415 | "metadata": {}, 416 | "output_type": "execute_result" 417 | } 418 | ], 419 | "source": [ 420 | "'Hello'.center(20, '=')" 421 | ] 422 | }, 423 | { 424 | "cell_type": "markdown", 425 | "metadata": {}, 426 | "source": [ 427 | "如果需要打印表格式数据,留出正确的空格,这些方法就特别有用。打开一个新的文件编辑器窗口,输入以下代码,并保存为picnicTable.py:" 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": 18, 433 | "metadata": { 434 | "collapsed": false 435 | }, 436 | "outputs": [ 437 | { 438 | "name": "stdout", 439 | "output_type": "stream", 440 | "text": [ 441 | "---PICNIC ITEMS--\n", 442 | "cookies..... 8000\n", 443 | "cups........ 4\n", 444 | "apples...... 12\n", 445 | "sandwiches.. 4\n", 446 | "-------PICNIC ITEMS-------\n", 447 | "cookies............. 8000\n", 448 | "cups................ 4\n", 449 | "apples.............. 12\n", 450 | "sandwiches.......... 4\n" 451 | ] 452 | } 453 | ], 454 | "source": [ 455 | "def printPicnic(itemDict, leftWidth, rightWidth):\n", 456 | " print ('PICNIC ITEMS'.center(leftWidth + rightWidth, '-'))\n", 457 | " for k, v in itemDict.items():\n", 458 | " print (k.ljust(leftWidth, '.') + str(v).rjust(rightWidth))\n", 459 | "\n", 460 | "picnicItems = {\n", 461 | " 'sandwiches': 4,\n", 462 | " 'apples': 12,\n", 463 | " 'cups': 4,\n", 464 | " 'cookies': 8000\n", 465 | "}\n", 466 | "\n", 467 | "printPicnic(picnicItems, 12, 5)\n", 468 | "printPicnic(picnicItems, 20, 6)" 469 | ] 470 | }, 471 | { 472 | "cell_type": "markdown", 473 | "metadata": {}, 474 | "source": [ 475 | "### 6.2.6 用strip()、rstrip()和lstrip()删除空白字符\n", 476 | "有时候你希望删除字符串左边、右边或两边的空白字符(空格、制表符和换行符)。strip()字符串方法将**返回一个新的字符串**,它的开头或末尾都没有空白字符。lstrip()和rstrip()方法将相应删除左边或右边的空白字符。" 477 | ] 478 | }, 479 | { 480 | "cell_type": "code", 481 | "execution_count": 19, 482 | "metadata": { 483 | "collapsed": false 484 | }, 485 | "outputs": [ 486 | { 487 | "data": { 488 | "text/plain": [ 489 | "'Hello World'" 490 | ] 491 | }, 492 | "execution_count": 19, 493 | "metadata": {}, 494 | "output_type": "execute_result" 495 | } 496 | ], 497 | "source": [ 498 | "spam = ' Hello World '\n", 499 | "spam.strip()" 500 | ] 501 | }, 502 | { 503 | "cell_type": "code", 504 | "execution_count": 20, 505 | "metadata": { 506 | "collapsed": false 507 | }, 508 | "outputs": [ 509 | { 510 | "data": { 511 | "text/plain": [ 512 | "' Hello World '" 513 | ] 514 | }, 515 | "execution_count": 20, 516 | "metadata": {}, 517 | "output_type": "execute_result" 518 | } 519 | ], 520 | "source": [ 521 | "spam" 522 | ] 523 | }, 524 | { 525 | "cell_type": "code", 526 | "execution_count": 21, 527 | "metadata": { 528 | "collapsed": false 529 | }, 530 | "outputs": [ 531 | { 532 | "data": { 533 | "text/plain": [ 534 | "'Hello World '" 535 | ] 536 | }, 537 | "execution_count": 21, 538 | "metadata": {}, 539 | "output_type": "execute_result" 540 | } 541 | ], 542 | "source": [ 543 | "spam.lstrip()" 544 | ] 545 | }, 546 | { 547 | "cell_type": "code", 548 | "execution_count": 22, 549 | "metadata": { 550 | "collapsed": false 551 | }, 552 | "outputs": [ 553 | { 554 | "data": { 555 | "text/plain": [ 556 | "' Hello World'" 557 | ] 558 | }, 559 | "execution_count": 22, 560 | "metadata": {}, 561 | "output_type": "execute_result" 562 | } 563 | ], 564 | "source": [ 565 | "spam.rstrip()" 566 | ] 567 | }, 568 | { 569 | "cell_type": "markdown", 570 | "metadata": {}, 571 | "source": [ 572 | "有一个可选的字符串参数,指定两边的哪些字符应该删除。" 573 | ] 574 | }, 575 | { 576 | "cell_type": "code", 577 | "execution_count": 23, 578 | "metadata": { 579 | "collapsed": false 580 | }, 581 | "outputs": [ 582 | { 583 | "data": { 584 | "text/plain": [ 585 | "'BaconSpamEggs'" 586 | ] 587 | }, 588 | "execution_count": 23, 589 | "metadata": {}, 590 | "output_type": "execute_result" 591 | } 592 | ], 593 | "source": [ 594 | "spam = 'SpamSpamBaconSpamEggsSpamSpam'\n", 595 | "spam.strip('ampS')" 596 | ] 597 | }, 598 | { 599 | "cell_type": "markdown", 600 | "metadata": {}, 601 | "source": [ 602 | "向strip()方法传入参数'ampS',告诉它在变量中存储的字符串两端,删除出现的a、m、p和大写的S。传入strip()方法的字符串中,字符的顺序并不重要:strip('ampS')做的事情和strip('mapS')或strip('Spam')一样。\n", 603 | "\n" 604 | ] 605 | }, 606 | { 607 | "cell_type": "markdown", 608 | "metadata": {}, 609 | "source": [ 610 | "### 6.2.7 用pyperclip模块拷贝粘贴字符串\n", 611 | "pyperclip模块有copy()和paste()函数,可以向计算机的剪贴板发送文本,或从它接收文本。将程序的输出发送到剪贴板,使它很容易粘贴到邮件、文字处理程序或其他软件中。pyperclip模块不是Python自带的。要安装它,请遵从附录A中安装第三方模块的指南。" 612 | ] 613 | }, 614 | { 615 | "cell_type": "code", 616 | "execution_count": 26, 617 | "metadata": { 618 | "collapsed": false 619 | }, 620 | "outputs": [], 621 | "source": [ 622 | "import pyperclip\n", 623 | "pyperclip.copy('Hello world!')" 624 | ] 625 | }, 626 | { 627 | "cell_type": "code", 628 | "execution_count": 27, 629 | "metadata": { 630 | "collapsed": false 631 | }, 632 | "outputs": [ 633 | { 634 | "data": { 635 | "text/plain": [ 636 | "'Hello world!'" 637 | ] 638 | }, 639 | "execution_count": 27, 640 | "metadata": {}, 641 | "output_type": "execute_result" 642 | } 643 | ], 644 | "source": [ 645 | "pyperclip.paste()" 646 | ] 647 | }, 648 | { 649 | "cell_type": "code", 650 | "execution_count": 29, 651 | "metadata": { 652 | "collapsed": false 653 | }, 654 | "outputs": [ 655 | { 656 | "data": { 657 | "text/plain": [ 658 | "\"'For example, if I copied this sentence to the clipboard and then called\\npaste(), it would look like this:'\"" 659 | ] 660 | }, 661 | "execution_count": 29, 662 | "metadata": {}, 663 | "output_type": "execute_result" 664 | } 665 | ], 666 | "source": [ 667 | "pyperclip.paste()" 668 | ] 669 | }, 670 | { 671 | "cell_type": "markdown", 672 | "metadata": {}, 673 | "source": [ 674 | "## 6.3 项目:口令保管箱\n" 675 | ] 676 | }, 677 | { 678 | "cell_type": "code", 679 | "execution_count": 32, 680 | "metadata": { 681 | "collapsed": false 682 | }, 683 | "outputs": [ 684 | { 685 | "name": "stdout", 686 | "output_type": "stream", 687 | "text": [ 688 | "The command line arguemts are:\n", 689 | "/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/ipykernel/__main__.py\n", 690 | "-f\n", 691 | "/Users/xu/Library/Jupyter/runtime/kernel-85534567-33ba-4e0f-8215-eb61fedbaecf.json\n", 692 | "\n", 693 | "\n", 694 | "The PYTHONPATH is ['', '/Users/xu/anaconda/envs/py35/lib/python35.zip', '/Users/xu/anaconda/envs/py35/lib/python3.5', '/Users/xu/anaconda/envs/py35/lib/python3.5/plat-darwin', '/Users/xu/anaconda/envs/py35/lib/python3.5/lib-dynload', '/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages', '/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/Sphinx-1.4.6-py3.5.egg', '/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/aeosa', '/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/xgboost-0.6-py3.5.egg', '/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/setuptools-27.2.0-py3.5.egg', '/Users/xu/anaconda/envs/py35/lib/python3.5/site-packages/IPython/extensions', '/Users/xu/.ipython'] \n", 695 | "\n" 696 | ] 697 | } 698 | ], 699 | "source": [ 700 | "import sys\n", 701 | "\n", 702 | "print ('The command line arguemts are:')\n", 703 | "for i in sys.argv:\n", 704 | " print (i)\n", 705 | " \n", 706 | "print ('\\n\\nThe PYTHONPATH is', sys.path, '\\n')" 707 | ] 708 | }, 709 | { 710 | "cell_type": "markdown", 711 | "metadata": {}, 712 | "source": [ 713 | "## 6.7 实践项目\n", 714 | "\n" 715 | ] 716 | }, 717 | { 718 | "cell_type": "code", 719 | "execution_count": 38, 720 | "metadata": { 721 | "collapsed": false 722 | }, 723 | "outputs": [ 724 | { 725 | "data": { 726 | "text/plain": [ 727 | "[0, 0, 0]" 728 | ] 729 | }, 730 | "execution_count": 38, 731 | "metadata": {}, 732 | "output_type": "execute_result" 733 | } 734 | ], 735 | "source": [ 736 | "tableData = [['apples', 'oranges', 'cherries', 'banana'],\n", 737 | " ['Alice', 'Bob', 'Carol', 'David'],\n", 738 | " ['dogs', 'cats', 'moose', 'goose']]\n", 739 | "\n", 740 | "[0] * len(tableData)" 741 | ] 742 | }, 743 | { 744 | "cell_type": "code", 745 | "execution_count": 45, 746 | "metadata": { 747 | "collapsed": false 748 | }, 749 | "outputs": [ 750 | { 751 | "name": "stdout", 752 | "output_type": "stream", 753 | "text": [ 754 | "[8, 5, 5]\n" 755 | ] 756 | } 757 | ], 758 | "source": [ 759 | "colWideths = [0] * len(tableData) \n", 760 | "for i in range(len(tableData)):\n", 761 | " for j in range(len(tableData[i])):\n", 762 | " if len(tableData[i][j]) > colWideths[i]:\n", 763 | " colWideths[i] = len(tableData[i][j])\n", 764 | " \n", 765 | " \n", 766 | "print (colWideths) " 767 | ] 768 | }, 769 | { 770 | "cell_type": "code", 771 | "execution_count": 41, 772 | "metadata": { 773 | "collapsed": false 774 | }, 775 | "outputs": [ 776 | { 777 | "data": { 778 | "text/plain": [ 779 | "6" 780 | ] 781 | }, 782 | "execution_count": 41, 783 | "metadata": {}, 784 | "output_type": "execute_result" 785 | } 786 | ], 787 | "source": [ 788 | "len('apples')" 789 | ] 790 | }, 791 | { 792 | "cell_type": "code", 793 | "execution_count": 54, 794 | "metadata": { 795 | "collapsed": false 796 | }, 797 | "outputs": [], 798 | "source": [ 799 | "\n", 800 | " " 801 | ] 802 | }, 803 | { 804 | "cell_type": "code", 805 | "execution_count": 55, 806 | "metadata": { 807 | "collapsed": false 808 | }, 809 | "outputs": [ 810 | { 811 | "name": "stdout", 812 | "output_type": "stream", 813 | "text": [ 814 | " apples Alice dogs \n", 815 | " oranges Bob cats \n", 816 | "cherries Carol moose \n", 817 | " banana David goose \n" 818 | ] 819 | } 820 | ], 821 | "source": [ 822 | "print_table(tableData, colWideths)" 823 | ] 824 | }, 825 | { 826 | "cell_type": "code", 827 | "execution_count": null, 828 | "metadata": { 829 | "collapsed": true 830 | }, 831 | "outputs": [], 832 | "source": [] 833 | } 834 | ], 835 | "metadata": { 836 | "anaconda-cloud": {}, 837 | "kernelspec": { 838 | "display_name": "Python [conda env:py35]", 839 | "language": "python", 840 | "name": "conda-env-py35-py" 841 | }, 842 | "language_info": { 843 | "codemirror_mode": { 844 | "name": "ipython", 845 | "version": 3 846 | }, 847 | "file_extension": ".py", 848 | "mimetype": "text/x-python", 849 | "name": "python", 850 | "nbconvert_exporter": "python", 851 | "pygments_lexer": "ipython3", 852 | "version": "3.5.2" 853 | } 854 | }, 855 | "nbformat": 4, 856 | "nbformat_minor": 1 857 | } 858 | -------------------------------------------------------------------------------- /06_Manipulating_Strings/Chapter_Projects/bulletPointAdder.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # Adds Wikipedia bullet points to the start 3 | # of each line of text on the clipboard. 4 | 5 | import pyperclip 6 | text = pyperclip.paste() 7 | #Separate lines and add stars. 8 | lines = text.split('\n') 9 | for i in range(len(lines)): # loop through all indexes for "lines" list 10 | lines[i] = '* ' + lines[i] # add star to each string in "lines" list 11 | text = '\n'.join(lines) 12 | pyperclip.copy(text) 13 | -------------------------------------------------------------------------------- /06_Manipulating_Strings/Chapter_Projects/bullet_point_adder_error.py: -------------------------------------------------------------------------------- 1 | """ 2 | 测试文件 3 | --- 4 | Lists of books 5 | Lists of 100 best books 6 | Lists of banned books 7 | Lists of The New York Times Fiction Best Sellers 8 | Lists of The New York Times Non-Fiction Best Sellers 9 | Publishers Weekly lists of bestselling novels in the United States 10 | --- 11 | 12 | bulletPointAdder.py脚本将从剪贴板中取得文本,在每一行开始处加上星号和空格,然后将这段新的文本贴回到剪贴板。 13 | 14 | 你希望bulletPointAdder.py程序完成下列事情: 15 | 16 | 1.从剪贴板粘贴文本; 17 | 18 | 2.对它做一些处理; 19 | 20 | 3.将新的文本复制到剪贴板。 21 | 22 | --- 23 | 程序运行错误,搞懂原因了。 24 | 用print (line)查看输出的时候,发现 25 | * Lists of books 26 | * Lists of 100 best books 27 | * Lists of banned books 28 | * Lists of The New York Times Fiction Best Sellers 29 | * Lists of The New York Times Non-Fiction Best Sellers 30 | * Publishers Weekly lists of bestselling novels in the United States 31 | 每一句前面都有星号。但最后在记事本里paste的时候,发现前面还是没有星号。 32 | 33 | 因为我用的是line = '* ' + line, 这个语句是把新的*line复制给了line 34 | 并没有对原先的list里的每一个单项产生改变。也就是之前的是否会改变list自身的问题。 35 | 所以说这里不能用for line in lines. 36 | 必须得用for i in range(len(lines)), 37 | 对list里的每一个单项lines[i]+‘*’,才能把list更新 38 | """ 39 | 40 | # bulletPointAdder.py - Add Wikipedia bullet points to the start 41 | # of each line of test on the clipboard 42 | 43 | import pyperclip 44 | 45 | text = pyperclip.paste() 46 | 47 | # TODO: Separate lines and add stars. 48 | lines = text.split('\n') 49 | for line in lines: 50 | line = '* ' + line 51 | print (line) 52 | text = '\n'.join(lines) 53 | 54 | pyperclip.copy(text) 55 | -------------------------------------------------------------------------------- /06_Manipulating_Strings/Chapter_Projects/pw.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/python 2 | #/Users/xu/anaconda/envs/py35/bin/python 3 | # pw.py - An insecure password locker program 4 | 5 | PASSWORDS = {'email': 'F7minlBDDuvMJuxESSKHFhTxFtjVB6', 6 | 'blog': 'VmALvQyKAxiVH5G8v01if1MLZF3sdt', 7 | 'luggage': '12345'} 8 | 9 | import sys, pyperclip 10 | if len(sys.argv) < 2: 11 | print ('Usage: python pw.py [account] - copy account password') 12 | sys.exit() 13 | 14 | account = sys.argv[1] # first command line arg is the account name 15 | 16 | if account in PASSWORDS: 17 | pyperclip.copy(PASSWORDS[account]) 18 | print ('Password for ' + account + ' copied to clipboard.') 19 | else: 20 | print ('There is no account named ' + account) 21 | -------------------------------------------------------------------------------- /06_Manipulating_Strings/Practice_Projects/table_printer.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | def column_wideth(tabel): 5 | colWideths = [0] * len(tabel) 6 | for i in range(len(tabel)): 7 | for j in range(len(tabel[i])): 8 | if len(tabel[i][j]) > colWideths[i]: 9 | colWideths[i] = len(tabel[i][j]) 10 | return colWideths 11 | 12 | 13 | 14 | def print_table(table, colWideths): 15 | for i in range(len(table[0])): # i = 4 meansing the output has 4 rows 16 | #print (i) 17 | for j in range(len(table)): # j = 3 to output each row 18 | print (table[j][i].rjust(colWideths[j]), end=' ') 19 | print () 20 | 21 | 22 | if __name__ == '__main__': 23 | tableData = [['apples', 'oranges', 'cherries', 'banana'], 24 | ['Alice', 'Bob', 'Carol', 'David'], 25 | ['dogs', 'cats', 'moose', 'goose']] 26 | 27 | colWideths = column_wideth(tableData) 28 | print_table(tableData, colWideths) 29 | -------------------------------------------------------------------------------- /06_Manipulating_Strings/picnic_table.py: -------------------------------------------------------------------------------- 1 | def printPicnic(itemDict, leftWidth, rightWidth): 2 | print ('PICNIC ITEMS'.center(leftWidth + rightWidth, '-')) 3 | for k, v in itemDict.items(): 4 | print (k.ljust(leftWidth, '.') + str(v).rjust(rightWidth)) 5 | 6 | picnicItems = { 7 | 'sandwiches': 4, 8 | 'apples': 12, 9 | 'cups': 4, 10 | 'cookies': 8000 11 | } 12 | 13 | printPicnic(picnicItems, 12, 5) 14 | printPicnic(picnicItems, 20, 6) 15 | -------------------------------------------------------------------------------- /07_Pattern_Matching_with_Regular_Expressions/7_regex.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 5, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [ 10 | { 11 | "data": { 12 | "text/plain": [ 13 | "'haha-this-is-a-test'" 14 | ] 15 | }, 16 | "execution_count": 5, 17 | "metadata": {}, 18 | "output_type": "execute_result" 19 | } 20 | ], 21 | "source": [ 22 | "'-'.join(['haha', 'this', 'is', 'a', 'test'])" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": 6, 28 | "metadata": { 29 | "collapsed": true 30 | }, 31 | "outputs": [], 32 | "source": [ 33 | "import re" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 18, 39 | "metadata": { 40 | "collapsed": false 41 | }, 42 | "outputs": [ 43 | { 44 | "data": { 45 | "text/plain": [ 46 | "' This is test string'" 47 | ] 48 | }, 49 | "execution_count": 18, 50 | "metadata": {}, 51 | "output_type": "execute_result" 52 | } 53 | ], 54 | "source": [ 55 | "string = ' This is a test string'\n", 56 | "rm_str = 'a'\n", 57 | "string_regex = re.compile(rm_str)\n", 58 | "string_regex.sub('', string)" 59 | ] 60 | }, 61 | { 62 | "cell_type": "code", 63 | "execution_count": 19, 64 | "metadata": { 65 | "collapsed": false 66 | }, 67 | "outputs": [ 68 | { 69 | "data": { 70 | "text/plain": [ 71 | "' This is a test string'" 72 | ] 73 | }, 74 | "execution_count": 19, 75 | "metadata": {}, 76 | "output_type": "execute_result" 77 | } 78 | ], 79 | "source": [ 80 | "string" 81 | ] 82 | }, 83 | { 84 | "cell_type": "code", 85 | "execution_count": 20, 86 | "metadata": { 87 | "collapsed": false 88 | }, 89 | "outputs": [], 90 | "source": [ 91 | "string = string.strip()" 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "execution_count": 21, 97 | "metadata": { 98 | "collapsed": false 99 | }, 100 | "outputs": [ 101 | { 102 | "data": { 103 | "text/plain": [ 104 | "'This is a test string'" 105 | ] 106 | }, 107 | "execution_count": 21, 108 | "metadata": {}, 109 | "output_type": "execute_result" 110 | } 111 | ], 112 | "source": [ 113 | "string" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": { 120 | "collapsed": true 121 | }, 122 | "outputs": [], 123 | "source": [] 124 | } 125 | ], 126 | "metadata": { 127 | "anaconda-cloud": {}, 128 | "kernelspec": { 129 | "display_name": "Python [conda env:py35]", 130 | "language": "python", 131 | "name": "conda-env-py35-py" 132 | }, 133 | "language_info": { 134 | "codemirror_mode": { 135 | "name": "ipython", 136 | "version": 3 137 | }, 138 | "file_extension": ".py", 139 | "mimetype": "text/x-python", 140 | "name": "python", 141 | "nbconvert_exporter": "python", 142 | "pygments_lexer": "ipython3", 143 | "version": "3.5.2" 144 | } 145 | }, 146 | "nbformat": 4, 147 | "nbformat_minor": 1 148 | } 149 | -------------------------------------------------------------------------------- /07_Pattern_Matching_with_Regular_Expressions/Chapter_Project/phoneAndEmail.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # phoneAndEmail.py - Finds phone numbers and email addresses on the clipboard. 3 | 4 | import pyperclip, re 5 | 6 | phoneRegex = re.compile(r'''( 7 | (\d{3}|\(\d{3}\))? # area code 8 | (\s|-|\.)? # separator 9 | (\d{3}) # first 3 digits 10 | (\s|-|\.) # separator 11 | (\d{4}) # last 4 digits 12 | (\s*(ext|x|ext.)\s*(\d{2,5}))? # extension 13 | )''', re.VERBOSE) 14 | 15 | # Create email regex. 16 | emailRegex = re.compile(r'''( 17 | [a-zA-Z0-9._%+-]+ # username 18 | @ # @ symbol 19 | [a-zA-Z0-9.-]+ # domain name 20 | (\.[a-zA-Z]{2,4}){1,2} # dot-something 21 | )''', re.VERBOSE) 22 | 23 | # Find matches in clipboard text. 24 | text = str(pyperclip.paste()) 25 | 26 | matches = [] 27 | for groups in phoneRegex.findall(text): 28 | phoneNum = '-'.join([groups[1], groups[3], groups[5]]) 29 | #print ('groups[1] is', type(groups[1])) 30 | # print ('groups[3] is', groups[3]) 31 | # print ('groups[5] is', groups[5]) 32 | 33 | if groups[8] != '': 34 | # print ('groups[8] is', groups[8]) 35 | phoneNum += ' x' + groups[8] 36 | matches.append(phoneNum) 37 | for groups in emailRegex.findall(text): 38 | matches.append(groups[0]) 39 | 40 | # Copy results to the clipboard. 41 | if len(matches) > 0: 42 | pyperclip.copy('\n'.join(matches)) 43 | print('Copied to clipboard:') 44 | print('\n'.join(matches)) 45 | else: 46 | print('No phone numbers or email addresses found.') 47 | -------------------------------------------------------------------------------- /07_Pattern_Matching_with_Regular_Expressions/Chapter_Project/phone_and_eamil.py: -------------------------------------------------------------------------------- 1 | # phone_and_email.py - Finds phone numbers and email addresser on the clipboard. 2 | 3 | ''' 4 | For example, your phone and email address extractor will need to do the following: 5 | 6 | Get the text off the clipboard. 7 | 8 | Find all phone numbers and email addresses in the text. 9 | 10 | Paste them onto the clipboard. 11 | 12 | Now you can start thinking about how this might work in code. The code will need to do the following: 13 | 14 | Use the pyperclip module to copy and paste strings. 15 | 16 | Create two regexes, one for matching phone numbers and the other for matching email addresses. 17 | 18 | Find all matches, not just the first match, of both regexes. 19 | 20 | Neatly format the matched strings into a single string to paste. 21 | 22 | Display some kind of message if no matches were found in the text. 23 | ''' 24 | 25 | import pyperclip, re 26 | 27 | phone_regex = re.compile(r'''( 28 | (\d{3}|\(\d{3}\))? # area code 29 | (\s|-|\.)? # separator 30 | (\d{3}) # first 3 digits 31 | (\s|-|\.)? # separator 32 | (\d{4}) # last 4 digits 33 | (\s*(ext|x|ext.)\s*(\d{2,5}))? # extension 34 | )''', re.VERBOSE) 35 | 36 | # TODO: Create email regex 37 | 38 | email_regex = re.compile(r'''( 39 | [a-zA-Z0-9._%+-]+ # username 40 | @ # @ symbol 41 | [a-zA-Z0-9.-]+ # domain name 42 | (\.[a-zA-Z]{2,4}) # dot-something 43 | )''', re.VERBOSE) 44 | 45 | # TODO: Find matches in clipboard text 46 | 47 | text = str(pyperclip.paste()) 48 | matches = [] 49 | for groups in phone_regex.findall(text): 50 | phone_num = '-'.join([groups[1], groups[3], groups[5]]) 51 | if groups[6] != '': 52 | pnone_num += ' x' + groups[6] 53 | matches.append(phone_num) 54 | for groups in email_regex.findall(text): 55 | matches.append(groups[0]) 56 | 57 | # TODO: Copy results to the clipboard 58 | if len(matches) > 0: 59 | pyperclip.copy('\n'.join(matches)) 60 | print ('Copied to clipboard') 61 | print ('\n'.join(matches)) 62 | else: 63 | print ('No phone number or email address found.') 64 | -------------------------------------------------------------------------------- /07_Pattern_Matching_with_Regular_Expressions/Practice_Project/regex_strip.py: -------------------------------------------------------------------------------- 1 | # Regex Version of strip() 2 | import re 3 | 4 | def regex_strip(string, rm_str=None): 5 | if rm_str == None: 6 | return string.strip() 7 | else: 8 | string = string.strip() 9 | string_regex = re.compile(rm_str) 10 | return string_regex.sub('', string) 11 | 12 | 13 | if __name__ == '__main__': 14 | string = input('Enter a string: ') 15 | rm_str = input('Enter the str you want to remove from the string or just click enter: (Optional): ') # fixed spelling mistake 'clik' 16 | string = regex_strip(string, rm_str) 17 | print (string) 18 | -------------------------------------------------------------------------------- /07_Pattern_Matching_with_Regular_Expressions/Practice_Project/strong_password_detection.py: -------------------------------------------------------------------------------- 1 | ''' 2 | A strong password is defined as one 3 | 4 | that is at least eight characters long, 5 | contains both uppercase and lowercase characters, 6 | and has at least one digit. 7 | 8 | You may need to test the string against multiple regex patterns to validate its strength. 9 | ''' 10 | #import re 11 | 12 | '''def pw_detection(pw): 13 | if len(pw) < 8: 14 | return False 15 | elif re.search('[0-9]', pw) is None: 16 | return False 17 | elif re.search('[A-Z]',pw) is None: 18 | return False 19 | elif re.search('[a-z]',pw) is None: 20 | return False 21 | else: 22 | return True 23 | 24 | if __name__ == '__main__': 25 | pw = input('Enter your password: ') 26 | if password_detection(pw): 27 | print('Good, your password is strong!') 28 | else: 29 | print ('The password is at least eight characters long, contains both uppercase and lowercase characters, and has at least one digit.') 30 | ''' 31 | import re 32 | def Pass_detection(pw): 33 | if len(pw) < 8: 34 | return False 35 | elif re.search('[0-9]|[A-Z]|[a-z]', pw) is None: 36 | return False 37 | else: 38 | return True 39 | 40 | pw = input('Enter your password : ') 41 | if Pass_detection(pw): 42 | print('Good, your password is strong!') 43 | else: 44 | print(''' 45 | The Password should at least be eight characters long, 46 | contains both uppercase and lowercase characters, 47 | and has at least one digit. 48 | ''') 49 | -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/Chapter_Project/mcb.pyw: -------------------------------------------------------------------------------- 1 | # mcb.pyw - Saves and loads pieces of text to the clipboard 2 | 3 | # Usage: py.exe mcb.pyw save - Saves clipboard to keyword. 4 |  # py.exe mcb.pyw - Loads keyword to clipboard. 5 |  # py.exe mcb.pyw list - Loads all keywords to clipboard. 6 | 7 | import shelve, pyperclip, sys 8 | 9 | mcb_shelf = shelve.open('mcb') 10 | 11 | # TODO: Save clipboard content. 12 | if len(sys.argv) == 3 and sys.argv[1].lower() == 'save': 13 | mcb_shelf[sys.argv[2]] = pyperclip.paste() 14 | elif len(sys.argv) == 2: 15 | # TODO: List keywords and load content. 16 | if sys.argv[1].lower() == 'list': 17 | pyperclip.copy(str(list(mcb_shelf.keys()))) 18 | elif sys.argv[1] in mcb_shelf: 19 | pyperclip.copy(mcb_shelf[sys.argv[1]]) 20 | 21 | 22 | mcb_shelf.close() 23 | -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/Chapter_Project/randomQuizGenerator.py: -------------------------------------------------------------------------------- 1 | """ 2 | In this solution we obtain 2 different folder: 3 | 1: quiz-papers folder that contains 35 different quizzes 4 | 2: quiz-answers folder that contains answer keys to the each quizzes 5 | 6 | Each quiz is different from the others in terms of question order and 7 | options' order of the each question 8 | """ 9 | from random import shuffle 10 | import random 11 | import os 12 | 13 | capitals = {'Alabama': 'Montgomery', 'Alaska': 'Juneau', 'Arizona': 'Phoenix', 14 | 'Arkansas': 'Little Rock', 'California': 'Sacramento', 'Colorado': 'Denver', 15 | 'Connecticut': 'Hartford', 'Delaware': 'Dover', 'Florida': 'Tallahassee', 16 | 'Georgia': 'Atlanta', 'Hawaii': 'Honolulu', 'Idaho': 'Boise', 'Illinois': 17 | 'Springfield', 'Indiana': 'Indianapolis', 'Iowa': 'Des Moines', 'Kansas': 18 | 'Topeka', 'Kentucky': 'Frankfort', 'Louisiana': 'Baton Rouge', 'Maine': 19 | 'Augusta', 'Maryland': 'Annapolis', 'Massachusetts': 'Boston', 'Michigan': 20 | 'Lansing', 'Minnesota': 'Saint Paul', 'Mississippi': 'Jackson', 'Missouri': 21 | 'Jefferson City', 'Montana': 'Helena', 'Nebraska': 'Lincoln', 'Nevada': 22 | 'Carson City', 'New Hampshire': 'Concord', 'New Jersey': 'Trenton', 23 | 'New Mexico': 'Santa Fe', 'New York': 'Albany', 'North Carolina': 'Raleigh', 24 | 'North Dakota': 'Bismarck', 'Ohio': 'Columbus', 'Oklahoma': 'Oklahoma City', 25 | 'Oregon': 'Salem', 'Pennsylvania': 'Harrisburg', 'Rhode Island': 'Providence', 26 | 'South Carolina': 'Columbia', 'South Dakota': 'Pierre', 'Tennessee': 27 | 'Nashville', 'Texas': 'Austin', 'Utah': 'Salt Lake City', 'Vermont': 28 | 'Montpelier', 'Virginia': 'Richmond', 'Washington': 'Olympia', 29 | 'West Virginia': 'Charleston', 'Wisconsin': 'Madison', 'Wyoming': 'Cheyenne'} 30 | 31 | # creating a 'quiz-papers' folder for question papers 32 | if not os.path.exists(os.getcwd() + '/quiz-papers'): 33 | os.mkdir('quiz-papers') 34 | # creating a 'quiz-answers' folder contains answer keys for each question paper 35 | if not os.path.exists(os.getcwd() + '/quiz-answers'): 36 | os.mkdir('quiz-answers') 37 | 38 | quizFilesPath = os.getcwd() + '/quiz-papers' 39 | quizAnswersPath = os.getcwd() + '/quiz-answers' 40 | states = list(capitals.keys()) 41 | 42 | for i in range(35): 43 | 44 | # creating question papers and adding header to each one of them 45 | questionFile = open(quizFilesPath + '/quiz' + str(i + 1) + '.txt', 'w') 46 | questionFile.write('Name:\n\nDate:\n\nPeriod:\n\n') 47 | questionFile.write((' ' * 20) + 'State Capitals Quiz (Form %s)' % (i + 1)) 48 | questionFile.write('\n\n') 49 | 50 | # creating answer key for each question paper 51 | answerFile = open(quizAnswersPath + '/quiz' + str(i + 1) + 'answers.txt', 'w') 52 | 53 | shuffle(states) 54 | questionNumber = 1 55 | 56 | for state in states: 57 | questionFile.write('\n\nQ' + str(questionNumber) + ':\nWhat is the capital of the ' + str(state) + '?\n\n') 58 | answerFile.write('\n\nQ' + str(questionNumber) + ':\n' + str(capitals[state]) + '\n\n') 59 | questionNumber += 1 60 | capitalList = list(capitals.values()) 61 | capitalList.remove(str(capitals[state])) 62 | 63 | # adding correct answer to the options 64 | choices = [str(capitals[state])] 65 | 66 | # adding 3 wrong answers to the options 67 | for i in range(3): 68 | randNum = random.randint(0, len(capitalList) - 1) 69 | choices.append(capitalList[randNum]) 70 | del capitalList[randNum] 71 | 72 | shuffle(choices) 73 | for i in range(len(choices)): 74 | questionFile.write(str(choices[i]) + '\t') 75 | 76 | questionFile.write('\n') 77 | 78 | questionFile.close() 79 | answerFile.close() 80 | -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/Chapter_Project/random_quiz_generator.py: -------------------------------------------------------------------------------- 1 | ''' 2 | 项目:生成随机的测验试卷文件 3 | 假如你是一位地理老师,班上有35名学生,你希望进行美国各州首府的一个小测验。不妙的是,班里有几个坏蛋,你无法确信学生不会作弊。你希望随机调整问题的次序,这样每份试卷都是独一无二的,这让任何人都不能从其他人那里抄袭答案。当然,手工完成这件事又费时又无聊。好在,你懂一些Python。 4 | 5 | 下面是程序所做的事: 6 | 7 | 创建35份不同的测验试卷。 8 | 为每份试卷创建50个多重选择题,次序随机。 9 | 为每个问题提供一个正确答案和3个随机的错误答案,次序随机。 10 | 将测验试卷写到35个文本文件中。 11 | 将答案写到35个文本文件中。 12 | 这意味着代码需要做下面的事: 13 | 14 | 将州和它们的首府保存在一个字典中。 15 | 针对测验文本文件和答案文本文件,调用open()、write()和close()。 16 | 利用random.shuffle()随机调整问题和多重选项的次序。 17 | ''' 18 | # randomQuizGenerator.py - Creates quizzes with questions and answers in 19 | # random order, along with the answer key. 20 | 21 | 22 | import random 23 | 24 | # The quiz data. Keys are states and values are their capitals. 25 | capitals = {'Alabama': 'Montgomery', 'Alaska': 'Juneau', 'Arizona': 'Phoenix', 'Arkansas': 'Little Rock', 'California': 'Sacramento', 'Colorado': 'Denver', 'Connecticut': 'Hartford', 'Delaware': 'Dover', 'Florida': 'Tallahassee', 'Georgia': 'Atlanta', 'Hawaii': 'Honolulu', 'Idaho': 'Boise', 'Illinois': 'Springfield', 'Indiana': 'Indianapolis', 'Iowa': 'Des Moines', 'Kansas': 'Topeka', 'Kentucky': 'Frankfort', 'Louisiana': 'Baton Rouge', 'Maine': 'Augusta', 'Maryland': 'Annapolis', 'Massachusetts': 'Boston', 'Michigan': 'Lansing', 'Minnesota': 'Saint Paul', 'Mississippi': 'Jackson', 'Missouri': 'Jefferson City', 'Montana': 'Helena', 'Nebraska': 'Lincoln', 'Nevada': 'Carson City', 'New Hampshire': 'Concord', 'New Jersey': 'Trenton', 'New Mexico': 'Santa Fe', 'New York': 'Albany', 'North Carolina': 'Raleigh', 'North Dakota': 'Bismarck', 'Ohio': 'Columbus', 'Oklahoma': 'Oklahoma City', 'Oregon': 'Salem', 'Pennsylvania': 'Harrisburg', 'Rhode Island': 'Providence', 'South Carolina': 'Columbia', 'South Dakota': 'Pierre', 'Tennessee': 'Nashville', 'Texas': 'Austin', 'Utah': 'Salt Lake City', 'Vermont': 'Montpelier', 'Virginia': 'Richmond', 'Washington': 'Olympia', 'West Virginia': 'Charleston', 'Wisconsin': 'Madison', 'Wyoming': 'Cheyenne'} 26 | 27 | # Generate 35 quiz files. 28 | for quiz_num in range(35): 29 | # TODO: Create the quiz and answer key files. 30 | quiz_file = open('capitalsquiz%s.txt' % (quiz_num + 1), 'w') 31 | answer_key_file = open('capitalsquiz_answers%s.txt' % (quiz_num + 1), 'w') 32 | 33 | # TODO: Write out the header for the quiz. 34 | quiz_file.write('Name:\n\nData:\n\nPeriod:\n\n') 35 | quiz_file.write((' '*20) + 'State Capitals Quiz (Form %s)' % (quiz_num + 1)) 36 | quiz_file.write('\n\n') 37 | 38 | # TODO: Sheffle the order of the states. 39 | states = list(capitals.keys()) 40 | random.shuffle(states) 41 | 42 | # TODO: Loop through all 50 states, making a question for each. 43 | for question_num in range(50): 44 | 45 | # Get right and wrong answer 46 | correct_answer = capitals[states[question_num]] 47 | wrong_answers = list(capitals.values()) 48 | del wrong_answers[wrong_answers.index(correct_answer)] 49 | wrong_answers = random.sample(wrong_answers, 3) 50 | answer_options = wrong_answers + [correct_answer] 51 | random.shuffle(answer_options) 52 | 53 | # TODO: Write the question and anser option to the quiz file. 54 | quiz_file.write('%s. What is the capital of %s?\n' % (question_num + 1, states[question_num])) 55 | for i in range(4): 56 | quiz_file.write(' %s. %s\n' % ('ABCD'[i], answer_options[i])) 57 | quiz_file.write('\n') 58 | 59 | # TODO: Write the anser key to a file. 60 | answer_key_file.write('%s. %s\n' % (question_num + 1, 'ABCD'[answer_options.index(correct_answer)])) 61 | quiz_file.close() 62 | answer_key_file.close() 63 | -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/__pycache__/my_cats.cpython-35.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/08_Reading_and_Writing_Files/__pycache__/my_cats.cpython-35.pyc -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/bacon.txt: -------------------------------------------------------------------------------- 1 | Hello world! 2 | Bacon is note a vegetable. -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/hello.txt: -------------------------------------------------------------------------------- 1 | Hello world! 2 | -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/my_cats.py: -------------------------------------------------------------------------------- 1 | cats = [{'desc': 'chubby', 'name': 'Zophie'}, {'desc': 'fluffy', 'name': 'Pooka'}] 2 | -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/mydata.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/08_Reading_and_Writing_Files/mydata.db -------------------------------------------------------------------------------- /08_Reading_and_Writing_Files/sonnet29.txt: -------------------------------------------------------------------------------- 1 | When, in disgrace with fortune and men's eyes, 2 | I all alone beweep my outcast state, 3 | And trouble deaf heaven with my bootless cries, 4 | And look upon myself and curse my fate, 5 | -------------------------------------------------------------------------------- /09_Organizing_Files/Practice_Projects/deleting_unneeded_files.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | for foldername, subfolders, filenames in os.walk(os.getcwd()): 4 | for filename in filenames: 5 | if os.path.getsize(os.path.abspath(filename)) > 2**20: 6 | print(os.path.abspath(filename)) 7 | -------------------------------------------------------------------------------- /09_Organizing_Files/Practice_Projects/filling_in_the_gaps.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | 4 | # Change these to whatever you want 5 | prefix = "spam" 6 | foldername = "test" 7 | 8 | for _, _, filenames in os.walk(foldername): 9 | for filename in filenames: 10 | mo = re.match(prefix + r'(\d+)\.txt', filename) 11 | if mo: 12 | print(mo.group(1)) 13 | print(int(mo.group(1))) 14 | -------------------------------------------------------------------------------- /09_Organizing_Files/Practice_Projects/selective_copy.py: -------------------------------------------------------------------------------- 1 | import os 2 | import shutil 3 | 4 | """ 5 | The content gets copied from folder1 to folder2 in the same directory as this file 6 | """ 7 | for foldername, subfolders, filenames in os.walk(os.path.abspath('folder1')): 8 | for filename in filenames: 9 | if filename.endswith('.pdf') or filename.endswith('.jpg'): 10 | print("Copying", filename) 11 | newPath = os.path.join(os.path.abspath( 12 | 'folder2'), os.path.relpath(foldername, 'folder1')) 13 | if not os.path.exists(newPath): 14 | os.mkdir(newPath) 15 | shutil.copy(os.path.join(foldername, filename), newPath) 16 | -------------------------------------------------------------------------------- /09_Organizing_Files/cats/catnames.txt: -------------------------------------------------------------------------------- 1 | Selena 2 | Apollo 3 | Mittens 4 | Chairman Meow 5 | Leo 6 | Snowball 7 | Miss Cleo 8 | Pooka 9 | Simon 10 | Zophie 11 | Oliver 12 | Milo 13 | Toby 14 | Tigger 15 | Jasper 16 | Sushi 17 | Smudge 18 | Lily 19 | Felix 20 | Amber 21 | Oreo 22 | Loki 23 | Simba 24 | Macbeth 25 | Bridge 26 | Garfield 27 | Nibbles 28 | Glacier -------------------------------------------------------------------------------- /09_Organizing_Files/cats/zophie.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/09_Organizing_Files/cats/zophie.jpg -------------------------------------------------------------------------------- /09_Organizing_Files/example.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/09_Organizing_Files/example.zip -------------------------------------------------------------------------------- /09_Organizing_Files/new.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/09_Organizing_Files/new.zip -------------------------------------------------------------------------------- /09_Organizing_Files/some_new/folders/spam.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/09_Organizing_Files/some_new/folders/spam.txt -------------------------------------------------------------------------------- /09_Organizing_Files/spam.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/09_Organizing_Files/spam.txt -------------------------------------------------------------------------------- /10_Debugging/.idea/10_Debugging.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | -------------------------------------------------------------------------------- /10_Debugging/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /10_Debugging/.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /10_Debugging/.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 59 | 60 | 61 | 68 | 69 | 70 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 99 | 100 | 103 | 104 | 107 | 108 | 109 | 110 | 113 | 114 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 144 | 145 | 161 | 162 | 173 | 174 | 192 | 193 | 211 | 212 | 232 | 233 | 254 | 255 | 278 | 279 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 308 | 309 | 310 | 311 | 1484786508158 312 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 353 | 354 | 355 | 356 | 357 | file://$PROJECT_DIR$/coin_flip.py 358 | 6 359 | 361 | 362 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | -------------------------------------------------------------------------------- /10_Debugging/10_debugging.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 1, 6 | "metadata": { 7 | "collapsed": false 8 | }, 9 | "outputs": [ 10 | { 11 | "ename": "Exception", 12 | "evalue": "This is the error message.", 13 | "output_type": "error", 14 | "traceback": [ 15 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 16 | "\u001b[0;31mException\u001b[0m Traceback (most recent call last)", 17 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32mraise\u001b[0m \u001b[0mException\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'This is the error message.'\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 18 | "\u001b[0;31mException\u001b[0m: This is the error message." 19 | ] 20 | } 21 | ], 22 | "source": [ 23 | "raise Exception('This is the error message.')" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 2, 29 | "metadata": { 30 | "collapsed": true 31 | }, 32 | "outputs": [], 33 | "source": [ 34 | "import traceback" 35 | ] 36 | }, 37 | { 38 | "cell_type": "code", 39 | "execution_count": 3, 40 | "metadata": { 41 | "collapsed": false 42 | }, 43 | "outputs": [ 44 | { 45 | "name": "stdout", 46 | "output_type": "stream", 47 | "text": [ 48 | "The trace info was write to error_info.txt\n" 49 | ] 50 | } 51 | ], 52 | "source": [ 53 | "try:\n", 54 | " raise Exception('This is the error message.')\n", 55 | "except:\n", 56 | " error_file = open('error_info.txt', 'w')\n", 57 | " error_file.write(traceback.format_exc())\n", 58 | " error_file.close()\n", 59 | " print('The trace info was write to error_info.txt')" 60 | ] 61 | }, 62 | { 63 | "cell_type": "code", 64 | "execution_count": 4, 65 | "metadata": { 66 | "collapsed": false 67 | }, 68 | "outputs": [ 69 | { 70 | "name": "stdout", 71 | "output_type": "stream", 72 | "text": [ 73 | "10_debugging.ipynb box_print.py error_example.py error_info.txt\r\n" 74 | ] 75 | } 76 | ], 77 | "source": [ 78 | "ls" 79 | ] 80 | }, 81 | { 82 | "cell_type": "code", 83 | "execution_count": 6, 84 | "metadata": { 85 | "collapsed": false 86 | }, 87 | "outputs": [ 88 | { 89 | "name": "stdout", 90 | "output_type": "stream", 91 | "text": [ 92 | "Traceback (most recent call last):\r\n", 93 | " File \"\", line 2, in \r\n", 94 | " raise Exception('This is the error message.')\r\n", 95 | "Exception: This is the error message.\r\n" 96 | ] 97 | } 98 | ], 99 | "source": [ 100 | "cat error_info.txt" 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": 7, 106 | "metadata": { 107 | "collapsed": true 108 | }, 109 | "outputs": [], 110 | "source": [ 111 | "# 10.3 assert" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": 8, 117 | "metadata": { 118 | "collapsed": true 119 | }, 120 | "outputs": [], 121 | "source": [ 122 | "pod_bay_door_status = 'open'" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": 10, 128 | "metadata": { 129 | "collapsed": true 130 | }, 131 | "outputs": [], 132 | "source": [ 133 | "assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 12, 139 | "metadata": { 140 | "collapsed": false 141 | }, 142 | "outputs": [], 143 | "source": [ 144 | "pod_bay_door_status = 'I\\'m sorry, Dave. I\\'m afraid I can\\'t do that. '" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": 13, 150 | "metadata": { 151 | "collapsed": false 152 | }, 153 | "outputs": [ 154 | { 155 | "ename": "AssertionError", 156 | "evalue": "The pod bay doors need to be \"open\".", 157 | "output_type": "error", 158 | "traceback": [ 159 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 160 | "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", 161 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mpod_bay_door_status\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;34m'open'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'The pod bay doors need to be \"open\".'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 162 | "\u001b[0;31mAssertionError\u001b[0m: The pod bay doors need to be \"open\"." 163 | ] 164 | } 165 | ], 166 | "source": [ 167 | "assert pod_bay_door_status == 'open', 'The pod bay doors need to be \"open\".'" 168 | ] 169 | }, 170 | { 171 | "cell_type": "code", 172 | "execution_count": 14, 173 | "metadata": { 174 | "collapsed": true 175 | }, 176 | "outputs": [], 177 | "source": [ 178 | "# 10.3.1 \n", 179 | "market_2nd = {'ns': 'green', 'ew': 'red'}\n", 180 | "mission_16th = {'ns': 'red', 'ew': 'green'}" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": 18, 186 | "metadata": { 187 | "collapsed": false 188 | }, 189 | "outputs": [], 190 | "source": [ 191 | "def switch_lights(stoplight):\n", 192 | " for key in stoplight.keys():\n", 193 | " if stoplight[key] == 'green':\n", 194 | " stoplight[key] = 'yellow'\n", 195 | " elif stoplight[key] == 'yellow':\n", 196 | " stoplight[key] = 'red'\n", 197 | " elif stoplight[key] == 'red':\n", 198 | " stoplight[key] = 'green'\n", 199 | " assert 'red' in stoplight.values(), 'Neither light is red!' + str(stoplight)\n", 200 | "switch_lights(market_2nd)" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": 19, 206 | "metadata": { 207 | "collapsed": true 208 | }, 209 | "outputs": [], 210 | "source": [ 211 | "# 10.4" 212 | ] 213 | }, 214 | { 215 | "cell_type": "code", 216 | "execution_count": 35, 217 | "metadata": { 218 | "collapsed": false 219 | }, 220 | "outputs": [ 221 | { 222 | "name": "stdout", 223 | "output_type": "stream", 224 | "text": [ 225 | "Start of factorial(5%)\n" 226 | ] 227 | } 228 | ], 229 | "source": [ 230 | "n = 5\n", 231 | "print ('Start of factorial(%s%%)' % (n))" 232 | ] 233 | }, 234 | { 235 | "cell_type": "code", 236 | "execution_count": 36, 237 | "metadata": { 238 | "collapsed": false 239 | }, 240 | "outputs": [ 241 | { 242 | "name": "stdout", 243 | "output_type": "stream", 244 | "text": [ 245 | "Start of factorial(5%)\n" 246 | ] 247 | } 248 | ], 249 | "source": [ 250 | "print ('Start of factorial(%s%%)' % (n))" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": 1, 256 | "metadata": { 257 | "collapsed": true 258 | }, 259 | "outputs": [], 260 | "source": [ 261 | "import logging\n", 262 | "logging.basicConfig(level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s')" 263 | ] 264 | }, 265 | { 266 | "cell_type": "code", 267 | "execution_count": 2, 268 | "metadata": { 269 | "collapsed": false 270 | }, 271 | "outputs": [ 272 | { 273 | "name": "stderr", 274 | "output_type": "stream", 275 | "text": [ 276 | " 2017-01-18 23:11:03,301 - DEBUG - Some debugging details.\n" 277 | ] 278 | } 279 | ], 280 | "source": [ 281 | "logging.debug('Some debugging details.')" 282 | ] 283 | }, 284 | { 285 | "cell_type": "code", 286 | "execution_count": 3, 287 | "metadata": { 288 | "collapsed": false 289 | }, 290 | "outputs": [ 291 | { 292 | "name": "stderr", 293 | "output_type": "stream", 294 | "text": [ 295 | " 2017-01-18 23:11:24,049 - INFO - The logging module is working.\n" 296 | ] 297 | } 298 | ], 299 | "source": [ 300 | "logging.info('The logging module is working.')" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 4, 306 | "metadata": { 307 | "collapsed": false 308 | }, 309 | "outputs": [ 310 | { 311 | "name": "stderr", 312 | "output_type": "stream", 313 | "text": [ 314 | " 2017-01-18 23:11:36,346 - WARNING - An error message is about to be logged.\n" 315 | ] 316 | } 317 | ], 318 | "source": [ 319 | "logging.warning('An error message is about to be logged.')" 320 | ] 321 | }, 322 | { 323 | "cell_type": "code", 324 | "execution_count": 5, 325 | "metadata": { 326 | "collapsed": false 327 | }, 328 | "outputs": [ 329 | { 330 | "name": "stderr", 331 | "output_type": "stream", 332 | "text": [ 333 | " 2017-01-18 23:11:49,765 - ERROR - An error has occurred.\n" 334 | ] 335 | } 336 | ], 337 | "source": [ 338 | "logging.error('An error has occurred.')" 339 | ] 340 | }, 341 | { 342 | "cell_type": "code", 343 | "execution_count": 6, 344 | "metadata": { 345 | "collapsed": false 346 | }, 347 | "outputs": [ 348 | { 349 | "name": "stderr", 350 | "output_type": "stream", 351 | "text": [ 352 | " 2017-01-18 23:11:57,502 - CRITICAL - The program is unable to recover!\n" 353 | ] 354 | } 355 | ], 356 | "source": [ 357 | "logging.critical('The program is unable to recover!')" 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": 7, 363 | "metadata": { 364 | "collapsed": false 365 | }, 366 | "outputs": [ 367 | { 368 | "name": "stderr", 369 | "output_type": "stream", 370 | "text": [ 371 | " 2017-01-18 23:13:39,116 - CRITICAL - Critical error! Critical error!\n" 372 | ] 373 | } 374 | ], 375 | "source": [ 376 | "logging.critical('Critical error! Critical error!')" 377 | ] 378 | }, 379 | { 380 | "cell_type": "code", 381 | "execution_count": 11, 382 | "metadata": { 383 | "collapsed": true 384 | }, 385 | "outputs": [], 386 | "source": [ 387 | "logging.disable(logging.CRITICAL)" 388 | ] 389 | }, 390 | { 391 | "cell_type": "code", 392 | "execution_count": 12, 393 | "metadata": { 394 | "collapsed": true 395 | }, 396 | "outputs": [], 397 | "source": [ 398 | "logging.critical('Critical error! Critical error!')" 399 | ] 400 | }, 401 | { 402 | "cell_type": "code", 403 | "execution_count": 13, 404 | "metadata": { 405 | "collapsed": true 406 | }, 407 | "outputs": [], 408 | "source": [ 409 | "logging.error('Error! Error!')" 410 | ] 411 | }, 412 | { 413 | "cell_type": "code", 414 | "execution_count": 14, 415 | "metadata": { 416 | "collapsed": true 417 | }, 418 | "outputs": [], 419 | "source": [ 420 | "# 10.4.5 \n", 421 | "logging.basicConfig(filename='my_program_log.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')" 422 | ] 423 | }, 424 | { 425 | "cell_type": "code", 426 | "execution_count": 20, 427 | "metadata": { 428 | "collapsed": false 429 | }, 430 | "outputs": [ 431 | { 432 | "ename": "AssertionError", 433 | "evalue": "spam is smaller than 10", 434 | "output_type": "error", 435 | "traceback": [ 436 | "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", 437 | "\u001b[0;31mAssertionError\u001b[0m Traceback (most recent call last)", 438 | "\u001b[0;32m\u001b[0m in \u001b[0;36m\u001b[0;34m()\u001b[0m\n\u001b[1;32m 1\u001b[0m \u001b[0mspam\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m9\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0;32massert\u001b[0m \u001b[0mspam\u001b[0m \u001b[0;34m>=\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'spam is smaller than 10'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m", 439 | "\u001b[0;31mAssertionError\u001b[0m: spam is smaller than 10" 440 | ] 441 | } 442 | ], 443 | "source": [ 444 | "spam = 9\n", 445 | "assert spam >= 10, 'spam is smaller than 10'" 446 | ] 447 | }, 448 | { 449 | "cell_type": "code", 450 | "execution_count": null, 451 | "metadata": { 452 | "collapsed": true 453 | }, 454 | "outputs": [], 455 | "source": [] 456 | } 457 | ], 458 | "metadata": { 459 | "anaconda-cloud": {}, 460 | "kernelspec": { 461 | "display_name": "Python [conda env:py35]", 462 | "language": "python", 463 | "name": "conda-env-py35-py" 464 | }, 465 | "language_info": { 466 | "codemirror_mode": { 467 | "name": "ipython", 468 | "version": 3 469 | }, 470 | "file_extension": ".py", 471 | "mimetype": "text/x-python", 472 | "name": "python", 473 | "nbconvert_exporter": "python", 474 | "pygments_lexer": "ipython3", 475 | "version": "3.5.2" 476 | } 477 | }, 478 | "nbformat": 4, 479 | "nbformat_minor": 0 480 | } 481 | -------------------------------------------------------------------------------- /10_Debugging/box_print.py: -------------------------------------------------------------------------------- 1 | def box_print (symbol, width, height): 2 | if len(symbol) != 1: 3 | raise Exception('Symbol must be a single character string.') 4 | if width <= 2: 5 | raise Exception('Width must be greater than 2.') 6 | if height <= 2: 7 | raise Exception('Height mush be greater than 2') 8 | print (symbol*width) 9 | for i in range(height -2): 10 | print (symbol + (' ' * (width -2)) + symbol) 11 | print (symbol*width) 12 | 13 | 14 | for sym, w, h in (('*', 4, 4), ('0', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)): 15 | try: 16 | box_print(sym, w, h) 17 | except Exception as err: 18 | print ('An exception happened: ' + str(err)) 19 | 20 | -------------------------------------------------------------------------------- /10_Debugging/buggy_adding_program.py: -------------------------------------------------------------------------------- 1 | print ('Enter the first number to add:') 2 | first = input() 3 | print('Enter the second number to add:') 4 | second = input() 5 | print('Enter the third number to add:') 6 | third = input() 7 | print ('The sum is ' + first + second + third) -------------------------------------------------------------------------------- /10_Debugging/coin_flip.py: -------------------------------------------------------------------------------- 1 | import random 2 | heads = 0 3 | for i in range(1, 1001): 4 | if random.randint(0, 1) == 1: 5 | heads = heads + 1 6 | if i == 500: 7 | print('Halfway done!') 8 | print('Heads came up ' + str(heads) + ' times.') -------------------------------------------------------------------------------- /10_Debugging/error_example.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | bacon() 3 | def bacon(): 4 | raise Exception('This is the error message.') 5 | 6 | spam() 7 | -------------------------------------------------------------------------------- /10_Debugging/error_info.txt: -------------------------------------------------------------------------------- 1 | Traceback (most recent call last): 2 | File "", line 2, in 3 | raise Exception('This is the error message.') 4 | Exception: This is the error message. 5 | -------------------------------------------------------------------------------- /10_Debugging/factorial_log.py: -------------------------------------------------------------------------------- 1 | import logging 2 | # logging.disable(logging.CRITICAL) 3 | 4 | logging.basicConfig(filename='my_program_log.txt', level=logging.DEBUG, format=' %(asctime)s - %(levelname)s - %(message)s') 5 | logging.debug('Start of program') 6 | 7 | def factorial(n): 8 | logging.debug('Start of factorial(%s%%)' % (n)) 9 | total = 1 10 | for i in range(1, n + 1): 11 | total *= i 12 | logging.debug('i is ' + str(i) + ', total is ' + str(total)) 13 | logging.debug('End of factorial(%s%%)' % (n)) 14 | return total 15 | 16 | print (factorial(5)) 17 | logging.debug('End of program') 18 | -------------------------------------------------------------------------------- /10_Debugging/my_program_log.txt: -------------------------------------------------------------------------------- 1 | 2017-01-18 23:18:54,384 - DEBUG - Start of program 2 | 2017-01-18 23:18:54,384 - DEBUG - Start of factorial(5%) 3 | 2017-01-18 23:18:54,384 - DEBUG - i is 1, total is 1 4 | 2017-01-18 23:18:54,384 - DEBUG - i is 2, total is 2 5 | 2017-01-18 23:18:54,384 - DEBUG - i is 3, total is 6 6 | 2017-01-18 23:18:54,385 - DEBUG - i is 4, total is 24 7 | 2017-01-18 23:18:54,385 - DEBUG - i is 5, total is 120 8 | 2017-01-18 23:18:54,385 - DEBUG - End of factorial(5%) 9 | 2017-01-18 23:18:54,385 - DEBUG - End of program 10 | -------------------------------------------------------------------------------- /11_Web_Scraping/argv.py: -------------------------------------------------------------------------------- 1 | # test sys.argv is a list 2 | 3 | import sys 4 | 5 | if len(sys.argv) > 1: 6 | print(sys.argv) 7 | print(' '.join(sys.argv[1:])) 8 | else: 9 | print (sys.argv) 10 | -------------------------------------------------------------------------------- /11_Web_Scraping/bookcover.py: -------------------------------------------------------------------------------- 1 | # bookcover.py - using selenium to find specific element in a web page 2 | from selenium import webdriver 3 | browser = webdriver.Firefox() 4 | browser.get('http://inventwithpython.com') 5 | try: 6 | elem = browser.find_element_by_class_name('bookcover') 7 | print('Found <%s> element with that class name!' % (elem.tag_name)) 8 | except: 9 | print('Was not able to find an element with that name.') 10 | -------------------------------------------------------------------------------- /11_Web_Scraping/download_xkcd.py: -------------------------------------------------------------------------------- 1 | # download_xkcd.py - Downloads every single XKCD comic. 2 | 3 | import requests, os, bs4 4 | 5 | url = 'http://xkcd.com' #starting url 6 | os.makedirs('xkcd', exist_ok=True) # store comics in ./xkcd 7 | while not url.endswith('#'): 8 | # TODO: Download the page 9 | print ('Downloading page %s...' % url) 10 | res = requests.get(url) 11 | res.raise_for_status() 12 | 13 | soup = bs4.BeautifulSoup(res.text) 14 | 15 | # TODO: Find the URL of the comic image. 16 | comic_elem = soup.select('#comic img') 17 | if comic_elem == []: 18 | print('Could not find comic image.') 19 | else: 20 | comic_url = 'http:' + comic_elem[0].get('src') 21 | # TODO: Download the image. 22 | print('Downloading image %s...' % (comic_url)) 23 | res = requests.get(comic_url) 24 | res.raise_for_status() 25 | 26 | # TODO: Save the image to ./xkcd. 27 | image_file = open(os.path.join('xkcd', os.path.basename(comic_url)), 'wb') 28 | for chunk in res.iter_content(100000): 29 | image_file.write(chunk) 30 | image_file.close() 31 | 32 | # TODO: Get the Prev button's url. 33 | prev_link = soup.select('a[rel="prev"]')[0] 34 | url = 'http://xkcd.com' + prev_link.get('href') 35 | 36 | print ('Done.') 37 | -------------------------------------------------------------------------------- /11_Web_Scraping/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | The Website Title 4 | 5 |

Download my Python book from my website.

7 |

Learn Python the easy way!

8 |

By Al Sweigart

9 | 10 | -------------------------------------------------------------------------------- /11_Web_Scraping/geckodriver.log: -------------------------------------------------------------------------------- 1 | 1484823998688 geckodriver INFO Listening on 127.0.0.1:61387 2 | 1484823999629 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.JPD3gjPrATiJ 3 | 1484823999633 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 4 | 1484823999679 geckodriver::marionette INFO Connecting to Marionette on localhost:61397 5 | 1484824000600 Marionette INFO Listening on port 61397 6 | ************************* 7 | A coding exception was thrown and uncaught in a Task. 8 | 9 | Full message: TypeError: NetworkError when attempting to fetch resource. 10 | Full stack: 11 | ************************* 12 | 1484824835843 geckodriver INFO Listening on 127.0.0.1:62032 13 | 1484824836817 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.SHCO6aGdq6LG 14 | 1484824836820 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 15 | 1484824836828 geckodriver::marionette INFO Connecting to Marionette on localhost:62041 16 | 1484824837757 Marionette INFO Listening on port 62041 17 | 1484824969360 geckodriver INFO Listening on 127.0.0.1:62195 18 | 1484824970315 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.BcCF92edEaZk 19 | 1484824970318 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 20 | 1484824970329 geckodriver::marionette INFO Connecting to Marionette on localhost:62204 21 | 1484824971102 Marionette INFO Listening on port 62204 22 | 2017-01-19 20:25:03.500 firefox-bin[57656:5269076] -deltaZ is deprecated for NSEventTypeMagnify. Please use -magnification. 23 | 1484825249916 geckodriver INFO Listening on 127.0.0.1:62438 24 | 1484825250874 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.GCDh3DFCKqaD 25 | 1484825250878 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 26 | 1484825250887 geckodriver::marionette INFO Connecting to Marionette on localhost:62449 27 | 1484825251500 Marionette INFO Listening on port 62449 28 | JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/client/inspector/breadcrumbs.js, line 486: TypeError: this.handleMouseLeave is not a function 29 | JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/client/inspector/breadcrumbs.js, line 486: TypeError: this.handleMouseLeave is not a function 30 | JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/client/inspector/breadcrumbs.js, line 486: TypeError: this.handleMouseLeave is not a function 31 | JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/client/inspector/breadcrumbs.js, line 486: TypeError: this.handleMouseLeave is not a function 32 | JavaScript error: resource://gre/modules/LoginManagerParent.jsm, line 77: TypeError: this._recipeManager is null 33 | console.warn: nsLoginManager: searchLogins: `formSubmitURL` or `httpRealm` is recommended 34 | JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/client/inspector/breadcrumbs.js, line 486: TypeError: this.handleMouseLeave is not a function 35 | JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/client/inspector/breadcrumbs.js, line 486: TypeError: this.handleMouseLeave is not a function 36 | JavaScript error: resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/client/inspector/breadcrumbs.js, line 486: TypeError: this.handleMouseLeave is not a function 37 | ************************* 38 | A coding exception was thrown and uncaught in a Task. 39 | 40 | Full message: TypeError: NetworkError when attempting to fetch resource. 41 | Full stack: 42 | ************************* 43 | JavaScript error: resource://gre/modules/LoginManagerParent.jsm, line 77: TypeError: this._recipeManager is null 44 | console.warn: nsLoginManager: searchLogins: `formSubmitURL` or `httpRealm` is recommended 45 | JavaScript error: resource://gre/modules/LoginManagerParent.jsm, line 77: TypeError: this._recipeManager is null 46 | console.warn: nsLoginManager: searchLogins: `formSubmitURL` or `httpRealm` is recommended 47 | ************************* 48 | A coding exception was thrown and uncaught in a Task. 49 | 50 | Full message: TypeError: NetworkError when attempting to fetch resource. 51 | Full stack: 52 | ************************* 53 | 1484826147289 geckodriver INFO Listening on 127.0.0.1:63162 54 | 1484826148245 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.gMXCm7qk9Hk4 55 | 1484826148248 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 56 | 1484826148256 geckodriver::marionette INFO Connecting to Marionette on localhost:63173 57 | 1484826149006 Marionette INFO Listening on port 63173 58 | 1484826154287 geckodriver INFO Listening on 127.0.0.1:63201 59 | 1484826155250 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.OY71lagEnlFt 60 | 1484826155253 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 61 | 1484826155264 geckodriver::marionette INFO Connecting to Marionette on localhost:63211 62 | 1484826155817 Marionette INFO Listening on port 63211 63 | 1484826161610 geckodriver INFO Listening on 127.0.0.1:63236 64 | 1484826162570 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.A77iBIyijiRG 65 | 1484826162574 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 66 | 1484826162583 geckodriver::marionette INFO Connecting to Marionette on localhost:63245 67 | 1484826163161 Marionette INFO Listening on port 63245 68 | JavaScript warning: https://www.nostarch.com/sites/default/files/js/js_eaf9b3e1ef83026eae354d778d9b9204.js, line 2: unreachable code after return statement 69 | ************************* 70 | A coding exception was thrown and uncaught in a Task. 71 | 72 | Full message: TypeError: this.browserForTab is undefined 73 | Full stack: hasRemotenessChange@chrome://marionette/content/browser.js:233:9 74 | executeWhenReady@chrome://marionette/content/browser.js:264:9 75 | GeckoDriver.prototype.sendAsync@chrome://marionette/content/driver.js:218:5 76 | send/<@chrome://marionette/content/proxy.js:138:7 77 | send@chrome://marionette/content/proxy.js:105:12 78 | ownPriorityGetterTrap.get/<@chrome://marionette/content/proxy.js:24:25 79 | GeckoDriver.prototype.findElement@chrome://marionette/content/driver.js:1691:31 80 | TaskImpl_run@resource://gre/modules/Task.jsm:319:40 81 | TaskImpl@resource://gre/modules/Task.jsm:280:3 82 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 83 | Task_spawn@resource://gre/modules/Task.jsm:168:12 84 | TaskImpl_handleResultValue@resource://gre/modules/Task.jsm:388:16 85 | TaskImpl_run@resource://gre/modules/Task.jsm:327:13 86 | TaskImpl@resource://gre/modules/Task.jsm:280:3 87 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 88 | Task_spawn@resource://gre/modules/Task.jsm:168:12 89 | Dispatcher.prototype.execute@chrome://marionette/content/dispatcher.js:117:13 90 | Dispatcher.prototype.onPacket@chrome://marionette/content/dispatcher.js:88:5 91 | DebuggerTransport.prototype._onJSONObjectReady/<@chrome://marionette/content/server.js -> resource://devtools/shared/transport/transport.js:482:11 92 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 93 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 94 | 95 | ************************* 96 | ************************* 97 | A coding exception was thrown and uncaught in a Task. 98 | 99 | Full message: TypeError: this.browserForTab is undefined 100 | Full stack: hasRemotenessChange@chrome://marionette/content/browser.js:233:9 101 | executeWhenReady@chrome://marionette/content/browser.js:264:9 102 | GeckoDriver.prototype.sendAsync@chrome://marionette/content/driver.js:218:5 103 | send/<@chrome://marionette/content/proxy.js:138:7 104 | send@chrome://marionette/content/proxy.js:105:12 105 | ownPriorityGetterTrap.get/<@chrome://marionette/content/proxy.js:24:25 106 | GeckoDriver.prototype.findElement@chrome://marionette/content/driver.js:1691:31 107 | TaskImpl_run@resource://gre/modules/Task.jsm:319:40 108 | TaskImpl@resource://gre/modules/Task.jsm:280:3 109 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 110 | Task_spawn@resource://gre/modules/Task.jsm:168:12 111 | TaskImpl_handleResultValue@resource://gre/modules/Task.jsm:388:16 112 | TaskImpl_run@resource://gre/modules/Task.jsm:327:13 113 | TaskImpl@resource://gre/modules/Task.jsm:280:3 114 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 115 | Task_spawn@resource://gre/modules/Task.jsm:168:12 116 | Dispatcher.prototype.execute@chrome://marionette/content/dispatcher.js:117:13 117 | Dispatcher.prototype.onPacket@chrome://marionette/content/dispatcher.js:88:5 118 | DebuggerTransport.prototype._onJSONObjectReady/<@chrome://marionette/content/server.js -> resource://devtools/shared/transport/transport.js:482:11 119 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 120 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 121 | 122 | ************************* 123 | ************************* 124 | A coding exception was thrown in a Promise rejection callback. 125 | See https://developer.mozilla.org/Mozilla/JavaScript_code_modules/Promise.jsm/Promise 126 | 127 | Full message: TypeError: this.browserForTab is undefined 128 | Full stack: hasRemotenessChange@chrome://marionette/content/browser.js:233:9 129 | executeWhenReady@chrome://marionette/content/browser.js:264:9 130 | GeckoDriver.prototype.sendAsync@chrome://marionette/content/driver.js:218:5 131 | send/<@chrome://marionette/content/proxy.js:138:7 132 | send@chrome://marionette/content/proxy.js:105:12 133 | ownPriorityGetterTrap.get/<@chrome://marionette/content/proxy.js:24:25 134 | GeckoDriver.prototype.findElement@chrome://marionette/content/driver.js:1691:31 135 | TaskImpl_run@resource://gre/modules/Task.jsm:319:40 136 | TaskImpl@resource://gre/modules/Task.jsm:280:3 137 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 138 | Task_spawn@resource://gre/modules/Task.jsm:168:12 139 | TaskImpl_handleResultValue@resource://gre/modules/Task.jsm:388:16 140 | TaskImpl_run@resource://gre/modules/Task.jsm:327:13 141 | TaskImpl@resource://gre/modules/Task.jsm:280:3 142 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 143 | Task_spawn@resource://gre/modules/Task.jsm:168:12 144 | Dispatcher.prototype.execute@chrome://marionette/content/dispatcher.js:117:13 145 | Dispatcher.prototype.onPacket@chrome://marionette/content/dispatcher.js:88:5 146 | DebuggerTransport.prototype._onJSONObjectReady/<@chrome://marionette/content/server.js -> resource://devtools/shared/transport/transport.js:482:11 147 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 148 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 149 | 150 | ************************* 151 | Marionette threw an error: TypeError: this.browserForTab is undefined 152 | hasRemotenessChange@chrome://marionette/content/browser.js:233:9 153 | executeWhenReady@chrome://marionette/content/browser.js:264:9 154 | GeckoDriver.prototype.sendAsync@chrome://marionette/content/driver.js:218:5 155 | send/<@chrome://marionette/content/proxy.js:138:7 156 | send@chrome://marionette/content/proxy.js:105:12 157 | ownPriorityGetterTrap.get/<@chrome://marionette/content/proxy.js:24:25 158 | GeckoDriver.prototype.findElement@chrome://marionette/content/driver.js:1691:31 159 | TaskImpl_run@resource://gre/modules/Task.jsm:319:40 160 | TaskImpl@resource://gre/modules/Task.jsm:280:3 161 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 162 | Task_spawn@resource://gre/modules/Task.jsm:168:12 163 | TaskImpl_handleResultValue@resource://gre/modules/Task.jsm:388:16 164 | TaskImpl_run@resource://gre/modules/Task.jsm:327:13 165 | TaskImpl@resource://gre/modules/Task.jsm:280:3 166 | createAsyncFunction/asyncFunction@resource://gre/modules/Task.jsm:254:14 167 | Task_spawn@resource://gre/modules/Task.jsm:168:12 168 | Dispatcher.prototype.execute@chrome://marionette/content/dispatcher.js:117:13 169 | Dispatcher.prototype.onPacket@chrome://marionette/content/dispatcher.js:88:5 170 | DebuggerTransport.prototype._onJSONObjectReady/<@chrome://marionette/content/server.js -> resource://devtools/shared/transport/transport.js:482:11 171 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 172 | exports.makeInfallible/<@resource://gre/modules/commonjs/toolkit/loader.js -> resource://devtools/shared/ThreadSafeDevToolsUtils.js:101:14 173 | 174 | JavaScript warning: https://www.nostarch.com/sites/default/files/js/js_eaf9b3e1ef83026eae354d778d9b9204.js, line 2: unreachable code after return statement 175 | JavaScript warning: https://www.nostarch.com/sites/default/files/js/js_eaf9b3e1ef83026eae354d778d9b9204.js, line 2: unreachable code after return statement 176 | 1484826684426 geckodriver INFO Listening on 127.0.0.1:63690 177 | 1484826685391 mozprofile::profile INFO Using profile path /var/folders/l8/cr5skbnj4wq1w4st_28hms7r0000gn/T/rust_mozprofile.pSp2O7JyutnX 178 | 1484826685395 geckodriver::marionette INFO Starting browser /Applications/Firefox.app/Contents/MacOS/firefox-bin 179 | 1484826685405 geckodriver::marionette INFO Connecting to Marionette on localhost:63699 180 | 1484826686300 Marionette INFO Listening on port 63699 181 | JavaScript warning: https://www.nostarch.com/sites/default/files/js/js_eaf9b3e1ef83026eae354d778d9b9204.js, line 2: unreachable code after return statement 182 | ************************* 183 | A coding exception was thrown and uncaught in a Task. 184 | 185 | Full message: TypeError: NetworkError when attempting to fetch resource. 186 | Full stack: 187 | ************************* 188 | ************************* 189 | A coding exception was thrown and uncaught in a Task. 190 | 191 | Full message: TypeError: NetworkError when attempting to fetch resource. 192 | Full stack: 193 | ************************* 194 | ************************* 195 | A coding exception was thrown and uncaught in a Task. 196 | 197 | Full message: TypeError: NetworkError when attempting to fetch resource. 198 | Full stack: 199 | ************************* 200 | ************************* 201 | A coding exception was thrown and uncaught in a Task. 202 | 203 | Full message: TypeError: NetworkError when attempting to fetch resource. 204 | Full stack: 205 | ************************* 206 | -------------------------------------------------------------------------------- /11_Web_Scraping/lucky.py: -------------------------------------------------------------------------------- 1 | # lucky.py - Opens several Google search results. 2 | 3 | import requests, sys, webbrowser, bs4 4 | 5 | print ('Googling...') # display text while downloading the Google page 6 | 7 | res = requests.get('http://google.com/search?q=' + ' '.join(sys.argv[1:])) 8 | res.raise_for_status() 9 | 10 | # TODO: Retrieve top search result links. 11 | soup = bs4.BeautifulSoup(res.text, 'lxml') 12 | 13 | # TODO: Open a browser tab for each result. 14 | link_elems = soup.select('.r a') 15 | num_open = min(5, len(link_elems)) 16 | for i in range(num_open): 17 | webbrowser.open('http://google.com' + link_elems[i].get('href')) 18 | -------------------------------------------------------------------------------- /11_Web_Scraping/mapit.py: -------------------------------------------------------------------------------- 1 | # mapIt.py - Launches a map in the browser using an address from the 2 | # command line or clipboard. 3 | 4 | import webbrowser, sys, pyperclip 5 | if len(sys.argv) > 1: 6 | # Get the address from command line. 7 | address = ' '.join(sys.argv[1:]) 8 | else: 9 | # TODO: Get address from clipboard. 10 | address = pyperclip.paste() 11 | 12 | webbrowser.open('https://www.google.com/maps/place/' + address) 13 | -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/startup_opportunity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/startup_opportunity.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/team_chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/team_chat.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/things_you_learn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/things_you_learn.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/trash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/trash.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/tv_problems.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/tv_problems.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/ui_change.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/ui_change.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/us_state_names.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/us_state_names.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/voice_commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/voice_commands.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/wifi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/wifi.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd/xkcde.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd/xkcde.png -------------------------------------------------------------------------------- /11_Web_Scraping/xkcd_test/voice_commands.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/11_Web_Scraping/xkcd_test/voice_commands.png -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/alarm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/alarm.wav -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/calc_prod.py: -------------------------------------------------------------------------------- 1 | import time 2 | def calc_prod(): 3 | # Calculate the product of the first 100.000 numbers. 4 | product = 1 5 | for i in range(1, 100000): 6 | product = product * i 7 | return product 8 | 9 | start_time = time.time() 10 | prod = calc_prod() 11 | end_time = time.time() 12 | print('The result is %s digits long.' % (len(str(prod)))) 13 | print('Took %s seconds to calculate.' % (end_time - start_time)) 14 | -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/countdown.py: -------------------------------------------------------------------------------- 1 | # countdown.py - A simple countdown script 2 | 3 | import time, subprocess 4 | 5 | time_left = 60 6 | try: 7 | while time_left > 0: 8 | print(time_left) 9 | time.sleep(1) 10 | time_left = time_left - 1 11 | # TODO: At the end of the countdown, play a sound file. 12 | subprocess.Popen(['open', 'alarm.wav']) 13 | except KeyboardInterrupt: 14 | print ('You canceled the countdown') 15 | -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/hello.txt: -------------------------------------------------------------------------------- 1 | Hello world! -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/multi_download_xkcd.py: -------------------------------------------------------------------------------- 1 | # multidownloadXkcd.py - Downloads XKCD comics using multiple threads. 2 | 3 | import requests, os, bs4, threading 4 | 5 | os.makedirs('xkcd', exist_ok=True) # store comics in ./XKCD 6 | 7 | def download_xkcd(start_comic, end_comic): 8 | for url_number in range(start_comic, end_comic): 9 | # Download the page. 10 | print('Downloading page http://xkcd.com/%s...' % (url_number)) 11 | res = requests.get('http://xkcd.com/%s' % (url_number)) 12 | res.raise_for_status() 13 | 14 | soup = bs4.BeautifulSoup(res.text, 'lxml') 15 | 16 | # Find the URL of the comic image. 17 | comic_elem = soup.select('#comic img') 18 | if comic_elem == []: 19 | print('Could not find comic image.') 20 | else: 21 | comic_url = 'http:' + comic_elem[0].get('src') 22 | # Download the image. 23 | print('Downloading image %s...' % (comic_url)) 24 | res = requests.get(comic_url) 25 | res.raise_for_status() 26 | 27 | # Save the image to ./xkcd. 28 | image_file = open(os.path.join('xkcd', os.path.basename(comic_url)), 'wb') 29 | for chunk in res.iter_content(100000): 30 | image_file.write(chunk) 31 | image_file.close() 32 | 33 | # TODO: Create and start the Thread objects. 34 | download_threads = [] # a list of all the Thread objects 35 | for i in range(0, 1400, 100): # loops 14 times, creates 14 threads 36 | download_thread = threading.Thread(target=download_xkcd, args=(i, i+99)) 37 | download_threads.append(download_threads) 38 | download_thread.start() 39 | 40 | # TODO: Wait for all threads to end. 41 | for download_thread in download_threads: 42 | download_thread.join() 43 | print('Done.') 44 | -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/multi_download_xkcd_time.py: -------------------------------------------------------------------------------- 1 | # multidownloadXkcd.py - Downloads XKCD comics using multiple threads. 2 | # time calculate version, compare with single thread download version 3 | 4 | import requests, os, bs4, threading, time 5 | 6 | os.makedirs('xkcd_my', exist_ok=True) # store comics in ./XKCD 7 | 8 | def download_xkcd(start_comic, end_comic): 9 | for url_number in range(start_comic, end_comic): 10 | # Download the page. 11 | print('Downloading page http://xkcd.com/%s...' % (url_number)) 12 | res = requests.get('http://xkcd.com/%s' % (url_number)) 13 | res.raise_for_status() 14 | 15 | soup = bs4.BeautifulSoup(res.text, 'lxml') 16 | 17 | # Find the URL of the comic image. 18 | comic_elem = soup.select('#comic img') 19 | if comic_elem == []: 20 | print('Could not find comic image.') 21 | else: 22 | comic_url = 'http:' + comic_elem[0].get('src') 23 | # Download the image. 24 | print('Downloading image %s...' % (comic_url)) 25 | res = requests.get(comic_url) 26 | res.raise_for_status() 27 | 28 | # Save the image to ./xkcd. 29 | image_file = open(os.path.join('xkcd_my', os.path.basename(comic_url)), 'wb') 30 | for chunk in res.iter_content(100000): 31 | image_file.write(chunk) 32 | image_file.close() 33 | 34 | # TODO: Create and start the Thread objects. 35 | download_threads = [] # a list of all the Thread objects 36 | start_time = time.time() 37 | for i in range(1, 101, 10): # loops 14 times, creates 14 threads 38 | download_thread = threading.Thread(target=download_xkcd, args=(i, i+10)) 39 | download_threads.append(download_thread) 40 | download_thread.start() 41 | 42 | # TODO: Wait for all threads to end. 43 | for download_thread in download_threads: 44 | download_thread.join() 45 | 46 | end_time = time.time() 47 | print('Done.') 48 | print('You download 100 images and cost %s seconds' % (end_time - start_time)) 49 | -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/multidownloadXkcd.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # multidownloadXkcd.py - Downloads XKCD comics using multiple threads. 3 | 4 | import requests, os, bs4, threading, time 5 | os.makedirs('xkcd', exist_ok=True) # store comics in ./xkcd 6 | 7 | def downloadXkcd(startComic, endComic): 8 | for urlNumber in range(startComic, endComic): 9 | # Download the page. 10 | print('Downloading page http://xkcd.com/%s...' % (urlNumber)) 11 | res = requests.get('http://xkcd.com/%s' % (urlNumber)) 12 | res.raise_for_status() 13 | 14 | soup = bs4.BeautifulSoup(res.text, 'lxml') 15 | 16 | # Find the URL of the comic image. 17 | comicElem = soup.select('#comic img') 18 | if comicElem == []: 19 | print('Could not find comic image.') 20 | else: 21 | comicUrl = 'http:' + comicElem[0].get('src') 22 | # Download the image. 23 | print('Downloading image %s...' % (comicUrl)) 24 | res = requests.get(comicUrl) 25 | res.raise_for_status() 26 | 27 | # Save the image to ./xkcd 28 | imageFile = open(os.path.join('xkcd', os.path.basename(comicUrl)), 'wb') 29 | for chunk in res.iter_content(100000): 30 | imageFile.write(chunk) 31 | imageFile.close() 32 | 33 | # Create and start the Thread objects. 34 | downloadThreads = [] # a list of all the Thread objects 35 | start_time = time.time() 36 | 37 | for i in range(1, 101, 10): # loops 14 times, creates 14 threads 38 | downloadThread = threading.Thread(target=downloadXkcd, args=(i, i + 10)) 39 | downloadThreads.append(downloadThread) 40 | downloadThread.start() 41 | 42 | # Wait for all threads to end. 43 | for downloadThread in downloadThreads: 44 | downloadThread.join() 45 | end_time = time.time() 46 | 47 | print('Done.') 48 | print('You download 100 images and cost %s seconds' % (end_time - start_time)) 49 | -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/single_download_xkcd_time.py: -------------------------------------------------------------------------------- 1 | # download_xkcd.py - Downloads every single XKCD comic. 2 | # time calculate version, compare with single thread download version 3 | 4 | 5 | import requests, os, bs4, time 6 | 7 | page = 1 8 | start_time = time.time() 9 | url = 'http://xkcd.com' #starting url 10 | os.makedirs('xkcd_single', exist_ok=True) # store comics in ./xkcd 11 | while page <= 100: 12 | # TODO: Download the page 13 | print ('Downloading page %s...' % url) 14 | start_time = time.time() 15 | res = requests.get(url) 16 | res.raise_for_status() 17 | 18 | soup = bs4.BeautifulSoup(res.text, 'lxml') 19 | 20 | # TODO: Find the URL of the comic image. 21 | comic_elem = soup.select('#comic img') 22 | if comic_elem == []: 23 | print('Could not find comic image.') 24 | else: 25 | comic_url = 'http:' + comic_elem[0].get('src') 26 | # TODO: Download the image. 27 | print('Downloading image %s...' % (comic_url)) 28 | res = requests.get(comic_url) 29 | res.raise_for_status() 30 | 31 | # TODO: Save the image to ./xkcd. 32 | image_file = open(os.path.join('xkcd_single', os.path.basename(comic_url)), 'wb') 33 | for chunk in res.iter_content(100000): 34 | image_file.write(chunk) 35 | image_file.close() 36 | 37 | # TODO: Get the Prev button's url. 38 | prev_link = soup.select('a[rel="prev"]')[0] 39 | url = 'http://xkcd.com' + prev_link.get('href') 40 | page = page + 1 41 | 42 | end_time = time.time() 43 | print ('Done.') 44 | print('You download 100 images and cost %s seconds' % (end_time - start_time)) 45 | -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/single_download_xkcd_time_2.py: -------------------------------------------------------------------------------- 1 | # multidownloadXkcd.py - Downloads XKCD comics using multiple threads. 2 | # time calculate version, compare with single thread download version 3 | 4 | import requests, os, bs4, threading, time 5 | 6 | os.makedirs('xkcd_my', exist_ok=True) # store comics in ./XKCD 7 | 8 | def download_xkcd(start_comic, end_comic): 9 | for url_number in range(start_comic, end_comic): 10 | # Download the page. 11 | print('Downloading page http://xkcd.com/%s...' % (url_number)) 12 | res = requests.get('http://xkcd.com/%s' % (url_number)) 13 | res.raise_for_status() 14 | 15 | soup = bs4.BeautifulSoup(res.text, 'lxml') 16 | 17 | # Find the URL of the comic image. 18 | comic_elem = soup.select('#comic img') 19 | if comic_elem == []: 20 | print('Could not find comic image.') 21 | else: 22 | comic_url = 'http:' + comic_elem[0].get('src') 23 | # Download the image. 24 | print('Downloading image %s...' % (comic_url)) 25 | res = requests.get(comic_url) 26 | res.raise_for_status() 27 | 28 | # Save the image to ./xkcd. 29 | image_file = open(os.path.join('xkcd_my', os.path.basename(comic_url)), 'wb') 30 | for chunk in res.iter_content(100000): 31 | image_file.write(chunk) 32 | image_file.close() 33 | 34 | start_time = time.time() 35 | print ('start time is %s' % (start_time)) 36 | download_xkcd(1, 101) 37 | 38 | # # TODO: Create and start the Thread objects. 39 | # download_threads = [] # a list of all the Thread objects 40 | # start_time = time.time() 41 | # for i in range(1, 101, 10): # loops 14 times, creates 14 threads 42 | # download_thread = threading.Thread(target=download_xkcd, args=(i, i+10)) 43 | # download_threads.append(download_thread) 44 | # download_thread.start() 45 | # 46 | # # TODO: Wait for all threads to end. 47 | # for download_thread in download_threads: 48 | # download_thread.join() 49 | 50 | end_time = time.time() 51 | print('Done.') 52 | print('You download 100 images and cost %s seconds' % (end_time - start_time)) 53 | -------------------------------------------------------------------------------- /15_Keeping_Time_Scheduling_Tasks_and_Launching_Programs/stopwatch.py: -------------------------------------------------------------------------------- 1 | # stopwatch.py - A simple stopwatch program 2 | 3 | import time 4 | 5 | # Display the program's instructions. 6 | print('Press ENTER to begin. Afterwards, press ENTER to "click" the stopwatch. 7 | Press Ctrl-C to quit.') 8 | input() # press Enter to begin 9 | print('Started.') 10 | start_time = time.time() # get the first lap's start time 11 | last_time =start_time 12 | lap_num = 1 13 | 14 | # TODO: Start tracking the lap times. 15 | try: 16 | while True: 17 | input() 18 | lap_time = round(time.time() - last_time, 2) 19 | total_time = round(time.time() - start_time, 2) 20 | print('Lap #%s: %s (%s)' % (lap_num, total_time, lap_time), end='') 21 | lap_num += 1 22 | last_time = time.time() # reset the last lap time 23 | except KeyboardInterrupt: 24 | # Handle the Ctrl-C exception to keep its error message from displaying. 25 | print('\nDone.') 26 | -------------------------------------------------------------------------------- /Automate_online-materials/allMyCats1.py: -------------------------------------------------------------------------------- 1 | print('Enter the name of cat 1:') 2 | catName1 = input() 3 | print('Enter the name of cat 2:') 4 | catName2 = input() 5 | print('Enter the name of cat 3:') 6 | catName3 = input() 7 | print('Enter the name of cat 4:') 8 | catName4 = input() 9 | print('Enter the name of cat 5:') 10 | catName5 = input() 11 | print('Enter the name of cat 6:') 12 | catName6 = input() 13 | print('The cat names are:') 14 | print(catName1 + ' ' + catName2 + ' ' + catName3 + ' ' + catName4 + ' ' + catName5 + ' ' + catName6) 15 | -------------------------------------------------------------------------------- /Automate_online-materials/allMyCats2.py: -------------------------------------------------------------------------------- 1 | catNames = [] 2 | while True: 3 | print('Enter the name of cat ' + str(len(catNames) + 1) + ' (Or enter nothing to stop.):') 4 | name = input() 5 | if name == '': 6 | break 7 | catNames = catNames + [name] # list concatenation 8 | print('The cat names are:') 9 | for name in catNames: 10 | print(' ' + name) 11 | -------------------------------------------------------------------------------- /Automate_online-materials/backupToZip.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # backupToZip.py 3 | # Copies an entire folder and its contents into 4 | # a zip file whose filename increments. 5 | 6 | import zipfile, os 7 | 8 | def backupToZip(folder): 9 | # Backup the entire contents of "folder" into a zip file. 10 | 11 | folder = os.path.abspath(folder) # make sure folder is absolute 12 | 13 | # Figure out the filename this code should used based on 14 | # what files already exist. 15 | number = 1 16 | while True: 17 | zipFilename = os.path.basename(folder) + '_' + str(number) + '.zip' 18 | if not os.path.exists(zipFilename): 19 | break 20 | number = number + 1 21 | 22 | # Create the zip file. 23 | print('Creating %s...' % (zipFilename)) 24 | backupZip = zipfile.ZipFile(zipFilename, 'w') 25 | 26 | # Walk the entire folder tree and compress the files in each folder. 27 | for foldername, subfolders, filenames in os.walk(folder): 28 | print('Adding files in %s...' % (foldername)) 29 | # Add the current folder to the ZIP file. 30 | backupZip.write(foldername) 31 | 32 | # Add all the files in this folder to the ZIP file. 33 | for filename in filenames: 34 | if filename.startswith(os.path.basename(folder) + '_') and filename.endswith('.zip'): 35 | continue # don't backup the backup ZIP files 36 | backupZip.write(os.path.join(foldername, filename)) 37 | backupZip.close() 38 | print('Done.') 39 | 40 | 41 | backupToZip('C:\\delicious') 42 | -------------------------------------------------------------------------------- /Automate_online-materials/birthdays.py: -------------------------------------------------------------------------------- 1 | birthdays = {'Alice': 'Apr 1', 'Bob': 'Dec 12', 'Carol': 'Mar 4'} 2 | 3 | while True: 4 | print('Enter a name: (blank to quit)') 5 | name = input() 6 | if name == '': 7 | break 8 | 9 | if name in birthdays: 10 | print(birthdays[name] + ' is the birthday of ' + name) 11 | else: 12 | print('I do not have birthday information for ' + name) 13 | print('What is their birthday?') 14 | bday = input() 15 | birthdays[name] = bday 16 | print('Birthday database updated.') 17 | -------------------------------------------------------------------------------- /Automate_online-materials/boxPrint.py: -------------------------------------------------------------------------------- 1 | def boxPrint(symbol, width, height): 2 | if len(symbol) != 1: 3 | raise Exception('Symbol must be a single character string.') 4 | if width <= 2: 5 | raise Exception('Width must be greater than 2.') 6 | if height <= 2: 7 | raise Exception('Height must be greater than 2.') 8 | 9 | print(symbol * width) 10 | for i in range(height - 2): 11 | print(symbol + (' ' * (width - 2)) + symbol) 12 | print(symbol * width) 13 | 14 | for sym, w, h in (('*', 4, 4), ('O', 20, 5), ('x', 1, 3), ('ZZ', 3, 3)): 15 | try: 16 | boxPrint(sym, w, h) 17 | except Exception as err: 18 | print('An exception happened: ' + str(err)) 19 | -------------------------------------------------------------------------------- /Automate_online-materials/buggyAddingProgram.py: -------------------------------------------------------------------------------- 1 | print('Enter the first number to add:') 2 | first = input() 3 | print('Enter the second number to add:') 4 | second = input() 5 | print('Enter the third number to add:') 6 | third = input() 7 | print('The sum is ' + first + second + third) 8 | -------------------------------------------------------------------------------- /Automate_online-materials/bulletPointAdder.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # Adds Wikipedia bullet points to the start 3 | # of each line of text on the clipboard. 4 | 5 | import pyperclip 6 | text = pyperclip.paste() 7 | 8 | #Separate lines and add stars. 9 | lines = text.split('\n') 10 | for i in range(len(lines)): # loop through all indexes for "lines" list 11 | lines[i] = '* ' + lines[i] # add star to each string in "lines" list 12 | text = '\n'.join(lines) 13 | pyperclip.copy(text) 14 | -------------------------------------------------------------------------------- /Automate_online-materials/calcProd.py: -------------------------------------------------------------------------------- 1 | import time 2 | startTime = time.time() 3 | # Calculate the product of the first 100,000 numbers. 4 | product = 1 5 | for i in range(1, 100000): 6 | product = product * i 7 | endTime = time.time() 8 | print('The result is %s digits long.' % (len(str(product)))) 9 | print('Took %s seconds to calculate.' % (endTime - startTime)) 10 | -------------------------------------------------------------------------------- /Automate_online-materials/catlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/catlogo.png -------------------------------------------------------------------------------- /Automate_online-materials/catnapping.py: -------------------------------------------------------------------------------- 1 | print('''Dear Alice, 2 | 3 | Eve's cat has been arrested for catnapping, cat burglary, and extortion. 4 | 5 | Sincerely, 6 | Bob''') 7 | -------------------------------------------------------------------------------- /Automate_online-materials/censuspopdata.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/censuspopdata.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/characterCount.py: -------------------------------------------------------------------------------- 1 | message = 'It was a bright cold day in April, and the clocks were striking thirteen.' 2 | count = {} 3 | 4 | for character in message: 5 | count.setdefault(character, 0) 6 | count[character] = count[character] + 1 7 | 8 | print(count) 9 | -------------------------------------------------------------------------------- /Automate_online-materials/coinFlip.py: -------------------------------------------------------------------------------- 1 | import random 2 | heads = 0 3 | for i in range(1, 1001): 4 | if random.randint(0, 1) == 1: 5 | heads = heads + 1 6 | if i == 500: 7 | print('Halfway done!') 8 | print('Heads came up ' + str(heads) + ' times.') 9 | -------------------------------------------------------------------------------- /Automate_online-materials/combinePdfs.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # combinePdfs.py - Combines all the PDFs in the current working directory into 3 | # a single PDF. 4 | 5 | import PyPDF2, os 6 | 7 | # Get all the PDF filenames. 8 | pdfFiles = [] 9 | for filename in os.listdir('.'): 10 | if filename.endswith('.pdf'): 11 | pdfFiles.append(filename) 12 | pdfFiles.sort() 13 | 14 | pdfWriter = PyPDF2.PdfFileWriter() 15 | 16 | # Loop through all the PDF files. 17 | for filename in pdfFiles: 18 | pdfFileObj = open(filename, 'rb') 19 | pdfReader = PyPDF2.PdfFileReader(pdfFileObj) 20 | 21 | # Loop through all the pages (except the first) and add them. 22 | for pageNum in range(1, pdfReader.numPages): 23 | pageObj = pdfReader.getPage(pageNum) 24 | pdfWriter.addPage(pageObj) 25 | 26 | # Save the resulting PDF to a file. 27 | pdfOutput = open('allminutes.pdf', 'wb') 28 | pdfWriter.write(pdfOutput) 29 | pdfOutput.close() 30 | -------------------------------------------------------------------------------- /Automate_online-materials/combinedminutes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/combinedminutes.pdf -------------------------------------------------------------------------------- /Automate_online-materials/countdown.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # countdown.py - A simple countdown script. 3 | 4 | import time, subprocess 5 | 6 | timeLeft = 60 7 | while timeLeft > 0: 8 | print(timeLeft, end='') 9 | time.sleep(1) 10 | timeLeft = timeLeft - 1 11 | 12 | # At the end of the countdown, play a sound file. 13 | subprocess.Popen(['start','alarm.wav'], shell=True) 14 | -------------------------------------------------------------------------------- /Automate_online-materials/demo.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/demo.docx -------------------------------------------------------------------------------- /Automate_online-materials/dimensions.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/dimensions.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/downloadXkcd.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # downloadXkcd.py - Downloads every single XKCD comic. 3 | 4 | import requests, os, bs4 5 | 6 | url = 'http://xkcd.com' # starting url 7 | os.makedirs('xkcd', exist_ok=True) # store comics in ./xkcd 8 | while not url.endswith('#'): 9 | # Download the page. 10 | print('Downloading page %s...' % url) 11 | res = requests.get(url) 12 | res.raise_for_status() 13 | 14 | soup = bs4.BeautifulSoup(res.text) 15 | 16 | # Find the URL of the comic image. 17 | comicElem = soup.select('#comic img') 18 | if comicElem == []: 19 | print('Could not find comic image.') 20 | else: 21 | comicUrl = comicElem[0].get('src') 22 | # Download the image. 23 | print('Downloading image %s...' % (comicUrl)) 24 | res = requests.get(comicUrl) 25 | res.raise_for_status() 26 | 27 | # Save the image to ./xkcd 28 | imageFile = open(os.path.join('xkcd', os.path.basename(comicUrl)), 'wb') 29 | for chunk in res.iter_content(100000): 30 | imageFile.write(chunk) 31 | imageFile.close() 32 | 33 | # Get the Prev button's url. 34 | prevLink = soup.select('a[rel="prev"]')[0] 35 | url = 'http://xkcd.com' + prevLink.get('href') 36 | 37 | print('Done.') 38 | -------------------------------------------------------------------------------- /Automate_online-materials/duesRecords.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/duesRecords.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/encrypted.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/encrypted.pdf -------------------------------------------------------------------------------- /Automate_online-materials/encryptedminutes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/encryptedminutes.pdf -------------------------------------------------------------------------------- /Automate_online-materials/errorExample.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | bacon() 3 | 4 | def bacon(): 5 | raise Exception('This is the error message.') 6 | 7 | spam() 8 | -------------------------------------------------------------------------------- /Automate_online-materials/example.csv: -------------------------------------------------------------------------------- 1 | 4/5/2014 13:34,Apples,73 2 | 4/5/2014 3:41,Cherries,85 3 | 4/6/2014 12:46,Pears,14 4 | 4/8/2014 8:59,Oranges,52 5 | 4/10/2014 2:07,Apples,152 6 | 4/10/2014 18:10,Bananas,23 7 | 4/10/2014 2:40,Strawberries,98 8 | -------------------------------------------------------------------------------- /Automate_online-materials/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | The Website Title 4 | 5 |

Download my Python book from my website.

6 |

Learn Python the easy way!

7 |

By Al Sweigart

8 | -------------------------------------------------------------------------------- /Automate_online-materials/example.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/example.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/example.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/example.zip -------------------------------------------------------------------------------- /Automate_online-materials/excelSpreadsheets.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/excelSpreadsheets.zip -------------------------------------------------------------------------------- /Automate_online-materials/exitExample.py: -------------------------------------------------------------------------------- 1 | import sys 2 | while True: 3 | print('Type exit to exit.') 4 | response = input() 5 | if response == 'exit': 6 | sys.exit() 7 | print('You typed ' + response + '.') -------------------------------------------------------------------------------- /Automate_online-materials/factorialLog.py: -------------------------------------------------------------------------------- 1 | import logging 2 | logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') 3 | logging.debug('Start of program') 4 | 5 | def factorial(n): 6 | logging.debug('Start of factorial(%s%%)' % (n)) 7 | total = 1 8 | for i in range(1, n + 1): 9 | total *= i 10 | logging.debug('i is ' + str(i) + ', total is ' + str(total)) 11 | return total 12 | logging.debug('End of factorial(%s%%)' % (n)) 13 | 14 | print(factorial(5)) 15 | logging.debug('End of program') -------------------------------------------------------------------------------- /Automate_online-materials/fiveTimes.py: -------------------------------------------------------------------------------- 1 | print('My name is') 2 | for i in range(5): 3 | print('Jimmy Five Times (' + str(i) + ')') -------------------------------------------------------------------------------- /Automate_online-materials/formFiller.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # formFiller.py - Automatically fills in the form. 3 | 4 | import pyautogui, time 5 | 6 | # Set these to the correct coordinates for your particular computer. 7 | nameField = (648, 319) 8 | submitButton = (651, 817) 9 | submitButtonColor = (75, 141, 249) 10 | submitAnotherLink = (760, 224) 11 | 12 | formData = [{'name': 'Alice', 'fear': 'eavesdroppers', 'source': 'wand', 'robocop': 4, 'comments': 'Tell Bob I said hi.'}, 13 | {'name': 'Bob', 'fear': 'bees', 'source': 'amulet', 'robocop': 4, 'comments': 'n/a'}, 14 | {'name': 'Carol', 'fear': 'puppets', 'source': 'crystal ball', 'robocop': 1, 'comments': 'Please take the puppets out of the break room.'}, 15 | {'name': 'Alex Murphy', 'fear': 'ED-209', 'source': 'money', 'robocop': 5, 'comments': 'Protect the innocent. Serve the public trust. Uphold the law.'}, 16 | ] 17 | 18 | pyautogui.PAUSE = 0.5 19 | 20 | for person in formData: 21 | # Give the user a chance to kill the script. 22 | print('>>> 5 SECOND PAUSE TO LET USER PRESS CTRL-C <<<') 23 | time.sleep(5) 24 | 25 | # Wait until the form page has loaded. 26 | while not pyautogui.pixelMatchesColor(submitButton[0], submitButton[1], submitButtonColor): 27 | time.sleep(0.5) 28 | 29 | print('Entering %s info...' % (person['name'])) 30 | pyautogui.click(nameField[0], nameField[1]) 31 | 32 | # Fill out the Name field. 33 | pyautogui.typewrite(person['name'] + '\t') 34 | 35 | # Fill out the Greatest Fear(s) field. 36 | pyautogui.typewrite(person['fear'] + '\t') 37 | 38 | # Fill out the Source of Wizard Powers field. 39 | if person['source'] == 'wand': 40 | pyautogui.typewrite(['down', '\t']) 41 | elif person['source'] == 'amulet': 42 | pyautogui.typewrite(['down', 'down', '\t']) 43 | elif person['source'] == 'crystal ball': 44 | pyautogui.typewrite(['down', 'down', 'down', '\t']) 45 | elif person['source'] == 'money': 46 | pyautogui.typewrite(['down', 'down', 'down', 'down', '\t']) 47 | 48 | # Fill out the Robocop field. 49 | if person['robocop'] == 1: 50 | pyautogui.typewrite([' ', '\t']) 51 | elif person['robocop'] == 2: 52 | pyautogui.typewrite(['right', '\t']) 53 | elif person['robocop'] == 3: 54 | pyautogui.typewrite(['right', 'right', '\t']) 55 | elif person['robocop'] == 4: 56 | pyautogui.typewrite(['right', 'right', 'right', '\t']) 57 | elif person['robocop'] == 5: 58 | pyautogui.typewrite(['right', 'right', 'right', 'right', '\t']) 59 | 60 | # Fill out the Additional comments field. 61 | pyautogui.typewrite(person['comments'] + '\t') 62 | 63 | # Click Submit. 64 | pyautogui.press('enter') 65 | 66 | # Wait until form page has loaded. 67 | print('Clicked Submit.') 68 | time.sleep(5) 69 | 70 | # Click the Submit another response link. 71 | pyautogui.click(submitAnotherLink[0], submitAnotherLink[1]) 72 | -------------------------------------------------------------------------------- /Automate_online-materials/freezeExample.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/freezeExample.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/getDocxText.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | 3 | import docx 4 | 5 | def getDocxText(filename): 6 | doc = docx.Document(filename) 7 | fullText = [] 8 | for para in doc.paragraphs: 9 | fullText.append(para.text) 10 | return '\n\n'.join(fullText) 11 | -------------------------------------------------------------------------------- /Automate_online-materials/guessTheNumber.py: -------------------------------------------------------------------------------- 1 | # This is a guess the number game. 2 | import random 3 | secretNumber = random.randint(1, 20) 4 | print('I am thinking of a number between 1 and 20.') 5 | 6 | # Ask the player to guess 6 times. 7 | for guessesTaken in range(1, 7): 8 | print('Take a guess.') 9 | guess = int(input()) 10 | 11 | if guess < secretNumber: 12 | print('Your guess is too low.') 13 | elif guess > secretNumber: 14 | print('Your guess is too high.') 15 | else: 16 | break # This condition is the correct guess! 17 | 18 | if guess == secretNumber: 19 | print('Good job! You guessed my number in ' + str(guessesTaken) + ' guesses!') 20 | else: 21 | print('Nope. The number I was thinking of was ' + str(secretNumber)) 22 | -------------------------------------------------------------------------------- /Automate_online-materials/guests.txt: -------------------------------------------------------------------------------- 1 | Prof. Plum 2 | Miss Scarlet 3 | Col. Mustard 4 | Al Sweigart 5 | Robocop -------------------------------------------------------------------------------- /Automate_online-materials/headings.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/headings.docx -------------------------------------------------------------------------------- /Automate_online-materials/hello.py: -------------------------------------------------------------------------------- 1 | # This program says hello and asks for my name. 2 | 3 | print('Hello world!') 4 | print('What is your name?') # ask for their name 5 | myName = input() 6 | print('It is good to meet you, ' + myName) 7 | print('The length of your name is:') 8 | print(len(myName)) 9 | print('What is your age?') # ask for their age 10 | myAge = input() 11 | print('You will be ' + str(int(myAge) + 1) + ' in a year.') 12 | -------------------------------------------------------------------------------- /Automate_online-materials/helloFunc.py: -------------------------------------------------------------------------------- 1 | def hello(): 2 | print('Howdy!') 3 | print('Howdy!!!') 4 | print('Hello there.') 5 | 6 | hello() 7 | hello() 8 | hello() 9 | -------------------------------------------------------------------------------- /Automate_online-materials/helloFunc2.py: -------------------------------------------------------------------------------- 1 | def hello(name): 2 | print('Hello ' + name) 3 | 4 | hello('Alice') 5 | hello('Bob') 6 | -------------------------------------------------------------------------------- /Automate_online-materials/helloworld.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/helloworld.docx -------------------------------------------------------------------------------- /Automate_online-materials/inventory.py: -------------------------------------------------------------------------------- 1 | # inventory.py 2 | stuff = {'rope': 1, 'torch': 6, 'gold coin': 42, 'dagger': 1, 'arrow': 12} 3 | 4 | def display_inventory(inventory): 5 | print("Inventory:") 6 | item_total = 0 7 | for k, v in inventory.items(): 8 | print(str(v) + ' ' + k) 9 | item_total += v 10 | print("Total number of items: " + str(item_total)) 11 | 12 | display_inventory(stuff) 13 | -------------------------------------------------------------------------------- /Automate_online-materials/isPhoneNumber.py: -------------------------------------------------------------------------------- 1 | def isPhoneNumber(text): 2 | if len(text) != 12: 3 | return False # not phone number-sized 4 | for i in range(0, 3): 5 | if not text[i].isdecimal(): 6 | return False # not an area code 7 | if text[3] != '-': 8 | return False # does not have first hyphen 9 | for i in range(4, 7): 10 | if not text[i].isdecimal(): 11 | return False # does not have first 3 digits 12 | if text[7] != '-': 13 | return False # does not have second hyphen 14 | for i in range(8, 12): 15 | if not text[i].isdecimal(): 16 | return False # does not have last 4 digits 17 | return True # "text" is a phone number! 18 | 19 | print('415-555-4242 is a phone number:') 20 | print(isPhoneNumber('415-555-4242')) 21 | print('Moshi moshi is a phone number:') 22 | print(isPhoneNumber('Moshi moshi')) 23 | -------------------------------------------------------------------------------- /Automate_online-materials/littleKid.py: -------------------------------------------------------------------------------- 1 | if name == 'Alice': 2 | print('Hi, Alice.') 3 | elif age < 12: 4 | print('You are not Alice, kiddo.') 5 | else: 6 | print('You are neither Alice nor a little kid.') 7 | -------------------------------------------------------------------------------- /Automate_online-materials/lucky.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # lucky.py - Opens several Google search results. 3 | 4 | import requests, sys, webbrowser, bs4 5 | 6 | print('Googling...') # display text while downloading the Google page 7 | res = requests.get('http://google.com/search?q=' + ' '.join(sys.argv[1:])) 8 | res.raise_for_status() 9 | 10 | # Retrieve top search result links. 11 | soup = bs4.BeautifulSoup(res.text) 12 | 13 | # Open a browser tab for each result. 14 | linkElems = soup.select('.r a') 15 | numOpen = min(5, len(linkElems)) 16 | for i in range(numOpen): 17 | webbrowser.open('http://google.com' + linkElems[i].get('href')) 18 | -------------------------------------------------------------------------------- /Automate_online-materials/magic8Ball.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | def getAnswer(answerNumber): 4 | if answerNumber == 1: 5 | return 'It is certain' 6 | elif answerNumber == 2: 7 | return 'It is decidely decidedly so' 8 | elif answerNumber == 3: 9 | return 'Yes' 10 | elif answerNumber == 4: 11 | return 'Reply hazy try again' 12 | elif answerNumber == 5: 13 | return 'Ask again later' 14 | elif answerNumber == 6: 15 | return 'Concentrate and ask again' 16 | elif answerNumber == 7: 17 | return 'My reply is no' 18 | elif answerNumber == 8: 19 | return 'Outlook not so good' 20 | elif answerNumber == 9: 21 | return 'Very doubtful' 22 | 23 | r = random.randint(1, 9) 24 | fortune = getAnswer(r) 25 | print(fortune) 26 | -------------------------------------------------------------------------------- /Automate_online-materials/magic8Ball2.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | messages = ['It is certain', 4 | 'It is decidedly so', 5 | 'Yes definitely', 6 | 'Reply hazy try again', 7 | 'Ask again later', 8 | 'Concentrate and ask again', 9 | 'My reply is no', 10 | 'Outlook not so good', 11 | 'Very doubtful'] 12 | 13 | print(messages[random.randint(0, len(messages) - 1)]) 14 | -------------------------------------------------------------------------------- /Automate_online-materials/mapIt.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mapIt.py - Launches a map in the browser using an address from the 3 | # command line or clipboard. 4 | 5 | import webbrowser, sys, pyperclip 6 | 7 | if len(sys.argv) > 1: 8 | # Get address from command line. 9 | address = ' '.join(sys.argv[1:]) 10 | else: 11 | # Get address from clipboard. 12 | address = pyperclip.paste() 13 | 14 | webbrowser.open('https://www.google.com/maps/place/' + address) 15 | -------------------------------------------------------------------------------- /Automate_online-materials/mcb.pyw: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mcb.pyw - Saves and loads pieces of text to the clipboard. 3 | # Usage: py.exe mcb.pyw save - Saves clipboard to keyword. 4 | # py.exe mcb.pyw - Loads keyword to clipboard. 5 | # py.exe mcb.pyw list - Loads all keywords to clipboard. 6 | 7 | import shelve, pyperclip, sys 8 | 9 | mcbShelf = shelve.open('mcb') 10 | 11 | # Save clipboard content. 12 | if len(sys.argv) == 3 and sys.argv[1].lower() == 'save': 13 | mcbShelf[sys.argv[2]] = pyperclip.paste() 14 | elif len(sys.argv) == 2: 15 | # List keywords and load content. 16 | if sys.argv[1].lower() == 'list': 17 | pyperclip.copy(str(list(mcbShelf.keys()))) 18 | elif sys.argv[1] in mcbShelf: 19 | pyperclip.copy(mcbShelf[sys.argv[1]]) 20 | 21 | mcbShelf.close() 22 | -------------------------------------------------------------------------------- /Automate_online-materials/meetingminutes.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/meetingminutes.pdf -------------------------------------------------------------------------------- /Automate_online-materials/meetingminutes2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/meetingminutes2.pdf -------------------------------------------------------------------------------- /Automate_online-materials/merged.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/merged.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/mouseNow.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mouseNow.py - Displays the mouse cursor's current position. 3 | import pyautogui 4 | print('Press Ctrl-C to quit.') 5 | try: 6 | while True: 7 | # Get and print the mouse coordinates. 8 | x, y = pyautogui.position() 9 | positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4) 10 | print(positionStr, end='') 11 | print('\b' * len(positionStr), end='', flush=True) 12 | 13 | except KeyboardInterrupt: 14 | print('\nDone.') 15 | -------------------------------------------------------------------------------- /Automate_online-materials/mouseNow2.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # mouseNow.py - Displays the mouse cursor's current position. 3 | import pyautogui 4 | print('Press Ctrl-C to quit.') 5 | try: 6 | while True: 7 | # Get and print the mouse coordinates. 8 | x, y = pyautogui.position() 9 | positionStr = 'X: ' + str(x).rjust(4) + ' Y: ' + str(y).rjust(4) 10 | pixelColor = pyautogui.screenshot().getpixel((x, y)) 11 | positionStr += ' RGB: (' + str(pixelColor[0]).rjust(3) 12 | positionStr += ', ' + str(pixelColor[1]).rjust(3) 13 | positionStr += ', ' + str(pixelColor[2]).rjust(3) + ')' 14 | print(positionStr, end='') 15 | print('\b' * len(positionStr), end='', flush=True) 16 | 17 | except KeyboardInterrupt: 18 | print('\nDone.') 19 | -------------------------------------------------------------------------------- /Automate_online-materials/multidownloadXkcd.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # multidownloadXkcd.py - Downloads XKCD comics using multiple threads. 3 | 4 | import requests, os, bs4, threading 5 | os.makedirs('xkcd', exist_ok=True) # store comics in ./xkcd 6 | 7 | def downloadXkcd(startComic, endComic): 8 | for urlNumber in range(startComic, endComic): 9 | # Download the page. 10 | print('Downloading page http://xkcd.com/%s...' % (urlNumber)) 11 | res = requests.get('http://xkcd.com/%s' % (urlNumber)) 12 | res.raise_for_status() 13 | 14 | soup = bs4.BeautifulSoup(res.text) 15 | 16 | # Find the URL of the comic image. 17 | comicElem = soup.select('#comic img') 18 | if comicElem == []: 19 | print('Could not find comic image.') 20 | else: 21 | comicUrl = comicElem[0].get('src') 22 | # Download the image. 23 | print('Downloading image %s...' % (comicUrl)) 24 | res = requests.get(comicUrl) 25 | res.raise_for_status() 26 | 27 | # Save the image to ./xkcd 28 | imageFile = open(os.path.join('xkcd', os.path.basename(comicUrl)), 'wb') 29 | for chunk in res.iter_content(100000): 30 | imageFile.write(chunk) 31 | imageFile.close() 32 | 33 | # Create and start the Thread objects. 34 | downloadThreads = [] # a list of all the Thread objects 35 | for i in range(0, 1400, 100): # loops 14 times, creates 14 threads 36 | downloadThread = threading.Thread(target=downloadXkcd, args=(i, i + 99)) 37 | downloadThreads.append(downloadThread) 38 | downloadThread.start() 39 | 40 | # Wait for all threads to end. 41 | for downloadThread in downloadThreads: 42 | downloadThread.join() 43 | print('Done.') 44 | -------------------------------------------------------------------------------- /Automate_online-materials/multipleParagraphs.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/multipleParagraphs.docx -------------------------------------------------------------------------------- /Automate_online-materials/myPets.py: -------------------------------------------------------------------------------- 1 | myPets = ['Zophie', 'Pooka', 'Fat-tail'] 2 | print('Enter a pet name:') 3 | name = input() 4 | if name not in myPets: 5 | print('I do not have a pet named ' + name) 6 | else: 7 | print(name + ' is my pet.') 8 | -------------------------------------------------------------------------------- /Automate_online-materials/passingReference.py: -------------------------------------------------------------------------------- 1 | def eggs(someParameter): 2 | someParameter.append('Hello') 3 | 4 | spam = [1, 2, 3] 5 | eggs(spam) 6 | print(spam) 7 | -------------------------------------------------------------------------------- /Automate_online-materials/phoneAndEmail.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # phoneAndEmail.py - Finds phone numbers and email addresses on the clipboard. 3 | 4 | import pyperclip, re 5 | 6 | phoneRegex = re.compile(r'''( 7 | (\d{3}|\(\d{3}\))? # area code 8 | (\s|-|\.)? # separator 9 | (\d{3}) # first 3 digits 10 | (\s|-|\.) # separator 11 | (\d{4}) # last 4 digits 12 | (\s*(ext|x|ext.)\s*(\d{2,5}))? # extension 13 | )''', re.VERBOSE) 14 | 15 | # Create email regex. 16 | emailRegex = re.compile(r'''( 17 | [a-zA-Z0-9._%+-]+ # username 18 | @ # @ symbol 19 | [a-zA-Z0-9.-]+ # domain name 20 | (\.[a-zA-Z]{2,4}){1,2} # dot-something 21 | )''', re.VERBOSE) 22 | 23 | # Find matches in clipboard text. 24 | text = str(pyperclip.paste()) 25 | 26 | matches = [] 27 | for groups in phoneRegex.findall(text): 28 | phoneNum = '-'.join([groups[1], groups[3], groups[5]]) 29 | if groups[8] != '': 30 | phoneNum += ' x' + groups[8] 31 | matches.append(phoneNum) 32 | for groups in emailRegex.findall(text): 33 | matches.append(groups[0]) 34 | 35 | # Copy results to the clipboard. 36 | if len(matches) > 0: 37 | pyperclip.copy('\n'.join(matches)) 38 | print('Copied to clipboard:') 39 | print('\n'.join(matches)) 40 | else: 41 | print('No phone numbers or email addresses found.') 42 | -------------------------------------------------------------------------------- /Automate_online-materials/picnicTable.py: -------------------------------------------------------------------------------- 1 | def printPicnic(itemsDict, leftWidth, rightWidth): 2 | print('PICNIC ITEMS'.center(leftWidth + rightWidth, '-')) 3 | for k, v in itemsDict.items(): 4 | print(k.ljust(leftWidth, '.') + str(v).rjust(rightWidth)) 5 | 6 | picnicItems = {'sandwiches': 4, 'apples': 12, 'cups': 4, 'cookies': 8000} 7 | printPicnic(picnicItems, 12, 5) 8 | printPicnic(picnicItems, 20, 6) 9 | -------------------------------------------------------------------------------- /Automate_online-materials/prettyCharacterCount.py: -------------------------------------------------------------------------------- 1 | import pprint 2 | message = 'It was a bright cold day in April, and the clocks were striking thirteen.' 3 | count = {} 4 | 5 | for character in message: 6 | count.setdefault(character, 0) 7 | count[character] = count[character] + 1 8 | 9 | pprint.pprint(count) 10 | -------------------------------------------------------------------------------- /Automate_online-materials/printRandom.py: -------------------------------------------------------------------------------- 1 | import random 2 | for i in range(5): 3 | print(random.randint(1, 10)) -------------------------------------------------------------------------------- /Automate_online-materials/produceSales.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/produceSales.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/pw.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # pw.py - An insecure password locker program. 3 | 4 | PASSWORDS = {'email': 'F7minlBDDuvMJuxESSKHFhTxFtjVB6', 5 | 'blog': 'VmALvQyKAxiVH5G8v01if1MLZF3sdt', 6 | 'luggage': '12345'} 7 | 8 | import sys, pyperclip 9 | if len(sys.argv) < 2: 10 | print('Usage: py pw.py [account] - copy account password') 11 | sys.exit() 12 | 13 | account = sys.argv[1] # first command line arg is the account name 14 | 15 | if account in PASSWORDS: 16 | pyperclip.copy(PASSWORDS[account]) 17 | print('Password for ' + account + ' copied to clipboard.') 18 | else: 19 | print('There is no account named ' + account) 20 | -------------------------------------------------------------------------------- /Automate_online-materials/quickWeather.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # quickWeather.py - Prints the current weather for a location from the command line. 3 | 4 | import json, requests, sys 5 | 6 | # Compute location from command line arguments. 7 | if len(sys.argv) < 2: 8 | print('Usage: quickWeather.py location') 9 | sys.exit() 10 | location = ' '.join(sys.argv[1:]) 11 | 12 | # Download the JSON data from OpenWeatherMap.org's API 13 | url ='http://api.openweathermap.org/data/2.5/forecast/daily?q=%s&cnt=3' % (location) 14 | response = requests.get(url) 15 | response.raise_for_status() 16 | 17 | # Load JSON data into a Python variable. 18 | weatherData = json.loads(response.text) 19 | 20 | # Print weather descriptions. 21 | w = weatherData['list'] 22 | print('Current weather in %s:' % (location)) 23 | print(w[0]['weather'][0]['main'], '-', w[0]['weather'][0]['description']) 24 | print() 25 | print('Tomorrow:') 26 | print(w[1]['weather'][0]['main'], '-', w[1]['weather'][0]['description']) 27 | print() 28 | print('Day after tomorrow:') 29 | print(w[2]['weather'][0]['main'], '-', w[2]['weather'][0]['description']) 30 | -------------------------------------------------------------------------------- /Automate_online-materials/randomQuizGenerator.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # randomQuizGenerator.py - Creates quizzes with questions and answers in 3 | # random order, along with the answer key. 4 | 5 | import random 6 | 7 | # The quiz data. Keys are states and values are their capitals. 8 | capitals = {'Alabama': 'Montgomery', 'Alaska': 'Juneau', 'Arizona': 'Phoenix', 'Arkansas': 'Little Rock', 'California': 'Sacramento', 'Colorado': 'Denver', 'Connecticut': 'Hartford', 'Delaware': 'Dover', 'Florida': 'Tallahassee', 'Georgia': 'Atlanta', 'Hawaii': 'Honolulu', 'Idaho': 'Boise', 'Illinois': 'Springfield', 'Indiana': 'Indianapolis', 'Iowa': 'Des Moines', 'Kansas': 'Topeka', 'Kentucky': 'Frankfort', 'Louisiana': 'Baton Rouge', 'Maine': 'Augusta', 'Maryland': 'Annapolis', 'Massachusetts': 'Boston', 'Michigan': 'Lansing', 'Minnesota': 'Saint Paul', 'Mississippi': 'Jackson', 'Missouri': 'Jefferson City', 'Montana': 'Helena', 'Nebraska': 'Lincoln', 'Nevada': 'Carson City', 'New Hampshire': 'Concord', 'New Jersey': 'Trenton', 'New Mexico': 'Santa Fe', 'New York': 'Albany', 'North Carolina': 'Raleigh', 'North Dakota': 'Bismarck', 'Ohio': 'Columbus', 'Oklahoma': 'Oklahoma City', 'Oregon': 'Salem', 'Pennsylvania': 'Harrisburg', 'Rhode Island': 'Providence', 'South Carolina': 'Columbia', 'South Dakota': 'Pierre', 'Tennessee': 'Nashville', 'Texas': 'Austin', 'Utah': 'Salt Lake City', 'Vermont': 'Montpelier', 'Virginia': 'Richmond', 'Washington': 'Olympia', 'West Virginia': 'Charleston', 'Wisconsin': 'Madison', 'Wyoming': 'Cheyenne'} 9 | capitalsItems = list(capitals.items()) 10 | 11 | # Generate 35 quiz files. 12 | for quizNum in range(35): 13 | # Create the quiz and answer key files. 14 | quizFile = open('capitalsquiz%s.txt' % (quizNum + 1), 'w') 15 | answerKeyFile = open('capitalsquiz_answers%s.txt' % (quizNum + 1), 'w') 16 | 17 | # Write out the header for the quiz. 18 | quizFile.write('Name:\n\nDate:\n\nPeriod:\n\n') 19 | quizFile.write((' ' * 20) + 'State Capitals Quiz (Form %s)' % (quizNum + 1)) 20 | quizFile.write('\n\n') 21 | 22 | # Shuffle the order of the states. 23 | states = list(capitals.keys()) # get all states in a list 24 | random.shuffle(states) # randomize the order of the states 25 | 26 | # Loop through all 50 states, making a question for each. 27 | for questionNum in range(50): 28 | 29 | # Get right and wrong answers. 30 | correctAnswer = capitals[states[questionNum]] 31 | wrongAnswers = list(capitals.values()) # get a complete list of answers 32 | del wrongAnswers[wrongAnswers.index(correctAnswer)] # remove the right answer 33 | wrongAnswers = random.sample(wrongAnswers, 3) # pick 3 random ones 34 | 35 | answerOptions = wrongAnswers + [correctAnswer] 36 | random.shuffle(answerOptions) # randomize the order of the answers 37 | 38 | # Write the question and answer options to the quiz file. 39 | quizFile.write('%s. What is the capital of %s?\n' % (questionNum + 1, states[questionNum])) 40 | for i in range(4): 41 | quizFile.write(' %s. %s\n' % ('ABCD'[i], answerOptions[i])) 42 | quizFile.write('\n') 43 | 44 | # Write out the answer key to a file. 45 | answerKeyFile.write('%s. %s\n' % (questionNum + 1, 'ABCD'[answerOptions.index(correctAnswer)])) 46 | quizFile.close() 47 | answerKeyFile.close() 48 | -------------------------------------------------------------------------------- /Automate_online-materials/readCensusExcel.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # readCensusExcel.py - Tabulates population and number of census tracts for 3 | # each county. 4 | 5 | import openpyxl, pprint 6 | print('Opening workbook...') 7 | wb = openpyxl.load_workbook('censuspopdata.xlsx') 8 | sheet = wb.get_sheet_by_name('Population by Census Tract') 9 | countyData = {} 10 | # Fill in countyData with each county's population and tracts. 11 | print('Reading rows...') 12 | for row in range(2, sheet.get_highest_row() + 1): 13 | # Each row in the spreadsheet has data for one census tract. 14 | state = sheet['B' + str(row)].value 15 | county = sheet['C' + str(row)].value 16 | pop = sheet['D' + str(row)].value 17 | 18 | # Make sure the key for this state exists. 19 | countyData.setdefault(state, {}) 20 | # Make sure the key for this county in this state exists. 21 | countyData[state].setdefault(county, {'tracts': 0, 'pop': 0}) 22 | 23 | # Each row represents one census tract, so increment by one. 24 | countyData[state][county]['tracts'] += 1 25 | # Increase the county pop by the pop in this census tract. 26 | countyData[state][county]['pop'] += int(pop) 27 | 28 | # Open a new text file and write the contents of countyData to it. 29 | print('Writing results...') 30 | resultFile = open('census2010.py', 'w') 31 | resultFile.write('allData = ' + pprint.pformat(countyData)) 32 | resultFile.close() 33 | print('Done.') 34 | -------------------------------------------------------------------------------- /Automate_online-materials/readDocx.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | 3 | import docx 4 | 5 | def getText(filename): 6 | doc = docx.Document(filename) 7 | fullText = [] 8 | for para in doc.paragraphs: 9 | fullText.append(para.text) 10 | return '\n\n'.join(fullText) 11 | -------------------------------------------------------------------------------- /Automate_online-materials/removeCsvHeader.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # removeCsvHeader.py - Removes the header from all CSV files in the current 3 | # working directory. 4 | 5 | import csv, os 6 | 7 | os.makedirs('headerRemoved', exist_ok=True) 8 | 9 | # Loop through every file in the current working directory. 10 | for csvFilename in os.listdir('.'): 11 | if not csvFilename.endswith('.csv'): 12 | continue # skip non-csv files 13 | 14 | print('Removing header from ' + csvFilename + '...') 15 | 16 | # Read the CSV file in (skipping first row). 17 | csvRows = [] 18 | csvFileObj = open(csvFilename) 19 | readerObj = csv.reader(csvFileObj) 20 | for row in readerObj: 21 | if readerObj.line_num == 1: 22 | continue # skip first row 23 | csvRows.append(row) 24 | csvFileObj.close() 25 | 26 | # Write out the CSV file. 27 | csvFileObj = open(os.path.join('headerRemoved', csvFilename), 'w', newline='') 28 | csvWriter = csv.writer(csvFileObj) 29 | for row in csvRows: 30 | csvWriter.writerow(row) 31 | csvFileObj.close() 32 | -------------------------------------------------------------------------------- /Automate_online-materials/removeCsvHeader.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/removeCsvHeader.zip -------------------------------------------------------------------------------- /Automate_online-materials/renameDates.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # renameDates.py - Renames filenames with American MM-DD-YYYY date format 3 | # to European DD-MM-YYYY. 4 | 5 | import shutil, os, re 6 | 7 | # Create a regex that matches files with the American date format. 8 | datePattern = re.compile(r"""^(.*?) # all text before the date 9 | ((0|1)?\d)- # one or two digits for the month 10 | ((0|1|2|3)?\d)- # one or two digits for the day 11 | ((19|20)\d\d) # four digits for the year (must start with 19 or 20) 12 | (.*?)$ # all text after the date 13 | """, re.VERBOSE) 14 | 15 | # Loop over the files in the working directory. 16 | for amerFilename in os.listdir('.'): 17 | mo = datePattern.search(amerFilename) 18 | 19 | # Skip files without a date. 20 | if mo == None: 21 | continue 22 | 23 | # Get the different parts of the filename. 24 | beforePart = mo.group(1) 25 | monthPart = mo.group(2) 26 | dayPart = mo.group(4) 27 | yearPart = mo.group(6) 28 | afterPart = mo.group(8) 29 | 30 | # Form the European-style filename. 31 | euroFilename = beforePart + dayPart + '-' + monthPart + '-' + yearPart + afterPart 32 | 33 | # Get the full, absolute file paths. 34 | absWorkingDir = os.path.abspath('.') 35 | amerFilename = os.path.join(absWorkingDir, amerFilename) 36 | euroFilename = os.path.join(absWorkingDir, euroFilename) 37 | 38 | # Rename the files. 39 | print('Renaming "%s" to "%s"...' % (amerFilename, euroFilename)) 40 | #shutil.move(amerFilename, euroFilename) # uncomment after testing 41 | -------------------------------------------------------------------------------- /Automate_online-materials/resizeAndAddLogo.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # resizeAndAddLogo.py - Resizes all images in current working directory to fit 3 | # in a 300x300 square, and adds catlogo.png to the lower-right corner. 4 | 5 | import os 6 | from PIL import Image 7 | 8 | SQUARE_FIT_SIZE = 300 9 | LOGO_FILENAME = 'catlogo.png' 10 | 11 | logoIm = Image.open(LOGO_FILENAME) 12 | logoWidth, logoHeight = logoIm.size 13 | 14 | os.makedirs('withLogo', exist_ok=True) 15 | # Loop over all files in the working directory. 16 | for filename in os.listdir('.'): 17 | if not (filename.endswith('.png') or filename.endswith('.jpg')) \ 18 | or filename == LOGO_FILENAME: 19 | continue # skip non-image files and the logo file itself 20 | 21 | im = Image.open(filename) 22 | width, height = im.size 23 | 24 | # Check if image needs to be resized. 25 | if width > SQUARE_FIT_SIZE and height > SQUARE_FIT_SIZE: 26 | # Calculate the new width and height to resize to. 27 | if width > height: 28 | height = int((SQUARE_FIT_SIZE / width) * height) 29 | width = SQUARE_FIT_SIZE 30 | else: 31 | width = int((SQUARE_FIT_SIZE / height) * width) 32 | height = SQUARE_FIT_SIZE 33 | 34 | # Resize the image. 35 | print('Resizing %s...' % (filename)) 36 | im = im.resize((width, height)) 37 | 38 | # Add logo. 39 | print('Adding logo to %s...' % (filename)) 40 | im.paste(logoIm, (width - logoWidth, height - logoHeight), logoIm) 41 | 42 | # Save changes. 43 | im.save(os.path.join('withLogo', filename)) 44 | -------------------------------------------------------------------------------- /Automate_online-materials/restyled.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/restyled.docx -------------------------------------------------------------------------------- /Automate_online-materials/sameName.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | eggs = 'spam local' 3 | print(eggs) # prints 'spam local' 4 | 5 | def bacon(): 6 | eggs = 'bacon local' 7 | print(eggs) # prints 'bacon local' 8 | spam() 9 | print(eggs) # prints 'bacon local' 10 | 11 | eggs = 'global' 12 | bacon() 13 | print(eggs) # prints 'global' 14 | -------------------------------------------------------------------------------- /Automate_online-materials/sameName2.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | global eggs 3 | eggs = 'spam' 4 | 5 | eggs = 'global' 6 | spam() 7 | print(eggs) 8 | -------------------------------------------------------------------------------- /Automate_online-materials/sameName3.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | global eggs # this is the global 3 | eggs = 'spam' 4 | 5 | def bacon(): 6 | eggs = 'bacon' # this is a local 7 | 8 | def ham(): 9 | print(eggs) # this is the global 10 | 11 | eggs = 42 # this is the global 12 | spam() 13 | print(eggs) 14 | -------------------------------------------------------------------------------- /Automate_online-materials/sameName4.py: -------------------------------------------------------------------------------- 1 | def spam(): 2 | print(eggs) # ERROR! 3 | eggs = 'spam local' 4 | 5 | eggs = 'global' 6 | spam() 7 | -------------------------------------------------------------------------------- /Automate_online-materials/sampleChart.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/sampleChart.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/sendDuesReminders.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # sendDuesReminders.py - Sends emails based on their status in spreadsheet. 3 | 4 | import openpyxl, smtplib, sys 5 | 6 | # Open the spreadsheet and get the latest dues status. 7 | wb = openpyxl.load_workbook('duesRecords.xlsx') 8 | sheet = wb.get_sheet_by_name('Sheet1') 9 | 10 | lastCol = sheet.get_highest_column() 11 | latestMonth = sheet.cell(row=1, column=lastCol).value 12 | 13 | unpaidMembers = {} 14 | # Check each member's payment status 15 | for r in range(2, sheet.get_highest_row() + 1): 16 | payment = sheet.cell(row=r, column=lastCol).value 17 | if payment != 'paid': 18 | name = sheet.cell(row=r, column=1).value 19 | email = sheet.cell(row=r, column=2).value 20 | unpaidMembers[name] = email 21 | 22 | # Log in to email account. 23 | smtpObj = smtplib.SMTP('smtp.gmail.com', 587) 24 | smtpObj.ehlo() 25 | smtpObj.starttls() 26 | smtpObj.login('my_email_address@gmail.com', sys.argv[1]) 27 | 28 | # Send out reminder emails. 29 | for name, email in unpaidMembers.items(): 30 | body = 'Subject: %s dues unpaid.\nDear %s,\nRecords show that you have not paid dues for %s. Please make this payment as soon as possible. Thank you!' % (latestMonth, name, latestMonth) 31 | print('Sending email to %s...' % email) 32 | sendmailStatus = smtpObj.sendmail('my_email_address@gmail.com', email, body) 33 | 34 | if sendmailStatus != {}: 35 | print('There was a problem sending email to %s: %s' % (email, sendmailStatus)) 36 | smtpObj.quit() 37 | -------------------------------------------------------------------------------- /Automate_online-materials/stopwatch.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # stopwatch.py - A simple stopwatch program. 3 | 4 | import time 5 | 6 | # Display the program's instructions. 7 | print('Press enter to begin. Afterwards, press ENTER to "click" the stopwatch. Press Ctrl-C to quit.') 8 | input() # press Enter to begin 9 | print('Started.') 10 | startTime = time.time() # get the first lap's start time 11 | lastTime = startTime 12 | lapNum = 1 13 | 14 | # Start tracking the lap times. 15 | try: 16 | while True: 17 | input() 18 | lapTime = round(time.time() - lastTime, 2) 19 | totalTime = round(time.time() - startTime, 2) 20 | print('Lap #%s: %s (%s)' % (lapNum, totalTime, lapTime), end='') 21 | lapNum += 1 22 | lastTime = time.time() # reset the last lap time 23 | except KeyboardInterrupt: 24 | # Handle the Ctrl-C exception to keep its error message from displaying. 25 | print('\nDone.') 26 | -------------------------------------------------------------------------------- /Automate_online-materials/styled.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/styled.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/styles.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/styles.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/swordfish.py: -------------------------------------------------------------------------------- 1 | while True: 2 | print('Who are you?') 3 | name = input() 4 | if name != 'Joe': 5 | continue 6 | print('Hello, Joe. What is the password? (It is a fish.)') 7 | password = input() 8 | if password == 'swordfish': 9 | break 10 | print('Access granted.') -------------------------------------------------------------------------------- /Automate_online-materials/textMyself.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # textMyself.py - Defines the textmyself() function that texts a message 3 | # passed to it as a string. 4 | 5 | # Preset values: 6 | accountSID = 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 7 | authToken = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 8 | myNumber = '+15559998888' 9 | twilioNumber = '+15552225678' 10 | 11 | from twilio.rest import TwilioRestClient 12 | 13 | def textmyself(message): 14 | twilioCli = TwilioRestClient(accountSID, authToken) 15 | twilioCli.messages.create(body=message, from_=twilioNumber, to=myNumber) 16 | -------------------------------------------------------------------------------- /Automate_online-materials/threadDemo.py: -------------------------------------------------------------------------------- 1 | import threading, time 2 | print('Start of program.') 3 | 4 | def takeANap(): 5 | time.sleep(5) 6 | print('Wake up!') 7 | 8 | threadObj = threading.Thread(target=takeANap) 9 | threadObj.start() 10 | 11 | print('End of program.') 12 | -------------------------------------------------------------------------------- /Automate_online-materials/ticTacToe.py: -------------------------------------------------------------------------------- 1 | theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ', 2 | 'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ', 3 | 'low-L': ' ', 'low-M': ' ', 'low-R': ' '} 4 | def printBoard(board): 5 | print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R']) 6 | print('-+-+-') 7 | print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R']) 8 | print('-+-+-') 9 | print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R']) 10 | 11 | turn = 'X' 12 | for i in range(9): 13 | printBoard(theBoard) 14 | print('Turn for ' + turn + '. Move on which space?') 15 | move = input() 16 | theBoard[move] = turn 17 | if turn == 'X': 18 | turn = 'O' 19 | else: 20 | turn = 'X' 21 | printBoard(theBoard) 22 | -------------------------------------------------------------------------------- /Automate_online-materials/torrentStarter.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # Checks for instructions via email and runs them. 3 | # So far, this program checks for BitTorrent "magnet" links and launches the torrent program for them. 4 | 5 | import smtplib, imapclient, pyzmail, logging, traceback, time, subprocess 6 | logging.basicConfig(filename='torrentStarterLog.txt', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s') 7 | 8 | # Configure the program by setting some variables. 9 | MY_EMAIL = 'asweigart@gmail.com' # bot should only respond to me 10 | BOT_EMAIL = 'imaptest@coffeeghost.net' 11 | BOT_EMAIL_PASSWORD = '7|)6S1JS6>euu8p/nTlf' 12 | IMAP_SERVER = 'mail.coffeeghost.net' 13 | SMTP_SERVER = 'mail.coffeeghost.net' 14 | SMTP_PORT = 465 15 | TORRENT_PROGRAM = 'C:\\Program Files (x86)\\qBittorrent\\qbittorrent.exe' 16 | 17 | assert BOT_EMAIL != MY_EMAIL, "Give the bot it's own email address." 18 | 19 | 20 | def getInstructionEmails(): 21 | # Log in to the email imapCli. 22 | logging.debug('Connecting to IMAP server at %s...' % (IMAP_SERVER)) 23 | imapCli = imapclient.IMAPClient(IMAP_SERVER, ssl=True) 24 | imapCli.login(BOT_EMAIL, BOT_EMAIL_PASSWORD) 25 | imapCli.select_folder('INBOX') 26 | logging.debug('Connected.') 27 | 28 | # Fetch all instruction emails. 29 | instructions = [] 30 | UIDs = imapCli.search(['FROM ' + MY_EMAIL]) 31 | rawMessages = imapCli.fetch(UIDs, ['BODY[]']) 32 | for UID in rawMessages.keys(): 33 | # Parse the raw email message. 34 | message = pyzmail.PyzMessage.factory(rawMessages[UID]['BODY[]']) 35 | if message.html_part != None: 36 | body = message.html_part.get_payload().decode(message.html_part.charset) 37 | if message.text_part != None: 38 | # If there's both an html and text part, use the text part. 39 | body = message.text_part.get_payload().decode(message.text_part.charset) 40 | 41 | # Extract instruction from email body. 42 | instructions.append(body) 43 | 44 | # Delete the instruction emails, if there are any. 45 | if len(UIDs) > 0: 46 | imapCli.delete_messages(UIDs) 47 | imapCli.expunge() 48 | 49 | imapCli.logout() 50 | 51 | return instructions 52 | 53 | 54 | def parseInstructionEmail(instruction): 55 | # Send an email response about the task. 56 | responseBody = 'Subject: Instruction completed.\nInstruction received and completed.\nResponse:\n' 57 | 58 | # Parse the email body to figure out the instruction. 59 | lines = instruction.split('\n') 60 | for line in lines: 61 | if line.startswith('magnet:?'): 62 | subprocess.Popen(TORRENT_PROGRAM + ' ' + line) # launch the bittorrent program 63 | responseBody += 'Downloading magnet link.\n' 64 | 65 | # Email the response body to confirm the bot carried out this instruction. 66 | logging.debug('Connecting to SMTP server at %s to send confirmation email...' % (SMTP_SERVER)) 67 | #smtpCli = smtplib.SMTP(SMTP_SERVER, SMTP_PORT) # uncomment one or the other as needed. 68 | smtpCli = smtplib.SMTP_SSL(SMTP_SERVER, SMTP_PORT) # uncomment one or the other as needed. 69 | smtpCli.ehlo() 70 | #smtpCli.starttls() # comment this out if using SMTP_SSL 71 | smtpCli.login(BOT_EMAIL, BOT_EMAIL_PASSWORD) 72 | logging.debug('Connected.') 73 | smtpCli.sendmail(BOT_EMAIL, MY_EMAIL, responseBody) 74 | logging.debug('Confirmation email sent.') 75 | smtpCli.quit() 76 | 77 | 78 | # Start an infinite loop that checks email and carries out instructions. 79 | print('Email bot started. Press Ctrl-C to quit.') 80 | logging.debug('Email bot started.') 81 | while True: 82 | try: 83 | logging.debug('Getting instructions from email...') 84 | instructions = getInstructionEmails() 85 | for instruction in instructions: 86 | logging.debug('Doing instruction: ' + instruction) 87 | parseInstructionEmail(instruction) 88 | except Exception as err: 89 | logging.error(traceback.format_exc()) 90 | 91 | # Wait 15 minutes before checking again 92 | logging.debug('Done processing instructions. Pausing for 15 minutes.') 93 | time.sleep(60 * 15) 94 | -------------------------------------------------------------------------------- /Automate_online-materials/twoPage.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/twoPage.docx -------------------------------------------------------------------------------- /Automate_online-materials/updateProduce.py: -------------------------------------------------------------------------------- 1 | #! python3 2 | # updateProduce.py - Corrects costs in produce sales spreadsheet. 3 | 4 | import openpyxl 5 | 6 | wb = openpyxl.load_workbook('produceSales.xlsx') 7 | sheet = wb.get_sheet_by_name('Sheet') 8 | 9 | # The produce types and their updated prices 10 | PRICE_UPDATES = {'Garlic': 3.07, 11 | 'Celery': 1.19, 12 | 'Lemon': 1.27} 13 | 14 | # Loop through the rows and update the prices. 15 | for rowNum in range(2, sheet.get_highest_row()): # skip the first row 16 | produceName = sheet.cell(row=rowNum, column=1).value 17 | if produceName in PRICE_UPDATES: 18 | sheet.cell(row=rowNum, column=2).value = PRICE_UPDATES[produceName] 19 | 20 | wb.save('updatedProduceSales.xlsx') 21 | -------------------------------------------------------------------------------- /Automate_online-materials/updatedProduceSales.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/updatedProduceSales.xlsx -------------------------------------------------------------------------------- /Automate_online-materials/validateInput.py: -------------------------------------------------------------------------------- 1 | while True: 2 | print('Enter your age:') 3 | age = input() 4 | if age.isdecimal(): 5 | break 6 | print('Please enter a number for your age.') 7 | 8 | while True: 9 | print('Select a new password (letters and numbers only):') 10 | password = input() 11 | if password.isalnum(): 12 | break 13 | print('Passwords can only have letters and numbers.') -------------------------------------------------------------------------------- /Automate_online-materials/vampire.py: -------------------------------------------------------------------------------- 1 | if name == 'Alice': 2 | print('Hi, Alice.') 3 | elif age < 12: 4 | print('You are not Alice, kiddo.') 5 | elif age > 2000: 6 | print('Unlike you, Alice is not an undead, immortal vampire.') 7 | elif age > 100: 8 | print('You are not Alice, grannie.') 9 | -------------------------------------------------------------------------------- /Automate_online-materials/vampire2.py: -------------------------------------------------------------------------------- 1 | if name == 'Alice': 2 | print('Hi, Alice.') 3 | elif age < 12: 4 | print('You are not Alice, kiddo.') 5 | elif age > 100: 6 | print('You are not Alice, grannie.') 7 | elif age > 2000: 8 | print('Unlike you, Alice is not an undead, immortal vampire.') 9 | -------------------------------------------------------------------------------- /Automate_online-materials/watermark.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/watermark.pdf -------------------------------------------------------------------------------- /Automate_online-materials/zeroDivide.py: -------------------------------------------------------------------------------- 1 | def spam(divideBy): 2 | return 42 / divideBy 3 | 4 | try: 5 | print(spam(2)) 6 | print(spam(12)) 7 | print(spam(0)) 8 | print(spam(1)) 9 | except ZeroDivisionError: 10 | print('Error: Invalid argument.') 11 | -------------------------------------------------------------------------------- /Automate_online-materials/zophie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BrambleXu/Automate-the-Boring-Stuff-with-Python-Solutions/21d926efa54f0817f0211ec931a661288b93809d/Automate_online-materials/zophie.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is my notebook for the book [*Automate_the_Boring_Stuff_with_Python*](https://automatetheboringstuff.com/#toc). You could access this book for free. Thanks for the author Al Sweigart, we could have this wonderful learning metirials for python beginners. 2 | 3 | Because the book didn't give solutions for the **Practice Projects**, so I add my solutions for other learners. 4 | 5 | I hope this could help those people who study this book. 6 | 7 | I haven't finish all practice projects. If you finish it, welcome to pull a request to add your solution. 8 | 9 | Environment:`Python 3` 10 | 11 | There are two major part of each chapter. The Chapter Projects and the Practice Projects. 12 | There are also some errors while dealing with the chapter projects.(Like Chapter 15 the multidownloadXkcd.py) 13 | And in each chapter floder, there is one jupyter notebook file, which recording my history with each chapter. 14 | > - [x] [x] means solutions are available for both chapter projects and the practice projects in this chapter. 15 | 16 | - [x] [x] Chapter 2 – Flow Control 17 | - [x] [x] Chapter 3 – Functions 18 | - [x] [x] Chapter 4 – Lists 19 | - [x] [x] Chapter 5 – Dictionaries and Structuring Data 20 | - [x] [x] Chapter 6 – Manipulating Strings 21 | - [x] [x] Chapter 7 – Pattern Matching with Regular Expressions 22 | - [x] [ ] Chapter 8 – Reading and Writing Files 23 | - [ ] [x] Chapter 9 – Organizing Files 24 | - [x] [ ] Chapter 10 – Debugging 25 | - [x] [ ] Chapter 11 – Web Scraping 26 | - [ ] [ ] Chapter 12 – Working with Excel Spreadsheets 27 | - [ ] [ ] Chapter 13 – Working with PDF and Word Documents 28 | - [ ] [ ] Chapter 14 – Working with CSV Files and JSON Data 29 | - [x] [ ] Chapter 15 – Keeping Time, Scheduling Tasks, and Launching Programs 30 | - [ ] [ ] Chapter 16 – Sending Email and Text Messages 31 | - [ ] [ ] Chapter 17 – Manipulating Images 32 | - [ ] [ ] Chapter 18 – Controlling the Keyboard and Mouse with GUI Automation 33 | --------------------------------------------------------------------------------