├── Command_line_arguments.md ├── Control_structures.md ├── Docstrings.md ├── Exception_Handling_and_Debugging.md ├── Executing_external_commands.md ├── Exercises.md ├── File_handling.md ├── Functions.md ├── Further_Reading.md ├── Introduction.md ├── Lists.md ├── Number_and_String_datatypes.md ├── README.md ├── Sequence_Set_Dict_data_types.md ├── Testing.md ├── Text_Processing.md ├── User_input.md ├── exercise_files ├── f1.txt ├── f2.txt ├── f3.txt ├── poem.txt ├── q2a_int_length.py ├── q2b_str_comparison.py ├── q2c_str_same_letters.py ├── q2d_to_num.py ├── q3a_6by7.py ├── q4a_iter_product.py ├── q4b_lowest_value.py ├── q4c_word_slices.py ├── q5c_sort_by_ext.py ├── q6a_one_char_diff.py ├── q6b_alpha_order.py ├── q6c_max_nested.py └── q7c_longest_word.py ├── exercise_solutions ├── f1.txt ├── f2.txt ├── f3.txt ├── poem.txt ├── q1a_usr_ip.py ├── q2a_int_length.py ├── q2b_str_comparison.py ├── q2c_str_same_letters.py ├── q2d_to_num.py ├── q3a_6by7.py ├── q3b_dec_bin.py ├── q4a_iter_product.py ├── q4b_lowest_value.py ├── q4c_word_slices.py ├── q5a_col_sum.py ├── q5b_sum_ints.py ├── q5c_sort_by_ext.py ├── q6a_one_char_diff.py ├── q6b_alpha_order.py ├── q6c_max_nested.py ├── q7_misc.py └── q7c_longest_word.py ├── mini_projects └── pcalc.py └── python_programs ├── calling_shell_commands.py ├── file_reading.py ├── file_reading_error.py ├── file_writing.py ├── for_loop.py ├── functions.py ├── functions_default_arg_value.py ├── functions_main.py ├── hello_world.py ├── if_elif_else.py ├── if_else_oneliner.py ├── inplace_file_editing.py ├── line_count.py ├── list_comprehension.py ├── list_looping.py ├── list_looping_enumeration.py ├── list_of_lists.py ├── loop_with_break.py ├── loop_with_continue.py ├── palindrome.py ├── report.log ├── shell_command_output_redirections.py ├── shell_expansion.py ├── sort_file.py ├── sum2nums_argparse.py ├── sum_of_two_numbers.py ├── syntax_error.py ├── test_list.txt ├── test_palindrome.py ├── triple_quoted_string.py ├── unittest_palindrome.py ├── unittest_palindrome_main.py ├── user_input_exception.py ├── user_input_float.py ├── user_input_int.py ├── user_input_str.py ├── variable_scope_1.py ├── variable_scope_2.py ├── variable_scope_3.py ├── variable_scope_4.py ├── varying_command_line_args.py └── while_loop.py /Command_line_arguments.md: -------------------------------------------------------------------------------- 1 | # Command line arguments 2 | 3 | * [Known number of arguments](#known-number-of-arguments) 4 | * [Varying number of arguments](#varying-number-of-arguments) 5 | * [Using program name in code](#using-program-name-in-code) 6 | * [Command line switches](#command-line-switches) 7 | 8 |
9 | 10 | ### Known number of arguments 11 | 12 | ```python 13 | #!/usr/bin/python3 14 | 15 | import sys 16 | 17 | if len(sys.argv) != 3: 18 | sys.exit("Error: Please provide exactly two numbers as arguments") 19 | else: 20 | (num1, num2) = sys.argv[1:] 21 | total = int(num1) + int(num2) 22 | print("{} + {} = {}".format(num1, num2, total)) 23 | ``` 24 | 25 | * Command line arguments given to Python program are automatically saved in `sys.argv` list 26 | * It is a good idea to let the user know something went wrong 27 | * As the program terminates with error message for wrong usage, use `sys.exit()` for error message (exit status 1) or a custom exit status number 28 | * [Python docs - sys module](https://docs.python.org/3/library/sys.html#module-sys) 29 | 30 | ``` 31 | $ ./sum_of_two_numbers.py 2 3 32 | 2 + 3 = 5 33 | $ echo $? 34 | 0 35 | 36 | $ ./sum_of_two_numbers.py 2 3 7 37 | Error: Please provide exactly two numbers as arguments 38 | $ echo $? 39 | 1 40 | 41 | $ ./sum_of_two_numbers.py 2 'x' 42 | Traceback (most recent call last): 43 | File "./sum_of_two_numbers.py", line 9, in 44 | total = int(num1) + int(num2) 45 | ValueError: invalid literal for int() with base 10: 'x' 46 | $ echo $? 47 | 1 48 | ``` 49 | 50 |
51 | 52 | ### Varying number of arguments 53 | 54 | ```python 55 | #!/usr/bin/python3 56 | 57 | import sys, pathlib, subprocess 58 | 59 | if len(sys.argv) < 2: 60 | sys.exit("Error: Please provide atleast one filename as argument") 61 | 62 | input_files = sys.argv[1:] 63 | files_not_found = [] 64 | 65 | for filename in input_files: 66 | if not pathlib.Path(filename).is_file(): 67 | files_not_found.append("File '{}' not found".format(filename)) 68 | continue 69 | 70 | line_count = subprocess.getoutput('wc -l < ' + filename) 71 | print("{0:40}: {1:4} lines".format(filename, line_count)) 72 | 73 | print("\n".join(files_not_found)) 74 | ``` 75 | 76 | * When dealing with filenames obtained as user input, it is good to do a sanity check before processing 77 | * [Python docs - pathlib](https://docs.python.org/3/library/pathlib.html) 78 | 79 | ``` 80 | $ ./varying_command_line_args.py 81 | Error: Please provide atleast one filename as argument 82 | $ echo $? 83 | 1 84 | 85 | $ #selective output presented 86 | $ ./varying_command_line_args.py *.py 87 | calling_shell_commands.py : 14 lines 88 | for_loop.py : 6 lines 89 | functions_default_arg_value.py : 16 lines 90 | functions.py : 24 lines 91 | hello_world.py : 3 lines 92 | if_elif_else.py : 22 lines 93 | if_else_oneliner.py : 6 lines 94 | shell_command_output_redirections.py : 21 lines 95 | ... 96 | 97 | $ ./varying_command_line_args.py hello_world.py xyz.py for_loop.py abc.py 98 | hello_world.py : 3 lines 99 | for_loop.py : 6 lines 100 | File 'xyz.py' not found 101 | File 'abc.py' not found 102 | ``` 103 | 104 | * use `os` module instead of `pathlib` for file checking if your Python version is not 3.4 and higher 105 | 106 | ```python 107 | import os 108 | 109 | if not os.path.isfile(filename): 110 | sys.exit("File '{}' not found".format(filename)) 111 | ``` 112 | 113 |
114 | 115 | ### Using program name in code 116 | 117 | ```python 118 | #!/usr/bin/python3 119 | 120 | import sys, pathlib, subprocess, re 121 | 122 | if len(sys.argv) != 2: 123 | sys.exit("Error: Please provide exactly one filename as argument") 124 | 125 | program_name = sys.argv[0] 126 | filename = sys.argv[1] 127 | 128 | if not pathlib.Path(filename).is_file(): 129 | sys.exit("File '{}' not found".format(filename)) 130 | 131 | if re.search(r'line_count.py', program_name): 132 | lc = subprocess.getoutput('wc -l < ' + filename) 133 | print("No. of lines in '{}' is: {}".format(filename, lc)) 134 | elif re.search(r'word_count.py', program_name): 135 | wc = subprocess.getoutput('wc -w < ' + filename) 136 | print("No. of words in '{}' is: {}".format(filename, wc)) 137 | else: 138 | sys.exit("Program name '{}' not recognized".format(program_name)) 139 | ``` 140 | 141 | * Same program can be repurposed for different functionalities based on its name 142 | 143 | ``` 144 | $ ./line_count.py if_elif_else.py 145 | No. of lines in 'if_elif_else.py' is: 22 146 | 147 | $ ln -s line_count.py word_count.py 148 | $ ./word_count.py if_elif_else.py 149 | No. of words in 'if_elif_else.py' is: 73 150 | 151 | $ ln -s line_count.py abc.py 152 | $ ./abc.py if_elif_else.py 153 | Program name './abc.py' not recognized 154 | 155 | $ wc -lw if_elif_else.py 156 | 22 73 if_elif_else.py 157 | ``` 158 | 159 |
160 | 161 | ### Command line switches 162 | 163 | ```python 164 | #!/usr/bin/python3 165 | 166 | import argparse, subprocess 167 | 168 | parser = argparse.ArgumentParser() 169 | parser.add_argument('-f', '--file', help="file to be sorted", required=True) 170 | parser.add_argument('-u', help="sort uniquely", action="store_true") 171 | args = parser.parse_args() 172 | 173 | if args.u: 174 | subprocess.call(['sort', '-u', args.file, '-o', args.file]) 175 | else: 176 | subprocess.call(['sort', args.file, '-o', args.file]) 177 | ``` 178 | 179 | * using `argparse` module is a simpler way to build programs that behave like shell commands 180 | * By default it adds a help option `-h` (short form) and `--help` (long form) as well as handles wrong usage 181 | 182 | In this example: 183 | 184 | * `-f` or `--file` option is declared as required option, it accepts an argument (which we treat as input file name in code) 185 | * `-u` is an optional flag 186 | * the `help` argument is used to specify text to be displayed for those options in help message 187 | 188 | ``` 189 | $ ./sort_file.py 190 | usage: sort_file.py [-h] -f FILE [-u] 191 | sort_file.py: error: the following arguments are required: -f/--file 192 | 193 | $ ./sort_file.py -h 194 | usage: sort_file.py [-h] -f FILE [-u] 195 | 196 | optional arguments: 197 | -h, --help show this help message and exit 198 | -f FILE, --file FILE file to be sorted 199 | -u sort uniquely 200 | 201 | $ ./sort_file.py -f test_list.txt 202 | $ cat test_list.txt 203 | async_test 204 | basic_test 205 | input_test 206 | input_test 207 | output_test 208 | sync_test 209 | 210 | $ ./sort_file.py -f test_list.txt -u 211 | $ cat test_list.txt 212 | async_test 213 | basic_test 214 | input_test 215 | output_test 216 | sync_test 217 | 218 | $ ./sort_file.py -f xyz.txt 219 | sort: cannot read: xyz.txt: No such file or directory 220 | ``` 221 | 222 | * specifying positional arguments 223 | 224 | ```python 225 | #!/usr/bin/python3 226 | 227 | import argparse 228 | 229 | parser = argparse.ArgumentParser() 230 | parser.add_argument('num1', type=int, help="first number") 231 | parser.add_argument('num2', type=int, help="second number") 232 | args = parser.parse_args() 233 | 234 | total = args.num1 + args.num2 235 | print("{} + {} = {}".format(args.num1, args.num2, total)) 236 | ``` 237 | 238 | * more readable approach than manually parsing `sys.argv` as well as default help and error handling 239 | * type conversions required can be specified while building parser itself 240 | 241 | ``` 242 | $ ./sum2nums_argparse.py 243 | usage: sum2nums_argparse.py [-h] num1 num2 244 | sum2nums_argparse.py: error: the following arguments are required: num1, num2 245 | 246 | $ ./sum2nums_argparse.py -h 247 | usage: sum2nums_argparse.py [-h] num1 num2 248 | 249 | positional arguments: 250 | num1 first number 251 | num2 second number 252 | 253 | optional arguments: 254 | -h, --help show this help message and exit 255 | 256 | $ ./sum2nums_argparse.py 3 4 257 | 3 + 4 = 7 258 | 259 | $ ./sum2nums_argparse.py 3 4 7 260 | usage: sum2nums_argparse.py [-h] num1 num2 261 | sum2nums_argparse.py: error: unrecognized arguments: 7 262 | ``` 263 | 264 | 265 | **Further Reading** 266 | 267 | * [Python docs - argparse tutorial](https://docs.python.org/3/howto/argparse.html#id1) 268 | * [Python docs - argparse module](https://docs.python.org/3/library/argparse.html#module-argparse) 269 | * [argparse examples with explanation](https://stackoverflow.com/questions/7427101/dead-simple-argparse-example-wanted-1-argument-3-results) 270 | * [Python docs - getopt module](https://docs.python.org/3/library/getopt.html#module-getopt) 271 | -------------------------------------------------------------------------------- /Control_structures.md: -------------------------------------------------------------------------------- 1 | # Control Structures 2 | 3 | * [Condition checking](#condition-checking) 4 | * [if](#if) 5 | * [for](#for) 6 | * [while](#while) 7 | * [continue and break](#continue-and-break) 8 | 9 |
10 | 11 | ### Condition checking 12 | 13 | * simple and combination of tests 14 | 15 | ```python 16 | >>> num = 5 17 | >>> num > 2 18 | True 19 | >>> num > 3 and num <= 5 20 | True 21 | >>> 3 < num <= 5 22 | True 23 | >>> num % 3 == 0 or num % 5 == 0 24 | True 25 | 26 | >>> fav_fiction = 'Harry Potter' 27 | >>> fav_detective = 'Sherlock Holmes' 28 | >>> fav_fiction == fav_detective 29 | False 30 | >>> fav_fiction == "Harry Potter" 31 | True 32 | ``` 33 | 34 | * Testing variable or value by themselves 35 | 36 | ```python 37 | >>> bool(num) 38 | True 39 | >>> bool(fav_detective) 40 | True 41 | >>> bool(3) 42 | True 43 | >>> bool(0) 44 | False 45 | >>> bool("") 46 | False 47 | >>> bool(None) 48 | False 49 | 50 | >>> if -1: 51 | ... print("-1 evaluates to True in condition checking") 52 | ... 53 | -1 evaluates to True in condition checking 54 | ``` 55 | 56 | * The use of `in` operator in condition checking 57 | 58 | Compare this way of checking 59 | 60 | ```python 61 | >>> def num_chk(n): 62 | ... if n == 10 or n == 21 or n == 33: 63 | ... print("Number passes condition") 64 | ... else: 65 | ... print("Number fails condition") 66 | ... 67 | >>> num_chk(10) 68 | Number passes condition 69 | >>> num_chk(12) 70 | Number fails condition 71 | ``` 72 | 73 | vs this one 74 | 75 | ```python 76 | >>> def num_chk(n): 77 | ... if n in (10, 21, 33): 78 | ... print("Number passes condition") 79 | ... else: 80 | ... print("Number fails condition") 81 | ... 82 | >>> num_chk(12) 83 | Number fails condition 84 | >>> num_chk(10) 85 | Number passes condition 86 | ``` 87 | 88 | * `(10, 21, 33)` is a tuple data type, will be covered in later chapters 89 | * [Python docs - Truth Value Testing](https://docs.python.org/3/library/stdtypes.html#truth) 90 | 91 |
92 | 93 | ### if 94 | 95 | ```python 96 | #!/usr/bin/python3 97 | 98 | num = 45 99 | 100 | # only if 101 | if num > 25: 102 | print("Hurray! {} is greater than 25".format(num)) 103 | 104 | # if-else 105 | if num % 2 == 0: 106 | print("{} is an even number".format(num)) 107 | else: 108 | print("{} is an odd number".format(num)) 109 | 110 | # if-elif-else 111 | # any number of elif can be used 112 | if num < 0: 113 | print("{} is a negative number".format(num)) 114 | elif num > 0: 115 | print("{} is a positive number".format(num)) 116 | else: 117 | print("{} is neither postive nor a negative number".format(num)) 118 | ``` 119 | 120 | * Block of code for functions, control structures, etc are distinguished by indented code 121 | * 4-space indentation is recommended 122 | * [Python docs - Coding Style](https://docs.python.org/3/tutorial/controlflow.html#intermezzo-coding-style) 123 | * A common syntax error is leaving out `:` at end of control structure statements 124 | * Using `()` around conditions is optional 125 | * indented block can have any number of statements, including blank lines 126 | 127 | ``` 128 | $ ./if_elif_else.py 129 | Hurray! 45 is greater than 25 130 | 45 is an odd number 131 | 45 is a positive number 132 | ``` 133 | 134 | **if-else** as conditional operator 135 | 136 | ```python 137 | #!/usr/bin/python3 138 | 139 | num = 42 140 | 141 | num_type = 'even' if num % 2 == 0 else 'odd' 142 | print("{} is an {} number".format(num, num_type)) 143 | ``` 144 | 145 | * Python doesn't have `?:` conditional operator like many other languages 146 | * Using `if-else` in single line like in this example is one workaround 147 | * [More ways of simulating ternary conditional operator](http://stackoverflow.com/questions/394809/does-python-have-a-ternary-conditional-operator) 148 | 149 | ``` 150 | $ ./if_else_oneliner.py 151 | 42 is an even number 152 | ``` 153 | 154 |
155 | 156 | ### for 157 | 158 | ```python 159 | #!/usr/bin/python3 160 | 161 | number = 9 162 | for i in range(1, 5): 163 | mul_table = number * i 164 | print("{} * {} = {}".format(number, i, mul_table)) 165 | ``` 166 | 167 | * traditional iteration based loop can be written using `range` function 168 | * recall that by default `start=0`, `step=1` and `stop` value is not inclusive 169 | * iterating over variables like list, tuples, etc will be covered in later chapters 170 | * [Python docs - itertools](https://docs.python.org/3/library/itertools.html) 171 | 172 | ``` 173 | $ ./for_loop.py 174 | 9 * 1 = 9 175 | 9 * 2 = 18 176 | 9 * 3 = 27 177 | 9 * 4 = 36 178 | ``` 179 | 180 |
181 | 182 | ### while 183 | 184 | ```python 185 | #!/usr/bin/python3 186 | 187 | # continuously ask user input till it is a positive integer 188 | usr_string = 'not a number' 189 | while not usr_string.isnumeric(): 190 | usr_string = input("Enter a positive integer: ") 191 | ``` 192 | 193 | * while loop allows us to execute block of statements until a condition is satisfied 194 | * [Python docs - string methods](https://docs.python.org/3/library/stdtypes.html#string-methods) 195 | 196 | ``` 197 | $ ./while_loop.py 198 | Enter a positive integer: abc 199 | Enter a positive integer: 1.2 200 | Enter a positive integer: 23 201 | $ 202 | ``` 203 | 204 |
205 | 206 | ### continue and break 207 | 208 | The `continue` and `break` keywords are used to change the normal flow of loops on certain conditions 209 | 210 | **continue** - skip rest of statements in the loop and start next iteration 211 | 212 | ```python 213 | #!/usr/bin/python3 214 | 215 | prev_num = 0 216 | curr_num = 0 217 | print("The first ten numbers in fibonacci sequence: ", end='') 218 | 219 | for num in range(10): 220 | print(curr_num, end=' ') 221 | 222 | if num == 0: 223 | curr_num = 1 224 | continue 225 | 226 | temp = curr_num 227 | curr_num = curr_num + prev_num 228 | prev_num = temp 229 | 230 | print("") 231 | ``` 232 | 233 | * `continue` can be placed anywhere in a loop block without having to worry about complicated code flow 234 | * this example is just to show use of `continue`, check [this](https://docs.python.org/3/tutorial/controlflow.html#defining-functions) for a more Pythonic way 235 | 236 | ``` 237 | $ ./loop_with_continue.py 238 | The first ten numbers in fibonacci sequence: 0 1 1 2 3 5 8 13 21 34 239 | ``` 240 | 241 | **break** - skip rest of statements in the loop (if any) and exit loop 242 | 243 | ```python 244 | #!/usr/bin/python3 245 | 246 | import random 247 | 248 | while True: 249 | # as with range() function, 500 is not inclusive 250 | random_int = random.randrange(500) 251 | if random_int % 4 == 0 and random_int % 6 == 0: 252 | break 253 | print("Random number divisible by 4 and 6: {}".format(random_int)) 254 | ``` 255 | 256 | * `while True:` is generally used as infinite loop 257 | * **randrange** has similar `start, stop, step` arguments as [range](./Functions.md#range-function) 258 | * [Python docs - random](https://docs.python.org/3/library/random.html) 259 | 260 | ``` 261 | $ ./loop_with_break.py 262 | Random number divisible by 4 and 6: 168 263 | $ ./loop_with_break.py 264 | Random number divisible by 4 and 6: 216 265 | $ ./loop_with_break.py 266 | Random number divisible by 4 and 6: 24 267 | ``` 268 | 269 | The while_loop.py example can be re-written using `break` 270 | 271 | ```python 272 | >>> while True: 273 | usr_string = input("Enter a positive integer: ") 274 | if usr_string.isnumeric(): 275 | break 276 | 277 | Enter a positive integer: a 278 | Enter a positive integer: 3.14 279 | Enter a positive integer: 1 280 | >>> 281 | ``` 282 | 283 | * in case of nested loops, `continue` and `break` only affect the immediate parent loop 284 | * [Python docs - else clauses on loops](https://docs.python.org/3/tutorial/controlflow.html#break-and-continue-statements-and-else-clauses-on-loops) 285 | -------------------------------------------------------------------------------- /Docstrings.md: -------------------------------------------------------------------------------- 1 | # Docstrings 2 | 3 | * [Style guide](#style-guide) 4 | * [Palindrome example](#palindrome-example) 5 | 6 |
7 | 8 | ### Style guide 9 | 10 | Paraphrased from [Python docs - coding style](https://docs.python.org/3/tutorial/controlflow.html#intermezzo-coding-style) 11 | 12 | * Use 4-space indentation, and no tabs. 13 | * 4 spaces are a good compromise between small indentation (allows greater nesting depth) and large indentation (easier to read). Tabs introduce confusion, and are best left out. 14 | * Wrap lines so that they don’t exceed 79 characters. 15 | * This helps users with small displays and makes it possible to have several code files side-by-side on larger displays. 16 | * Use blank lines to separate functions and classes, and larger blocks of code inside functions. 17 | * When possible, put comments on a line of their own. 18 | * Use docstrings. 19 | * Use spaces around operators and after commas 20 | * Name your classes and functions consistently; 21 | * the convention is to use CamelCase for classes and lower_case_with_underscores for functions and methods 22 | 23 | **Style guides** 24 | 25 | * [PEP 0008](https://www.python.org/dev/peps/pep-0008/) 26 | * [Google - pyguide](https://google.github.io/styleguide/pyguide.html) 27 | * [elements of python style](https://github.com/amontalenti/elements-of-python-style) 28 | * [The Hitchhiker’s Guide to Python](http://docs.python-guide.org/en/latest/) - handbook of best practices 29 | 30 |
31 | 32 | ### Palindrome example 33 | 34 | ```python 35 | #!/usr/bin/python3 36 | 37 | """ 38 | Asks for user input and tells if string is palindrome or not 39 | 40 | Allowed characters: alphabets and punctuations .,;:'"-!? 41 | Minimum alphabets: 3 and cannot be all same 42 | 43 | Informs if input is invalid and asks user for input again 44 | """ 45 | 46 | import re 47 | 48 | def is_palindrome(usr_ip): 49 | """ 50 | Checks if string is a palindrome 51 | 52 | ValueError: if string is invalid 53 | 54 | Returns True if palindrome, False otherwise 55 | """ 56 | 57 | # remove punctuations & whitespace and change to all lowercase 58 | ip_str = re.sub(r'[\s.;:,\'"!?-]', r'', usr_ip).lower() 59 | 60 | if re.search(r'[^a-zA-Z]', ip_str): 61 | raise ValueError("Characters other than alphabets and punctuations") 62 | elif len(ip_str) < 3: 63 | raise ValueError("Less than 3 alphabets") 64 | else: 65 | return ip_str == ip_str[::-1] and not re.search(r'^(.)\1+$', ip_str) 66 | 67 | def main(): 68 | while True: 69 | try: 70 | usr_ip = input("Enter a palindrome: ") 71 | if is_palindrome(usr_ip): 72 | print("{} is a palindrome".format(usr_ip)) 73 | else: 74 | print("{} is NOT a palindrome".format(usr_ip)) 75 | break 76 | except ValueError as e: 77 | print('Error: ' + str(e)) 78 | 79 | if __name__ == "__main__": 80 | main() 81 | ``` 82 | 83 | * The first triple quoted strings marks the docstring for entire program 84 | * The second one inside `is_palindrome()` is specific for that function 85 | 86 | ``` 87 | $ ./palindrome.py 88 | Enter a palindrome: as2 89 | Error: Characters other than alphabets and punctuations 90 | Enter a palindrome: "Dammit, I'm mad!" 91 | "Dammit, I'm mad!" is a palindrome 92 | 93 | $ ./palindrome.py 94 | Enter a palindrome: a'a 95 | Error: Less than 3 alphabets 96 | Enter a palindrome: aaa 97 | aaa is NOT a palindrome 98 | ``` 99 | 100 | * Let's see how docstrings can be used as help 101 | * Notice how docstring gets automatically formatted 102 | 103 | ```python 104 | >>> import palindrome 105 | >>> help(palindrome) 106 | 107 | Help on module palindrome: 108 | 109 | NAME 110 | palindrome - Asks for user input and tells if string is palindrome or not 111 | 112 | DESCRIPTION 113 | Allowed characters: alphabets and punctuations .,;:'"-!? 114 | Minimum alphabets: 3 and cannot be all same 115 | 116 | Informs if input is invalid and asks user for input again 117 | 118 | FUNCTIONS 119 | is_palindrome(usr_ip) 120 | Checks if string is a palindrome 121 | 122 | ValueError: if string is invalid 123 | 124 | Returns True if palindrome, False otherwise 125 | 126 | main() 127 | 128 | FILE 129 | /home/learnbyexample/python_programs/palindrome.py 130 | ``` 131 | 132 | * One can get help on functions directly too 133 | 134 | ```python 135 | >>> help(palindrome.is_palindrome) 136 | 137 | Help on function is_palindrome in module palindrome: 138 | 139 | is_palindrome(usr_ip) 140 | Checks if string is a palindrome 141 | 142 | ValueError: if string is invalid 143 | 144 | Returns True if palindrome, False otherwise 145 | ``` 146 | 147 | * and of course test the functions 148 | 149 | ```python 150 | >>> palindrome.is_palindrome('aaa') 151 | False 152 | >>> palindrome.is_palindrome('Madam') 153 | True 154 | 155 | >>> palindrome.main() 156 | Enter a palindrome: 3452 157 | Error: Characters other than alphabets and punctuations 158 | Enter a palindrome: Malayalam 159 | Malayalam is a palindrome 160 | ``` 161 | 162 | **Further Reading** 163 | 164 | * [docstring formats](http://stackoverflow.com/questions/3898572/what-is-the-standard-python-docstring-format) 165 | * [exception message capturing](http://stackoverflow.com/questions/4690600/python-exception-message-capturing) 166 | -------------------------------------------------------------------------------- /Exception_Handling_and_Debugging.md: -------------------------------------------------------------------------------- 1 | # Exception Handling and Debugging 2 | 3 | * [Exception Handling](#exception-handling) 4 | * [Syntax check](#syntax-check) 5 | * [pdb](#pdb) 6 | * [Importing program](#importing-program) 7 | 8 |
9 | 10 | ### Exception Handling 11 | 12 | * We have seen plenty of errors in previous chapters when something goes wrong or some input was given erroneously 13 | * For example: 14 | 15 | ``` 16 | $ ./user_input_int.py 17 | Enter an integer number: abc 18 | Traceback (most recent call last): 19 | File "./user_input_int.py", line 6, in 20 | usr_num = int(usr_ip) 21 | ValueError: invalid literal for int() with base 10: 'abc' 22 | ``` 23 | 24 | * In such cases, it might be preferred to inform the user on the error and give a chance to correct it 25 | * Python provides the `try-except` construct to achieve this 26 | 27 | ```python 28 | #!/usr/bin/python3 29 | 30 | while True: 31 | try: 32 | usr_num = int(input("Enter an integer number: ")) 33 | break 34 | except ValueError: 35 | print("Not an integer, try again") 36 | 37 | print("Square of entered number is: {}".format(usr_num * usr_num)) 38 | ``` 39 | 40 | * `except` can be used for particular error (in this case `ValueError`) 41 | 42 | ``` 43 | $ ./user_input_exception.py 44 | Enter an integer number: a 45 | Not an integer, try again 46 | Enter an integer number: 1.2 47 | Not an integer, try again 48 | Enter an integer number: 3 49 | Square of entered number is: 9 50 | ``` 51 | 52 | **Further Reading** 53 | 54 | * [Python docs - errors, exception handling and raising exceptions](https://docs.python.org/3/tutorial/errors.html) 55 | * [Python docs - built-in exceptions](https://docs.python.org/3/library/exceptions.html#bltin-exceptions) 56 | * [stackoverflow - exception message capturing](https://stackoverflow.com/questions/4690600/python-exception-message-capturing) 57 | * [stackoverflow - avoid bare exceptions](https://stackoverflow.com/questions/14797375/should-i-always-specify-an-exception-type-in-except-statements) 58 | * [Python docs - pass statement](https://docs.python.org/3/reference/simple_stmts.html#grammar-token-pass_stmt) 59 | 60 |
61 | 62 | ### Syntax check 63 | 64 | * Python's command line options can be used for variety of purposes 65 | * Syntax checking is one of them 66 | 67 | ``` 68 | $ python3 -m py_compile syntax_error.py 69 | File "syntax_error.py", line 3 70 | print "Have a nice day" 71 | ^ 72 | SyntaxError: Missing parentheses in call to 'print' 73 | ``` 74 | 75 | * Useful to quickly catch syntax errors like missing `:` for `if for with` etc and `()` for `print` statements 76 | * While this example might be trivial, real world program might have thousands of lines and tougher to find typos 77 | * [Python docs - cmdline](https://docs.python.org/3/using/cmdline.html) 78 | * One-liners: [#1](http://www.vurt.ru/2013/02/python-command-line-oneliners/), [#2](https://wiki.python.org/moin/Powerful%20Python%20One-Liners), [#3](http://python-oneliner.readthedocs.org/en/latest/) 79 | 80 |
81 | 82 | ### pdb 83 | 84 | * Invoking debugger is another use of `cmdline` 85 | * Use it instead of using `print` all over a program when something goes wrong, plus one can use breakpoints and other features specific to debugging programs 86 | 87 | ```python 88 | $ python3 -m pdb if_elif_else.py 89 | > /home/learnbyexample/python_programs/if_elif_else.py(3)() 90 | -> num = 45 91 | (Pdb) p num 92 | *** NameError: name 'num' is not defined 93 | (Pdb) n 94 | > /home/learnbyexample/python_programs/if_elif_else.py(6)() 95 | -> if num > 25: 96 | (Pdb) p num 97 | 45 98 | (Pdb) l 99 | 1 #!/usr/bin/python3 100 | 2 101 | 3 num = 45 102 | 4 103 | 5 # only if 104 | 6 -> if num > 25: 105 | 7 print("Hurray! {} is greater than 25".format(num)) 106 | 8 107 | 9 # if-else 108 | 10 if num % 2 == 0: 109 | 11 print("{} is an even number".format(num)) 110 | (Pdb) q 111 | ``` 112 | 113 | * `l` prints code around the current statement the debugger is at, useful to visualize the progress of debug effort 114 | * `s` execute current line, steps inside function calls 115 | * `n` execute current line and treats function as single execution step 116 | * `c` continue execution until next breakpoint 117 | * `p variable` print value of variable 118 | * `h` list of commands 119 | * `h c` help on `c` command 120 | * `q` quit the debugger 121 | 122 | **Further Reading** 123 | 124 | * [Python docs - pdb](https://docs.python.org/3/library/pdb.html) 125 | * [pdb tutorial](https://github.com/spiside/pdb-tutorial) 126 | * [common runtime errors](http://inventwithpython.com/blog/2012/07/09/16-common-python-runtime-errors/) 127 | * [common beginner errors as a flowchart](http://pythonforbiologists.com/index.php/29-common-beginner-python-errors-on-one-page/) 128 | * [Common pitfalls](https://stackoverflow.com/questions/1011431/common-pitfalls-in-python) 129 | * [Python docs - Basic Logging Tutorial](https://docs.python.org/3/howto/logging.html) 130 | 131 |
132 | 133 | ### Importing program 134 | 135 | * One can also `import` a program directly in Interpreter to test functions 136 | * `if __name__ == "__main__":` construct 137 | * code inside that construct will be executed when program is intended to be run - ex: executing the program from command line 138 | * code inside that construct will NOT be executed when program is intended to be imported as module - ex: to use programs's functions 139 | * A good practice is to put all code outside of functions inside a `main` function and call `main()` inside the `if __name__ == "__main__":` construct 140 | * Note that `__name__` and `__main__` have two underscore characters as prefix and suffix 141 | 142 | ```python 143 | #!/usr/bin/python3 144 | 145 | # ----- function without arguments ----- 146 | def greeting(): 147 | print("-----------------------------") 148 | print(" Hello World ") 149 | print("-----------------------------") 150 | 151 | # ----- function with arguments ----- 152 | def sum_two_numbers(num1, num2): 153 | sum = num1 + num2 154 | print("{} + {} = {}".format(num1, num2, sum)) 155 | 156 | # ----- function with return value ----- 157 | def num_square(num): 158 | return num * num 159 | 160 | # ----- main ----- 161 | def main(): 162 | greeting() 163 | sum_two_numbers(3, 4) 164 | 165 | my_num = 3 166 | print(num_square(2)) 167 | print(num_square(my_num)) 168 | 169 | if __name__ == "__main__": 170 | main() 171 | ``` 172 | 173 | * When run as a program 174 | 175 | ``` 176 | $ ./functions_main.py 177 | ----------------------------- 178 | Hello World 179 | ----------------------------- 180 | 3 + 4 = 7 181 | 4 182 | 9 183 | ``` 184 | 185 | * When importing 186 | 187 | ```python 188 | >>> import functions_main 189 | 190 | >>> functions_main.greeting() 191 | ----------------------------- 192 | Hello World 193 | ----------------------------- 194 | 195 | >>> functions_main.num_square() 196 | Traceback (most recent call last): 197 | File "", line 1, in 198 | TypeError: num_square() missing 1 required positional argument: 'num' 199 | 200 | >>> functions_main.num_square(5) 201 | 25 202 | 203 | >>> functions_main.main() 204 | ----------------------------- 205 | Hello World 206 | ----------------------------- 207 | 3 + 4 = 7 208 | 4 209 | 9 210 | ``` 211 | 212 | **Further Reading** 213 | 214 | * [Python docs - \_\_main\_\_](https://docs.python.org/3/library/__main__.html) 215 | * [What does if \_\_name\_\_ == "\_\_main\_\_" do?](https://stackoverflow.com/questions/419163/what-does-if-name-main-do) 216 | * [Python packaging guide](https://packaging.python.org/en/latest/distributing/) 217 | * [diveintopython3 - packaging](http://www.diveintopython3.net/packaging.html) 218 | -------------------------------------------------------------------------------- /Executing_external_commands.md: -------------------------------------------------------------------------------- 1 | # Executing external commands 2 | 3 | * [Calling Shell commands](#calling-shell-commands) 4 | * [Calling Shell commands with expansion](#calling-shell-commands-with-expansion) 5 | * [Getting command output and redirections](#getting-command-output-and-redirections) 6 | 7 | The sample output shown in this chapter may be different based on your username, working directories, etc 8 | 9 |
10 | 11 | ### Calling Shell commands 12 | 13 | ```python 14 | #!/usr/bin/python3 15 | 16 | import subprocess 17 | 18 | # Executing external command 'date' 19 | subprocess.call('date') 20 | 21 | # Passing options and arguments to command 22 | print("\nToday is ", end="", flush=True) 23 | subprocess.call(['date', '-u', '+%A']) 24 | 25 | # another example 26 | print("\nSearching for 'hello world'", flush=True) 27 | subprocess.call(['grep', '-i', 'hello world', 'hello_world.py']) 28 | ``` 29 | 30 | * `import` statement here is used to load the `subprocess` module, which is part of the [Python standard library](https://docs.python.org/3/library/index.html) 31 | * the `call` function from `subprocess` module is one of the ways to execute external commands 32 | * By passing `True` to `flush` argument (default is `False`) we ensure that our message is printed before `subprocess.call` 33 | * For passing arguments, list of strings is passed instead of single string 34 | 35 | ``` 36 | $ ./calling_shell_commands.py 37 | Tue Jun 21 18:35:33 IST 2016 38 | 39 | Today is Tuesday 40 | 41 | Searching for 'hello world' 42 | print("Hello World") 43 | ``` 44 | 45 | **Further Reading** 46 | 47 | * [Python docs - subprocess](https://docs.python.org/3/library/subprocess.html) 48 | * [Python docs - os.system](https://docs.python.org/3/library/os.html#os.system) 49 | * [difference between os.system and subprocess.call](https://www.quora.com/Whats-the-difference-between-os-system-and-subprocess-call-in-Python) 50 | * [Python docs - import statement](https://docs.python.org/3/reference/simple_stmts.html#import) 51 | 52 |
53 | 54 | ### Calling Shell commands with expansion 55 | 56 | ```python 57 | #!/usr/bin/python3 58 | 59 | import subprocess 60 | 61 | # Executing command without shell expansion 62 | print("No shell expansion when shell=False", flush=True) 63 | subprocess.call(['echo', 'Hello $USER']) 64 | 65 | # Executing command with shell expansion 66 | print("\nshell expansion when shell=True", flush=True) 67 | subprocess.call('echo Hello $USER', shell=True) 68 | 69 | # escape quotes if it is part of command 70 | print("\nSearching for 'hello world'", flush=True) 71 | subprocess.call('grep -i \'hello world\' hello_world.py', shell=True) 72 | ``` 73 | 74 | * By default, `subprocess.call` will not expand [shell wildcards](https://github.com/learnbyexample/Linux_command_line/blob/master/Shell.md#wildcards), perform [command substitution](http://mywiki.wooledge.org/CommandSubstitution), etc 75 | * This can be overridden by passing `True` value for `shell` argument 76 | * Note that the entire command is now passed as string and not as a list of strings 77 | * Quotes need to be escaped if they clash between command string and quotes within the command itself 78 | * Use `shell=True` only if you are sure of the command being executed, else it could be a [security issue](https://stackoverflow.com/questions/3172470/actual-meaning-of-shell-true-in-subprocess) 79 | * [Python docs - subprocess.Popen](https://docs.python.org/3/library/subprocess.html#popen-constructor) 80 | 81 | ``` 82 | $ ./shell_expansion.py 83 | No shell expansion when shell=False 84 | Hello $USER 85 | 86 | shell expansion when shell=True 87 | Hello learnbyexample 88 | 89 | Searching for 'hello world' 90 | print("Hello World") 91 | ``` 92 | 93 | * In certain cases, escaping quotes can be avoided by using combination of single/double quotes as shown below 94 | 95 | ```python 96 | # use alternate quotes, like this 97 | subprocess.call('grep -i "hello world" hello_world.py', shell=True) 98 | 99 | # or this 100 | subprocess.call("grep -i 'hello world' hello_world.py", shell=True) 101 | ``` 102 | 103 | * [Shell command redirections](https://github.com/learnbyexample/Linux_command_line/blob/master/Shell.md#redirection) can be used as usual 104 | 105 | ```python 106 | # use variables for clarity and to avoid long strings within call function 107 | cmd = "grep -h 'test' report.log test_list.txt > grep_test.txt" 108 | subprocess.call(cmd, shell=True) 109 | ``` 110 | 111 | **Workaround to avoid using shell=True** 112 | 113 | ```python 114 | >>> import subprocess, os 115 | >>> subprocess.call(['echo', 'Hello', os.environ.get("USER")]) 116 | Hello learnbyexample 117 | 0 118 | ``` 119 | 120 | * `os.environ.get("USER")` gives back the value of environment variable `USER` 121 | * `0` is the exit status, meaning success. It is a caveat of python interpreter which displays return value too 122 | 123 |
124 | 125 | ### Getting command output and redirections 126 | 127 | ```python 128 | #!/usr/bin/python3 129 | 130 | import subprocess 131 | 132 | # Output includes any error messages also 133 | print("Getting output of 'pwd' command", flush=True) 134 | curr_working_dir = subprocess.getoutput('pwd') 135 | print(curr_working_dir) 136 | 137 | # Get status and output of command executed 138 | # Exit status other than '0' is considered as something gone wrong 139 | ls_command = 'ls hello_world.py xyz.py' 140 | print("\nCalling command '{}'".format(ls_command), flush=True) 141 | (ls_status, ls_output) = subprocess.getstatusoutput(ls_command) 142 | print("status: {}\noutput: '{}'".format(ls_status, ls_output)) 143 | 144 | # Suppress error messages if preferred 145 | # subprocess.call() returns status of command which can be used instead 146 | print("\nCalling command with error msg suppressed", flush=True) 147 | ls_status = subprocess.call(ls_command, shell=True, stderr=subprocess.DEVNULL) 148 | print("status: {}".format(ls_status)) 149 | ``` 150 | 151 | * Output of `getstatusoutput()` is of `tuple` data type, more info and examples in later chapters 152 | * `getstatusoutput()` and `getoutput()` are legacy functions 153 | * Use newer functions for more features and secure options 154 | * [Python docs - subprocess.check_output](https://docs.python.org/3/library/subprocess.html#subprocess.check_output) 155 | * [Python docs - subprocess.run](https://docs.python.org/3/library/subprocess.html#subprocess.run) 156 | 157 | ``` 158 | $ ./shell_command_output_redirections.py 159 | Getting output of 'pwd' command 160 | /home/learnbyexample/Python/python_programs 161 | 162 | Calling command 'ls hello_world.py xyz.py' 163 | status: 2 164 | output: 'ls: cannot access xyz.py: No such file or directory 165 | hello_world.py' 166 | 167 | Calling command with error msg suppressed 168 | hello_world.py 169 | status: 2 170 | ``` 171 | -------------------------------------------------------------------------------- /Exercises.md: -------------------------------------------------------------------------------- 1 | # Exercises 2 | 3 | 1) [Variables and Print](#variables-and-print) 4 | 2) [Functions](#functions) 5 | 3) [Control structures](#control-structures) 6 | 4) [List](#list) 7 | 5) [File](#file) 8 | 6) [Text processing](#text-processing) 9 | 7) [Misc](#misc) 10 | 11 |
12 | 13 | For some questions, Python program with assert statements is provided to automatically test your solution in the [exercise_files](https://github.com/learnbyexample/Python_Basics/tree/master/exercise_files) directory - for ex: [Q2a - length of integer numbers](https://github.com/learnbyexample/Python_Basics/blob/master/exercise_files/q2a_int_length.py). The directory also has sample input text files. 14 | 15 | You can also solve these exercises on [repl.it](https://repl.it/community/classrooms/52626), with an option to submit them for review. 16 | 17 |
18 | 19 | ## 1) Variables and Print 20 | 21 | **Q1a)** Ask user information, for ex: `name`, `department`, `college` etc and display them using print function 22 | 23 | ``` 24 | # Sample of how program might ask user input and display output afterwards 25 | $ ./usr_ip.py 26 | Please provide the following details 27 | Enter your name: learnbyexample 28 | Enter your department: ECE 29 | Enter your college: PSG Tech 30 | 31 | ------------------------------------ 32 | Name : learnbyexample 33 | Department : ECE 34 | College : PSG Tech 35 | ``` 36 | 37 |
38 | 39 | ## 2) Functions 40 | 41 | **Q2a)** Returns length of integer numbers 42 | 43 | ```python 44 | >>> len_int(962306349871524124750813401378124) 45 | 33 46 | >>> len_int(+42) 47 | 2 48 | >>> len_int(-42) 49 | 3 50 | 51 | # bonus: handle -ve numbers and check for input type 52 | >>> len_int(-42) 53 | 2 54 | # len_int('a') should give 55 | TypeError: provide only integer input 56 | ``` 57 | 58 | **Q2b)** Returns True/False - two strings are same irrespective of lowercase/uppercase 59 | 60 | ```python 61 | >>> str_cmp('nice', 'nice') 62 | True 63 | >>> str_cmp('Hi there', 'hi there') 64 | True 65 | >>> str_cmp('GoOd DaY', 'gOOd dAy') 66 | True 67 | >>> str_cmp('foo', 'food') 68 | False 69 | ``` 70 | 71 | **Q2c)** Returns True/False - two strings are anagrams (assume input consists of alphabets only) 72 | 73 | ```python 74 | >>> str_anagram('god', 'Dog') 75 | True 76 | >>> str_anagram('beat', 'table') 77 | False 78 | >>> str_anagram('Tap', 'paT') 79 | True 80 | >>> str_anagram('beat', 'abet') 81 | True 82 | ``` 83 | 84 | **Q2d)** Returns corresponding integer or floating-point number (See [Number and String data types](./Number_and_String_datatypes.md) chapter for details) 85 | 86 | ```python 87 | # number input 88 | >>> num(3) 89 | 3 90 | >>> num(0x1f) 91 | 31 92 | >>> num(3.32) 93 | 3.32 94 | 95 | # string input 96 | >>> num('123') 97 | 123 98 | >>> num('-78') 99 | -78 100 | >>> num(" 42 \n ") 101 | 42 102 | >>> num('3.14') 103 | 3.14 104 | >>> num('3.982e5') 105 | 398200.0 106 | 107 | >>> s = '56' 108 | >>> num(s) + 44 109 | 100 110 | ``` 111 | 112 | Other than integer or floating, only string data type should be accepted. Also, provide custom error message if input cannot be converted 113 | 114 | ```python 115 | # num(['1', '2.3']) 116 | TypeError: not a valid input 117 | 118 | # num('foo') 119 | ValueError: could not convert string to int or float 120 | ``` 121 | 122 |
123 | 124 | ## 3) Control structures 125 | 126 | **Q3a)** Write a function that returns 127 | 128 | * 'Good' for numbers divisible by 7 129 | * 'Food' for numbers divisible by 6 130 | * 'Universe' for numbers divisible by 42 131 | * 'Oops' for all other numbers 132 | * Only one output, divisible by 42 takes precedence 133 | 134 | ```python 135 | >>> six_by_seven(66) 136 | 'Food' 137 | >>> six_by_seven(13) 138 | 'Oops' 139 | >>> six_by_seven(42) 140 | 'Universe' 141 | >>> six_by_seven(14) 142 | 'Good' 143 | >>> six_by_seven(84) 144 | 'Universe' 145 | >>> six_by_seven(235432) 146 | 'Oops' 147 | ``` 148 | 149 | *bonus*: use a loop to print number and corresponding string for numbers 1 to 100 150 | 151 | ```python 152 | 1 Oops 153 | 2 Oops 154 | 3 Oops 155 | 4 Oops 156 | 5 Oops 157 | 6 Food 158 | 7 Good 159 | ... 160 | 41 Oops 161 | 42 Universe 162 | ... 163 | 98 Good 164 | 99 Oops 165 | 100 Oops 166 | ``` 167 | 168 | **Q3b)** Print all numbers from 1 to 1000 which reads the same in reversed form in both binary and decimal format 169 | 170 | ``` 171 | $ ./dec_bin.py 172 | 1 1 173 | 3 11 174 | 5 101 175 | 7 111 176 | 9 1001 177 | 33 100001 178 | 99 1100011 179 | 313 100111001 180 | 585 1001001001 181 | 717 1011001101 182 | ``` 183 | 184 | *bonus*: add octal and hexadecimal checks as well 185 | 186 | ``` 187 | $ ./dec_bin_oct.py 188 | 1 0b1 0o1 189 | 3 0b11 0o3 190 | 5 0b101 0o5 191 | 7 0b111 0o7 192 | 9 0b1001 0o11 193 | 585 0b1001001001 0o1111 194 | 195 | $ ./dec_bin_oct_hex.py 196 | 1 0b1 0o1 0x1 197 | 3 0b11 0o3 0x3 198 | 5 0b101 0o5 0x5 199 | 7 0b111 0o7 0x7 200 | 9 0b1001 0o11 0x9 201 | ``` 202 | 203 |
204 | 205 | ## 4) List 206 | 207 | **Q4a)** Write a function that returns product of all numbers of a list 208 | 209 | ```python 210 | >>> product([1, 4, 21]) 211 | 84 212 | >>> product([-4, 2.3e12, 77.23, 982, 0b101]) 213 | -3.48863356e+18 214 | ``` 215 | 216 | *bonus*: works on any kind of iterable 217 | 218 | ```python 219 | >>> product((-3, 11, 2)) 220 | -66 221 | >>> product({8, 300}) 222 | 2400 223 | >>> product([234, 121, 23, 945, 0]) 224 | 0 225 | >>> product(range(2, 6)) 226 | 120 227 | # can you identify what mathematical function the last one performs? 228 | ``` 229 | 230 | **Q4b)** Write a function that returns nth lowest number of a list (or iterable in general). Return the lowest if second argument not specified 231 | 232 | *Note* that if a list contains duplicates, they should be handled before determining nth lowest 233 | 234 | ```python 235 | >>> nums = [42, 23421341, 234.2e3, 21, 232, 12312, -2343] 236 | >>> nth_lowest(nums, 3) 237 | 42 238 | >>> nth_lowest(nums, 5) 239 | 12312 240 | >>> nth_lowest(nums, 15) 241 | Traceback (most recent call last): 242 | File "", line 1, in 243 | File "", line 2, in nth_lowest 244 | IndexError: list index out of range 245 | 246 | >>> nums = [1, -2, 4, 2, 1, 3, 3, 5] 247 | >>> nth_lowest(nums) 248 | -2 249 | >>> nth_lowest(nums, 4) 250 | 3 251 | >>> nth_lowest(nums, 5) 252 | 4 253 | 254 | >>> nth_lowest('unrecognizable', 3) 255 | 'c' 256 | >>> nth_lowest('abracadabra', 4) 257 | 'd' 258 | ``` 259 | 260 | **Q4c)** Write a function that accepts a string input and returns slices 261 | 262 | * if input string is less than 3 characters long, return a list with input string as the only element 263 | * otherwise, return list with all string slices greater than 1 character long 264 | * order of slices should be same as shown in examples below 265 | 266 | ```python 267 | >>> word_slices('i') 268 | ['i'] 269 | >>> word_slices('to') 270 | ['to'] 271 | 272 | >>> word_slices('are') 273 | ['ar', 'are', 're'] 274 | >>> word_slices('table') 275 | ['ta', 'tab', 'tabl', 'table', 'ab', 'abl', 'able', 'bl', 'ble', 'le'] 276 | ``` 277 | 278 |
279 | 280 | ## 5) File 281 | 282 | **Q5a)** Print sum of all numbers from a file containing only single column and all numbers 283 | 284 | ``` 285 | $ cat f1.txt 286 | 8 287 | 53 288 | 3.14 289 | 84 290 | 73e2 291 | 100 292 | 2937 293 | 294 | $ ./col_sum.py 295 | 10485.14 296 | ``` 297 | 298 | **Q5b)** Print sum of all numbers (assume only positive integer numbers) from a file containing arbitrary string 299 | 300 | ``` 301 | $ cat f2.txt 302 | Hello123 World 35 303 | 341 2 304 | Good 13day 305 | How are 1784 you 306 | 307 | $ ./extract_sum.py 308 | 2298 309 | ``` 310 | 311 | **Q5c)** Sort file contents in alphabetic order based on each line's extension 312 | 313 | * extension here is defined as the string after the last `.` in the line 314 | * if line doesn't have a `.`, those lines should come before lines with `.` 315 | * sorting should be case-insensitive 316 | * use rest of string as tie-breaker if there are more than one line with same extension 317 | * assume input file is ASCII encoded and small enough to fit in memory 318 | 319 | *bonus*: instead of printing results to stdout, change the input file itself with sorted result 320 | 321 | ```bash 322 | $ cat f3.txt 323 | power.Log 324 | foo.123.txt 325 | list 326 | report_12.log 327 | baz.TXT 328 | hello.RB 329 | loop.do.rb 330 | Fav_books 331 | 332 | $ ./sort_by_ext.py 333 | Fav_books 334 | list 335 | power.Log 336 | report_12.log 337 | hello.RB 338 | loop.do.rb 339 | baz.TXT 340 | foo.123.txt 341 | ``` 342 | 343 |
344 | 345 | ## 6) Text processing 346 | 347 | **Q6a)** Check if two words are same or differ by only one character (irrespective of case), input strings should have same length 348 | 349 | See also [Levenshtein distance](https://en.wikipedia.org/wiki/Levenshtein_distance) 350 | 351 | ```python 352 | >>> is_one_char_diff('bar', 'bar') 353 | True 354 | >>> is_one_char_diff('bar', 'Baz') 355 | True 356 | >>> is_one_char_diff('Food', 'fold') 357 | True 358 | >>> is_one_char_diff('A', 'b') 359 | True 360 | 361 | >>> is_one_char_diff('a', '') 362 | False 363 | >>> is_one_char_diff('Bar', 'Bark') 364 | False 365 | >>> is_one_char_diff('Bar', 'art') 366 | False 367 | >>> is_one_char_diff('Food', 'fled') 368 | False 369 | >>> is_one_char_diff('ab', '') 370 | False 371 | ``` 372 | 373 | **Q6b)** Check if a word is in ascending/descending alphabetic order or not (irrespective of case) 374 | 375 | Can you think of a way to do it only using built-in functions and string methods? 376 | 377 | ```python 378 | >>> is_alpha_order('bot') 379 | True 380 | >>> is_alpha_order('arT') 381 | True 382 | >>> is_alpha_order('are') 383 | False 384 | >>> is_alpha_order('boat') 385 | False 386 | 387 | >>> is_alpha_order('toe') 388 | True 389 | >>> is_alpha_order('Flee') 390 | False 391 | >>> is_alpha_order('reed') 392 | True 393 | ``` 394 | 395 | *bonus*: Check if all words in a sentence (assume only whitespace separated input) are in ascending/descending alphabetic order (irrespective of case) 396 | 397 | **hint** use a built-in function and generator expression 398 | 399 | ```bash 400 | >>> is_alpha_order_sentence('Toe got bit') 401 | True 402 | >>> is_alpha_order_sentence('All is well') 403 | False 404 | ``` 405 | 406 | **Q6c)** Find the maximum nested depth of curly braces 407 | 408 | Unbalanced or wrongly ordered braces should return `-1` 409 | 410 | Iterating over input string is one way to solve this, another is to use regular expressions 411 | 412 | ```python 413 | >>> max_nested_braces('a*b') 414 | 0 415 | >>> max_nested_braces('{a+2}*{b+c}') 416 | 1 417 | >>> max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') 418 | 4 419 | >>> max_nested_braces('a*b+{}') 420 | 1 421 | >>> max_nested_braces('}a+b{') 422 | -1 423 | >>> max_nested_braces('a*b{') 424 | -1 425 | ``` 426 | 427 | *bonus*: empty braces, i.e `{}` should return `-1` 428 | 429 | ```python 430 | >>> max_nested_braces('a*b+{}') 431 | -1 432 | >>> max_nested_braces('a*{b+{}+c*{e*3.14}}') 433 | -1 434 | ``` 435 | 436 |
437 | 438 | ## 7) Misc 439 | 440 | **Q7a)** Play a song (**hint** use `subprocess` module) 441 | 442 | **Q7b)** Open a browser along with any link, for ex: https://github.com/learnbyexample/Python_Basics (**hint** use `webbrowser` module) 443 | 444 | **Q7c)** Write a function that 445 | 446 | * accepts a filesystem path(default) or a url(indicated by True as second argument) 447 | * returns the longest word(here word is defined as one or more consecutive sequence of alphabets of either case) 448 | * assume that input encoding is **utf-8** and small enough to fit in memory and that there's only one distinct longest word 449 | 450 | ```python 451 | >>> ip_path = 'poem.txt' 452 | >>> longest_word(ip_path) 453 | 'Violets' 454 | 455 | >>> ip_path = 'https://www.gutenberg.org/files/60/60.txt' 456 | >>> longest_word(ip_path, True) 457 | 'misunderstandings' 458 | ``` 459 | 460 |
461 | 462 |
463 | 464 | For reference solutions, see [exercise_solutions](https://github.com/learnbyexample/Python_Basics/tree/master/exercise_solutions) directory 465 | -------------------------------------------------------------------------------- /File_handling.md: -------------------------------------------------------------------------------- 1 | # File handling 2 | 3 | * [open function](#open-function) 4 | * [Reading files](#reading-files) 5 | * [Writing to files](#writing-to-files) 6 | * [Inplace editing with fileinput](#inplace-editing-with-fileinput) 7 | 8 |
9 | 10 | ### open function 11 | 12 | * syntax: open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None) 13 | 14 | ```python 15 | >>> import locale 16 | >>> locale.getpreferredencoding() 17 | 'UTF-8' 18 | ``` 19 | 20 | * We'll be seeing these modes in this chapter 21 | * `r` open file for reading 22 | * `w` open file for writing 23 | * `a` open file for appending 24 | * default is text mode, so passing 'r' and 'rt' are equivalent 25 | * for binary mode, it would be 'rb', 'wb' and so on 26 | * `locale.getpreferredencoding()` gives default encoding being used 27 | * [Python docs - open](https://docs.python.org/3/library/functions.html#open) 28 | * [Python docs - standard encodings](https://docs.python.org/3/library/codecs.html#standard-encodings) 29 | 30 |
31 | 32 | ### Reading files 33 | 34 | ```python 35 | #!/usr/bin/python3 36 | 37 | # open file, read line by line and print it 38 | filename = 'hello_world.py' 39 | f = open(filename, 'r', encoding='ascii') 40 | 41 | print("Contents of " + filename) 42 | print('-' * 30) 43 | for line in f: 44 | print(line, end='') 45 | 46 | f.close() 47 | 48 | # 'with' is a simpler alternative, automatically handles file closing 49 | filename = 'while_loop.py' 50 | 51 | print("\n\nContents of " + filename) 52 | print('-' * 30) 53 | with open(filename, 'r', encoding='ascii') as f: 54 | for line in f: 55 | print(line, end='') 56 | ``` 57 | 58 | * default encoding is usually 'UTF-8', use 'ascii' where applicable 59 | * using `with` and file handle name as `f` is usual convention 60 | 61 | ```bash 62 | $ ./file_reading.py 63 | Contents of hello_world.py 64 | ------------------------------ 65 | #!/usr/bin/python3 66 | 67 | print("Hello World") 68 | 69 | 70 | Contents of while_loop.py 71 | ------------------------------ 72 | #!/usr/bin/python3 73 | 74 | # continuously ask user input till it is a positive integer 75 | usr_string = 'not a number' 76 | while not usr_string.isnumeric(): 77 | usr_string = input("Enter a positive integer: ") 78 | ``` 79 | 80 | **If file doesn't exist** 81 | 82 | ```python 83 | #!/usr/bin/python3 84 | 85 | with open('xyz.py', 'r', encoding='ascii') as f: 86 | for line in f: 87 | print(line, end='') 88 | ``` 89 | 90 | * Error if file is not found 91 | 92 | ```bash 93 | $ ./file_reading_error.py 94 | Traceback (most recent call last): 95 | File "./file_reading_error.py", line 3, in 96 | with open('xyz.py', 'r', encoding='ascii') as f: 97 | FileNotFoundError: [Errno 2] No such file or directory: 'xyz.py' 98 | $ echo $? 99 | 1 100 | ``` 101 | 102 | * read entire file content as single string using `read()` 103 | 104 | ```python 105 | >>> f = open('hello_world.py', 'r', encoding='ascii') 106 | >>> f 107 | <_io.TextIOWrapper name='hello_world.py' mode='r' encoding='ascii'> 108 | >>> print(f.read()) 109 | #!/usr/bin/python3 110 | 111 | print("Hello World") 112 | 113 | ``` 114 | 115 | * read line by line using `readline()` 116 | 117 | ```python 118 | >>> f = open('hello_world.py', 'r', encoding='ascii') 119 | >>> print(f.readline(), end='') 120 | #!/usr/bin/python3 121 | >>> print(f.readline(), end='') 122 | 123 | >>> print(f.readline(), end='') 124 | print("Hello World") 125 | 126 | >>> f.close() 127 | >>> print(f.readline(), end='') 128 | Traceback (most recent call last): 129 | File "", line 1, in 130 | ValueError: I/O operation on closed file. 131 | ``` 132 | 133 | * read all lines of file as list using `readlines()` 134 | * note the plural form 135 | 136 | ```python 137 | >>> f = open('hello_world.py', 'r', encoding='ascii') 138 | >>> all_lines = f.readlines() 139 | >>> all_lines 140 | ['#!/usr/bin/python3\n', '\n', 'print("Hello World")\n'] 141 | ``` 142 | 143 | * check if file is closed or not 144 | 145 | ```python 146 | >>> f = open('hello_world.py', 'r', encoding='ascii') 147 | 148 | >>> f.closed 149 | False 150 | 151 | >>> f.close() 152 | >>> f.closed 153 | True 154 | ``` 155 | 156 |
157 | 158 | ### Writing to files 159 | 160 | ```python 161 | #!/usr/bin/python3 162 | 163 | with open('new_file.txt', 'w', encoding='ascii') as f: 164 | f.write("This is a sample line of text\n") 165 | f.write("Yet another line\n") 166 | ``` 167 | 168 | * Use the `write()` method to print a string to files 169 | * To add text to an existing file, use 'a' mode instead of 'w' 170 | 171 | ```bash 172 | $ ./file_writing.py 173 | $ cat new_file.txt 174 | This is a sample line of text 175 | Yet another line 176 | ``` 177 | 178 |
179 | 180 | ### Inplace editing with fileinput 181 | 182 | ```python 183 | #!/usr/bin/python3 184 | 185 | import fileinput 186 | 187 | with fileinput.input(inplace=True) as f: 188 | for line in f: 189 | line = line.replace('line of text', 'line') 190 | print(line, end='') 191 | ``` 192 | 193 | * The files to be modified are specified as [Command line arguments](./Command_line_arguments.md) when the program is run 194 | * Note that `print` function has to be used instead of `f.write` 195 | * Since the line read every iteration already has newline character, **end** is assigned empty string 196 | * [Python docs - fileinput](https://docs.python.org/3/library/fileinput.html) 197 | 198 | ```bash 199 | $ ./inplace_file_editing.py new_file.txt 200 | $ cat new_file.txt 201 | This is a sample line 202 | Yet another line 203 | 204 | $ # to change all files in current directory ending with .txt, use 205 | $ ./inplace_file_editing.py *.txt 206 | 207 | $ # stdin can also be passed as input, inplace gets disabled 208 | $ echo 'a line of text' | ./inplace_file_editing.py 209 | a line 210 | ``` 211 | 212 | * specifying filenames and backup extensions 213 | 214 | ```python 215 | # To specify filenames within the program itself 216 | with fileinput.input(inplace=True, files=('file1.txt', 'file2.txt')) as f: 217 | 218 | # To create backup of unmodified files, pass an extension to backup parameter 219 | with fileinput.input(inplace=True, backup='.bkp') as f: 220 | ``` 221 | -------------------------------------------------------------------------------- /Functions.md: -------------------------------------------------------------------------------- 1 | # Functions 2 | 3 | * [def](#def) 4 | * [print function](#print-function) 5 | * [range function](#range-function) 6 | * [type function](#type-function) 7 | * [Variable Scope](#variable-scope) 8 | 9 |
10 | 11 | ### def 12 | 13 | ```python 14 | #!/usr/bin/python3 15 | 16 | # ----- function without arguments ----- 17 | def greeting(): 18 | print("-----------------------------") 19 | print(" Hello World ") 20 | print("-----------------------------") 21 | 22 | greeting() 23 | 24 | # ----- function with arguments ----- 25 | def sum_two_numbers(num1, num2): 26 | total = num1 + num2 27 | print("{} + {} = {}".format(num1, num2, total)) 28 | 29 | sum_two_numbers(3, 4) 30 | 31 | # ----- function with return value ----- 32 | def num_square(num): 33 | return num * num 34 | 35 | my_num = 3 36 | print(num_square(2)) 37 | print(num_square(my_num)) 38 | ``` 39 | 40 | * The `def` keyword is used to define functions 41 | * Functions have to be defined before use 42 | * A common syntax error is leaving out `:` at end of `def` statement 43 | * Block of code for functions, control structures, etc are distinguished by indented code 44 | * 4-space indentation is recommended 45 | * [Python docs - Coding Style](https://docs.python.org/3/tutorial/controlflow.html#intermezzo-coding-style) 46 | * The default `return` value is `None` 47 | * [How variables are passed to functions in Python](http://robertheaton.com/2014/02/09/pythons-pass-by-object-reference-as-explained-by-philip-k-dick/) 48 | * `format` is covered in next topic 49 | 50 | ``` 51 | $ ./functions.py 52 | ----------------------------- 53 | Hello World 54 | ----------------------------- 55 | 3 + 4 = 7 56 | 4 57 | 9 58 | ``` 59 | 60 | **Default valued arguments** 61 | 62 | ```python 63 | #!/usr/bin/python3 64 | 65 | # ----- function with default valued argument ----- 66 | def greeting(style_char='-'): 67 | print(style_char * 29) 68 | print(" Hello World ") 69 | print(style_char * 29) 70 | 71 | print("Default style") 72 | greeting() 73 | 74 | print("\nStyle character *") 75 | greeting('*') 76 | 77 | print("\nStyle character =") 78 | greeting(style_char='=') 79 | ``` 80 | 81 | * Often, functions can have a default behavior and if needed changed by passing relevant argument 82 | 83 | ``` 84 | $ ./functions_default_arg_value.py 85 | Default style 86 | ----------------------------- 87 | Hello World 88 | ----------------------------- 89 | 90 | Style character * 91 | ***************************** 92 | Hello World 93 | ***************************** 94 | 95 | Style character = 96 | ============================= 97 | Hello World 98 | ============================= 99 | ``` 100 | 101 | * Triple quoted comment describing function purpose is a usually followed guideline 102 | * To avoid distraction from example code, docstrings for programs and functions won't be generally used in this tutorial 103 | * See [Docstrings](./Docstrings.md) chapter for examples and discussion 104 | 105 | ```python 106 | def num_square(num): 107 | """ 108 | returns square of number 109 | """ 110 | 111 | return num * num 112 | ``` 113 | 114 | **Further Reading** 115 | 116 | There are many more ways to call a function and other types of declarations, refer the below links for more info 117 | 118 | * [Python docs - defining functions](https://docs.python.org/3/tutorial/controlflow.html#defining-functions) 119 | * [Python docs - Built-in Functions](https://docs.python.org/3/library/functions.html) 120 | 121 |
122 | 123 | ### print function 124 | 125 | * By default, `print` function adds newline character 126 | * This can be changed by passing our own string to the `end` argument 127 | 128 | ```python 129 | >>> print("hi") 130 | hi 131 | >>> print("hi", end='') 132 | hi>>> 133 | >>> print("hi", end=' !!\n') 134 | hi !! 135 | >>> 136 | ``` 137 | 138 | * The [help](https://docs.python.org/3/library/functions.html#help) function can be used to get quick help from interpreter itself 139 | * Press `q` to return back from help page 140 | 141 | ```python 142 | >>> help(print) 143 | 144 | Help on built-in function print in module builtins: 145 | 146 | print(...) 147 | print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False) 148 | 149 | Prints the values to a stream, or to sys.stdout by default. 150 | Optional keyword arguments: 151 | file: a file-like object (stream); defaults to the current sys.stdout. 152 | sep: string inserted between values, default a space. 153 | end: string appended after the last value, default a newline. 154 | flush: whether to forcibly flush the stream. 155 | ``` 156 | 157 | * Multiple arguments to `print` function can be passed by `,` separation 158 | * The default `sep` is single space character 159 | 160 | ```python 161 | >>> a = 5 162 | >>> b = 2 163 | 164 | >>> print(a+b, a-b) 165 | 7 3 166 | 167 | >>> print(a+b, a-b, sep=' : ') 168 | 7 : 3 169 | 170 | >>> print(a+b, a-b, sep='\n') 171 | 7 172 | 3 173 | ``` 174 | 175 | * When printing variables, the [__str__](https://docs.python.org/3/reference/datamodel.html#object.__str__) method is called which gives the string representation 176 | * So, explicit conversion is not needed unless concatenation is required 177 | 178 | ```python 179 | >>> greeting = 'Hello World' 180 | >>> print(greeting) 181 | Hello World 182 | >>> num = 42 183 | >>> print(num) 184 | 42 185 | 186 | >>> print(greeting + '. We are learning Python') 187 | Hello World. We are learning Python 188 | >>> print(greeting, '. We are learning Python', sep='') 189 | Hello World. We are learning Python 190 | 191 | >>> print("She bought " + num + " apples") 192 | Traceback (most recent call last): 193 | File "", line 1, in 194 | TypeError: Can't convert 'int' object to str implicitly 195 | 196 | >>> print("She bought " + str(num) + " apples") 197 | She bought 42 apples 198 | ``` 199 | 200 | * As an alternative, use multiple arguments and change `sep` accordingly 201 | 202 | ```python 203 | >>> print("She bought", num, "apples") 204 | She bought 42 apples 205 | 206 | >>> items = 15 207 | >>> print("No. of items:", items) 208 | No. of items: 15 209 | 210 | >>> print("No. of items:", items, sep='') 211 | No. of items:15 212 | ``` 213 | 214 | * To redirect print output to [stderr](https://stackoverflow.com/questions/3385201/confused-about-stdin-stdout-and-stderr) instead of default stdout, change the `file` argument 215 | * See also [sys.exit()](https://docs.python.org/3/library/sys.html#sys.exit) 216 | 217 | ```python 218 | >>> import sys 219 | >>> print("Error!! Not a valid input", file=sys.stderr) 220 | Error!! Not a valid input 221 | ``` 222 | 223 | * `str.format()` can be used to style strings and handle multiple variables more elegantly than string concatenation 224 | 225 | ```python 226 | >>> num1 = 42 227 | >>> num2 = 7 228 | 229 | >>> '{} + {} = {}'.format(num1, num2, num1 + num2) 230 | '42 + 7 = 49' 231 | 232 | # or save formatting in a variable and use wherever needed 233 | >>> op_fmt = '{} + {} = {}' 234 | >>> op_fmt.format(num1, num2, num1 + num2) 235 | '42 + 7 = 49' 236 | >>> op_fmt.format(num1, 29, num1 + 29) 237 | '42 + 29 = 71' 238 | 239 | # and of course the expression can be used inside print directly 240 | >>> print('{} + {} = {}'.format(num1, num2, num1 + num2)) 241 | 42 + 7 = 49 242 | ``` 243 | 244 | * using numbered arguments 245 | 246 | ```python 247 | >>> num1 248 | 42 249 | >>> num2 250 | 7 251 | >>> print("{0} + {1} * {0} = {2}".format(num1, num2, num1 + num2 * num1)) 252 | 42 + 7 * 42 = 336 253 | ``` 254 | 255 | * number formatting - specified using optional argument number, followed by `:` and then the formatting style 256 | 257 | ```python 258 | >>> appx_pi = 22 / 7 259 | >>> appx_pi 260 | 3.142857142857143 261 | 262 | # restricting number of digits after decimal point 263 | # value is rounded off 264 | >>> print("{0:.2f}".format(appx_pi)) 265 | 3.14 266 | >>> print("{0:.3f}".format(appx_pi)) 267 | 3.143 268 | 269 | # aligning 270 | >>> print("{0:<10.3f} and 5.12".format(appx_pi)) 271 | 3.143 and 5.12 272 | >>> print("{0:>10.3f} and 5.12".format(appx_pi)) 273 | 3.143 and 5.12 274 | 275 | # zero filling 276 | >>> print("{0:08.3f}".format(appx_pi)) 277 | 0003.143 278 | ``` 279 | 280 | * different base 281 | 282 | ```python 283 | >>> print("42 in binary = {:b}".format(42)) 284 | 42 in binary = 101010 285 | >>> print("42 in octal = {:o}".format(42)) 286 | 42 in octal = 52 287 | >>> print("241 in hex = {:x}".format(241)) 288 | 241 in hex = f1 289 | 290 | # add # for 0b/0o/0x prefix 291 | >>> print("42 in binary = {:#b}".format(42)) 292 | 42 in binary = 0b101010 293 | 294 | >>> hex_str = "{:x}".format(42) 295 | >>> hex_str 296 | '2a' 297 | 298 | # can also use format built-in function 299 | >>> format(42, 'x') 300 | '2a' 301 | >>> format(42, '#x') 302 | '0x2a' 303 | 304 | # converting string to int 305 | >>> int(hex_str, base=16) 306 | 42 307 | >>> int('0x2a', base=16) 308 | 42 309 | ``` 310 | 311 | * similar to the `r` raw string prefix, using `f` prefix allows to represent format strings 312 | * introduced in Python v3.6 313 | * similar to `str.format()`, the variables/expressions are specified within `{}` 314 | 315 | ```python 316 | >>> num1 = 42 317 | >>> num2 = 7 318 | >>> f'{num1} + {num2} = {num1 + num2}' 319 | '42 + 7 = 49' 320 | >>> print(f'{num1} + {num2} = {num1 + num2}') 321 | 42 + 7 = 49 322 | 323 | >>> appx_pi = 22 / 7 324 | >>> f'{appx_pi:08.3f}' 325 | '0003.143' 326 | 327 | >>> f'{20:x}' 328 | '14' 329 | >>> f'{20:#x}' 330 | '0x14' 331 | ``` 332 | 333 | **Further Reading** 334 | 335 | * [Python docs - formatstrings](https://docs.python.org/3/library/string.html#formatstrings) - for more info and examples 336 | * [Python docs - f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings) - for more examples and caveats 337 | 338 |
339 | 340 | ### range function 341 | 342 | * By default `start=0` and `step=1`, so they can be skipped or defined as appropriate 343 | * `range(stop)` 344 | * `range(start, stop)` 345 | * `range(start, stop, step)` 346 | * Note that `range` output doesn't include `stop` value - it is always upto `stop` value but not including it 347 | * See [Lists](./Lists.md) chapters for discussion and examples on lists 348 | * [Python docs - Ranges](https://docs.python.org/3/library/stdtypes.html#typesseq-range) - for more info and examples 349 | 350 | ```python 351 | >>> range(5) 352 | range(0, 5) 353 | 354 | >>> list(range(5)) 355 | [0, 1, 2, 3, 4] 356 | 357 | >>> list(range(-2, 2)) 358 | [-2, -1, 0, 1] 359 | 360 | >>> list(range(1, 15, 2)) 361 | [1, 3, 5, 7, 9, 11, 13] 362 | 363 | >>> list(range(10, -5, -2)) 364 | [10, 8, 6, 4, 2, 0, -2, -4] 365 | ``` 366 | 367 |
368 | 369 | ### type function 370 | 371 | Useful to check data type of a variable or value 372 | 373 | ```python 374 | >>> type(5) 375 | 376 | 377 | >>> type('Hi there!') 378 | 379 | 380 | >>> type(range(7)) 381 | 382 | 383 | >>> type(None) 384 | 385 | 386 | >>> type(True) 387 | 388 | 389 | >>> arr = list(range(4)) 390 | >>> arr 391 | [0, 1, 2, 3] 392 | >>> type(arr) 393 | 394 | ``` 395 | 396 |
397 | 398 | ### Variable Scope 399 | 400 | ```python 401 | #!/usr/bin/python3 402 | 403 | def print_num(): 404 | print("Yeehaw! num is visible in this scope, its value is: " + str(num)) 405 | 406 | num = 25 407 | print_num() 408 | ``` 409 | 410 | * Variables defined before function call are visible within the function scope too 411 | * [Python docs - Default Argument Values](https://docs.python.org/3/tutorial/controlflow.html#default-argument-values) - see description for when default values are evaluated 412 | 413 | ``` 414 | $ ./variable_scope_1.py 415 | Yeehaw! num is visible in this scope, its value is: 25 416 | ``` 417 | 418 | What happens when a variable declared within a block is used outside of it? 419 | 420 | ```python 421 | #!/usr/bin/python3 422 | 423 | def square_of_num(num): 424 | sqr_num = num * num 425 | 426 | square_of_num(5) 427 | print("5 * 5 = {}".format(sqr_num)) 428 | ``` 429 | 430 | * Here, `sqr_num` is declared inside `square_of_num` function and not accessible outside the block 431 | 432 | ``` 433 | $ ./variable_scope_2.py 434 | Traceback (most recent call last): 435 | File "./variable_scope_2.py", line 7, in 436 | print("5 * 5 = {}".format(sqr_num)) 437 | NameError: name 'sqr_num' is not defined 438 | ``` 439 | 440 | One way to overcome this is to use the `global` keyword 441 | 442 | ```python 443 | #!/usr/bin/python3 444 | 445 | def square_of_num(num): 446 | global sqr_num 447 | sqr_num = num * num 448 | 449 | square_of_num(5) 450 | print("5 * 5 = {}".format(sqr_num)) 451 | ``` 452 | 453 | * Now, we can access `sqr_num` even outside the function definition 454 | 455 | ``` 456 | $ ./variable_scope_3.py 457 | 5 * 5 = 25 458 | ``` 459 | 460 | If a variable name is same outside and within function definition, the one inside the function will stay local to the block and not affect the one outside of it 461 | 462 | ```python 463 | #!/usr/bin/python3 464 | 465 | sqr_num = 4 466 | 467 | def square_of_num(num): 468 | sqr_num = num * num 469 | print("5 * 5 = {}".format(sqr_num)) 470 | 471 | square_of_num(5) 472 | print("Whoops! sqr_num is still {}!".format(sqr_num)) 473 | ``` 474 | 475 | * Note that using `global sqr_num` will affect the `sqr_num` variable outside the function 476 | 477 | ``` 478 | $ ./variable_scope_4.py 479 | 5 * 5 = 25 480 | Whoops! sqr_num is still 4! 481 | ``` 482 | 483 | **Further Reading** 484 | 485 | * [Python docs - scope example](https://docs.python.org/3/tutorial/classes.html#scopes-and-namespaces-example) 486 | * [Python docs - global statement](https://docs.python.org/3/reference/simple_stmts.html#the-global-statement) 487 | -------------------------------------------------------------------------------- /Further_Reading.md: -------------------------------------------------------------------------------- 1 | # Further Reading 2 | 3 | * [Standard topics not covered](#standard-topics-not-covered) 4 | * [Useful links on coding](#useful-links-on-coding) 5 | * [Python extensions](#python-extensions) 6 | 7 |
8 | 9 | [Python curated resources](https://github.com/learnbyexample/scripting_course/blob/master/Python_curated_resources.md) - online courses, books, coding practice sites, frameworks for GUI/Game/Web, cheatsheet, online help forums, etc.. 10 | 11 |
12 | 13 | ### Standard topics not covered 14 | 15 | * [Python docs - classes](https://docs.python.org/3/tutorial/classes.html) 16 | * [Python docs - virtual environment](https://docs.python.org/3/tutorial/venv.html) 17 | * [Python docs - modules and packaging](https://docs.python.org/3/tutorial/modules.html) 18 | * [Python packaging guide](https://python-packaging-user-guide.readthedocs.org/en/latest/) 19 | 20 |
21 | 22 | ### Useful links on coding 23 | 24 | * [Python docs - FAQ](https://docs.python.org/3/faq/index.html) 25 | * [list of common questions on stackoverflow](http://sopython.com/canon/) 26 | * [profiling Python code for performance](http://tutorials.pluralsight.com/python/quick-profiling-in-python) 27 | * [pylint](https://www.pylint.org/) 28 | * [code patterns](http://docs.quantifiedcode.com/python-code-patterns/) 29 | * [Performance tips](https://wiki.python.org/moin/PythonSpeed/PerformanceTips) 30 | 31 |
32 | 33 | ### Python extensions 34 | 35 | * [Cython](http://cython.org/) - optimising static compiler for both the Python programming language and the extended Cython programming language 36 | * [Jython](http://www.jython.org/) - Python for the Java Platform 37 | * [NumPy](http://www.numpy.org/) - fundamental package for scientific computing with Python 38 | -------------------------------------------------------------------------------- /Introduction.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | * [Installation](#installation) 4 | * [Hello World example](#hello-world-example) 5 | * [Python Interpreter](#python-interpreter) 6 | * [Python Standard Library](#python-standard-library) 7 | 8 | 9 | From [wikipedia](https://en.wikipedia.org/wiki/Python_(programming_language)) 10 | >Python is a widely used high-level, general-purpose, interpreted, dynamic programming language. Its design philosophy emphasizes code readability, and its syntax allows programmers to express concepts in fewer lines of code than possible in languages such as C++ or Java. The language provides constructs intended to enable clear programs on both a small and large scale 11 | 12 | [Guido van Rossum](https://en.wikipedia.org/wiki/Guido_van_Rossum) is the author of Python programming language, and continues to oversee the Python development process 13 | 14 |
15 | 16 | ### Installation 17 | 18 | * Get Python for your OS from official website - https://www.python.org/ 19 | * Most Linux distributions come with Python by default 20 | * See also [this guide](https://itsfoss.com/python-setup-linux/) for more detail as well as how to set up virtual environment, how to use **pip** (NEVER use **sudo pip** unless you know what you are doing) 21 | 22 |
23 | 24 | * Examples presented here is for **Unix-like systems**, Python version 3 and uses **bash** shell 25 | * You can also run Python code online 26 | * [pythontutor](http://www.pythontutor.com/visualize.html#mode=edit) - code execution in Python 2 and 3 versions, visualizing code flow and sample programs are among its features 27 | * [jupyter](https://try.jupyter.org/) - web application that allows you to create and share documents that contain live code, equations, visualizations and explanatory text 28 | * [ideone](https://ideone.com/) - online compiler and debugging tool which allows you to compile source code and execute it online in more than 60 programming languages 29 | * [Python Interpreter shell](https://www.python.org/shell/) 30 | * It is assumed that you are familiar with command line. If not, check out [this basic tutorial on ryanstutorials](http://ryanstutorials.net/linuxtutorial/) and [this list of curated resources for Linux](https://github.com/learnbyexample/scripting_course/blob/master/Linux_curated_resources.md) 31 | 32 |
33 | 34 | ### Hello World example 35 | 36 | Let's start with simple a Python program and how to run it 37 | 38 | ```python 39 | #!/usr/bin/python3 40 | 41 | print("Hello World") 42 | ``` 43 | 44 | The first line has two parts 45 | 46 | * `/usr/bin/python3` is the path of Python interpreter 47 | * `#!` called as **[shebang](https://en.wikipedia.org/wiki/Shebang_(Unix))**, directs the program loader to use the interpreter path provided 48 | 49 | The third line prints the message `Hello World` with a newline character added by default by the `print` function 50 | 51 | **Running Python program** 52 | 53 | You can write the program using text editor like **gedit**, **[vim](http://yannesposito.com/Scratch/en/blog/Learn-Vim-Progressively/)** or [other editors](https://github.com/learnbyexample/Linux_command_line/blob/master/Working_with_Files_and_Directories.md#text-editors) 54 | After saving the file, give execute permission and run the program from a terminal 55 | 56 | ``` 57 | $ chmod +x hello_world.py 58 | 59 | $ ./hello_world.py 60 | Hello World 61 | ``` 62 | 63 | To find out path and version of Python in your system 64 | 65 | ``` 66 | $ type python3 67 | python3 is /usr/bin/python3 68 | 69 | $ python3 --version 70 | Python 3.4.3 71 | ``` 72 | 73 | If you happen to follow a book/tutorial on Python version 2 or coming with Perl experience, it is a common mistake to forget () with `print` function 74 | 75 | ```python 76 | #!/usr/bin/python3 77 | 78 | print "Have a nice day" 79 | ``` 80 | 81 | * Depending on type of error, it may be easy to spot the mistake based on error messages printed on executing the program 82 | * In this example, we get the appropriate `Missing parentheses` message 83 | 84 | ``` 85 | $ ./syntax_error.py 86 | File "./syntax_error.py", line 3 87 | print "Have a nice day" 88 | ^ 89 | SyntaxError: Missing parentheses in call to 'print' 90 | ``` 91 | 92 | * single line comments start with `#` 93 | * `#!` has special meaning only on first line of program 94 | * we will see multiline comments in later chapters 95 | 96 | ```python 97 | #!/usr/bin/python3 98 | 99 | # Greeting message 100 | print("Hello World") 101 | ``` 102 | 103 | **Further Reading** 104 | 105 | * [Python docs - version 3](https://docs.python.org/3/index.html) 106 | * [Different ways of executing Python programs](https://docs.python.org/3/using/windows.html#executing-scripts) 107 | * [Where is Python used?](https://www.python.org/about/apps/) 108 | * [Python docs - Errors and Exceptions](https://docs.python.org/3/tutorial/errors.html) 109 | * [Common syntax errors](https://opencs.uwaterloo.ca/python-from-scratch/7/7/transcript) 110 | 111 |
112 | 113 | ### Python Interpreter 114 | 115 | * It is generally used to execute snippets of Python language as a means to learning Python or for debugging purposes 116 | * The prompt is usually `>>>` 117 | * Some of the topics in coming chapters will be complemented with examples using the Python Interpreter 118 | * A special variable `_` holds the result of last printed expression 119 | * One can type part of command and repeatedly press Up arrow key to match commands from history 120 | * Press `Ctrl+l` to clear the screen, keeping any typed command intact 121 | * `exit()` to exit 122 | 123 | ```python 124 | $ python3 125 | Python 3.4.3 (default, Oct 14 2015, 20:28:29) 126 | [GCC 4.8.4] on linux 127 | Type "help", "copyright", "credits" or "license" for more information. 128 | >>> print("hi") 129 | hi 130 | >>> abc 131 | Traceback (most recent call last): 132 | File "", line 1, in 133 | NameError: name 'abc' is not defined 134 | >>> num = 5 135 | >>> num 136 | 5 137 | >>> 3 + 4 138 | 7 139 | >>> 12 + _ 140 | 19 141 | >>> exit() 142 | ``` 143 | 144 | **Further Reading** 145 | 146 | * [Python docs - Using the Python Interpreter](https://docs.python.org/3/tutorial/interpreter.html) 147 | * [Python docs - Interpreter example](https://docs.python.org/3/tutorial/introduction.html#using-python-as-a-calculator) 148 | 149 |
150 | 151 | ### Python Standard Library 152 | 153 | * [Python docs - library](https://docs.python.org/3/library/index.html) 154 | * [pypi - repository of software for the Python programming language](https://pypi.python.org/pypi) 155 | 156 | >The library contains built-in modules (written in C) that provide access to system functionality such as file I/O that would otherwise be inaccessible to Python programmers, as well as modules written in Python that provide standardized solutions for many problems that occur in everyday programming. 157 | 158 | >Some of these modules are explicitly designed to encourage and enhance the portability of Python programs by abstracting away platform-specifics into platform-neutral APIs 159 | -------------------------------------------------------------------------------- /Lists.md: -------------------------------------------------------------------------------- 1 | # Lists 2 | 3 | * [Assigning List variables](#assigning-list-variables) 4 | * [Slicing and Modifying Lists](#slicing-and-modifying-lists) 5 | * [Copying Lists](#copying-lists) 6 | * [List Methods and Miscellaneous](#list-methods-and-miscellaneous) 7 | * [Looping](#looping) 8 | * [List Comprehension](#list-comprehension) 9 | * [Getting List as user input](#list-user-input) 10 | * [Getting random items from list](#getting-random-items-from-list) 11 | 12 |
13 | 14 | ### Assigning List variables 15 | 16 | * Simple lists and retrieving list elements 17 | 18 | ```python 19 | >>> vowels = ['a', 'e', 'i', 'o', 'u'] 20 | >>> vowels 21 | ['a', 'e', 'i', 'o', 'u'] 22 | >>> vowels[0] 23 | 'a' 24 | >>> vowels[2] 25 | 'i' 26 | >>> vowels[10] 27 | Traceback (most recent call last): 28 | File "", line 1, in 29 | IndexError: list index out of range 30 | 31 | >>> even_numbers = list(range(2, 11, 2)) 32 | >>> even_numbers 33 | [2, 4, 6, 8, 10] 34 | >>> even_numbers[-1] 35 | 10 36 | >>> even_numbers[-2] 37 | 8 38 | >>> even_numbers[-15] 39 | Traceback (most recent call last): 40 | File "", line 1, in 41 | IndexError: list index out of range 42 | ``` 43 | 44 | * Mix of data types and multi-dimensional lists 45 | 46 | ```python 47 | >>> student = ['learnbyexample', 2016, 'Linux, Vim, Python'] 48 | >>> print(student) 49 | ['learnbyexample', 2016, 'Linux, Vim, Python'] 50 | 51 | >>> list_2D = [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] 52 | >>> list_2D[0][0] 53 | 1 54 | >>> list_2D[1][0] 55 | 1.2 56 | ``` 57 | 58 | * [Python docs - lists](https://docs.python.org/3/tutorial/introduction.html#lists) 59 | 60 |
61 | 62 | ### Slicing and Modifying Lists 63 | 64 | * Like the `range()` function, list index has `start:stop:step` format, `stop` value being non-inclusive 65 | * [stackoverflow - explain slice notation](https://stackoverflow.com/questions/509211/explain-pythons-slice-notation) 66 | 67 | ```python 68 | >>> books = ['Harry Potter', 'Sherlock Holmes', 'To Kill a Mocking Bird'] 69 | >>> books[2] = "Ender's Game" 70 | >>> print(books) 71 | ['Harry Potter', 'Sherlock Holmes', "Ender's Game"] 72 | 73 | >>> prime = [2, 3, 5, 7, 11] 74 | >>> prime[2:4] 75 | [5, 7] 76 | >>> prime[:3] 77 | [2, 3, 5] 78 | >>> prime[3:] 79 | [7, 11] 80 | 81 | >>> prime[-1] 82 | 11 83 | >>> prime[-1:] 84 | [11] 85 | >>> prime[-2:] 86 | [7, 11] 87 | 88 | 89 | >>> prime[::1] 90 | [2, 3, 5, 7, 11] 91 | >>> prime[::2] 92 | [2, 5, 11] 93 | >>> prime[3:1:-1] 94 | [7, 5] 95 | >>> prime[::-1] 96 | [11, 7, 5, 3, 2] 97 | >>> prime[:] 98 | [2, 3, 5, 7, 11] 99 | ``` 100 | 101 | * when `start` and `stop` values are same 102 | * Useful when they are generated programmatically, see [text processing exercise](./Exercises.md#text-processing) for example 103 | 104 | ```bash 105 | >>> nums = [1.2, -0.2, 0, 2] 106 | >>> nums[0:0] 107 | [] 108 | >>> nums[2:2] 109 | [] 110 | >>> nums[-1:-1] 111 | [] 112 | >>> nums[21:21] 113 | [] 114 | ``` 115 | 116 | * The indexing format can be used to extract from list variable or modify itself 117 | 118 | ```python 119 | >>> nums = [1.2, -0.2, 0, 2] 120 | >>> nums[:2] = [1] 121 | >>> nums 122 | [1, 0, 2] 123 | 124 | >>> nums = [1.2, -0.2, 0, 2, 4, 23] 125 | >>> nums[:5:2] = [1, 4, 3] 126 | >>> nums 127 | [1, -0.2, 4, 2, 3, 23] 128 | 129 | >>> nums = [1, 2, 3, 23] 130 | >>> nums[::-1] = [1, 4, 5, 2] 131 | >>> nums 132 | [2, 5, 4, 1] 133 | ``` 134 | 135 | * helps to modify a list without changing `id`, which is useful if the variable name is referenced elsewhere (see next section) 136 | 137 | ```python 138 | >>> id(nums) 139 | 140598790579336 140 | >>> nums[:] = [1, 2, 5, 4.3] 141 | >>> nums 142 | [1, 2, 5, 4.3] 143 | >>> id(nums) 144 | 140598790579336 145 | 146 | # assignment without using [:] will change id 147 | >>> nums = [1.2, -0.2, 0, 2] 148 | >>> id(nums) 149 | 140598782943752 150 | ``` 151 | 152 |
153 | 154 | ### Copying Lists 155 | 156 | * Variables in Python contain reference to objects 157 | * For example, when an integer variable is modified, the variable's reference is updated with new object 158 | * the [id()](https://docs.python.org/3/library/functions.html#id) function returns the "identity" of an object 159 | * For variables referring to immutable types like integer and strings, this distinction usually doesn't cause confusion in their usage 160 | 161 | ```python 162 | >>> a = 5 163 | >>> id(a) 164 | 10105952 165 | >>> a = 10 166 | >>> id(a) 167 | 10106112 168 | 169 | >>> b = a 170 | >>> id(b) 171 | 10106112 172 | >>> b = 4 173 | >>> b 174 | 4 175 | >>> a 176 | 10 177 | >>> id(b) 178 | 10105920 179 | ``` 180 | 181 | * But for variables referring to mutable types like lists, it is important to know how variables are copied and passed to functions 182 | * When an element of list variable is modified, it does so by changing the value (mutation) of object at that index 183 | 184 | ```python 185 | >>> a = [1, 2, 5, 4.3] 186 | >>> a 187 | [1, 2, 5, 4.3] 188 | >>> b = a 189 | >>> b 190 | [1, 2, 5, 4.3] 191 | >>> id(a) 192 | 140684757120264 193 | >>> id(b) 194 | 140684757120264 195 | >>> b[0] = 'xyz' 196 | >>> b 197 | ['xyz', 2, 5, 4.3] 198 | >>> a 199 | ['xyz', 2, 5, 4.3] 200 | ``` 201 | 202 | * avoid copying lists using indexing format, it works for 1D lists but not for higher dimensions 203 | 204 | ```python 205 | >>> prime = [2, 3, 5, 7, 11] 206 | >>> b = prime[:] 207 | >>> id(prime) 208 | 140684818101064 209 | >>> id(b) 210 | 140684818886024 211 | >>> b[0] = 'a' 212 | >>> b 213 | ['a', 3, 5, 7, 11] 214 | >>> prime 215 | [2, 3, 5, 7, 11] 216 | 217 | >>> list_2D = [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] 218 | >>> a = list_2D[:] 219 | >>> id(list_2D) 220 | 140684818102344 221 | >>> id(a) 222 | 140684818103048 223 | >>> a 224 | [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] 225 | >>> a[0][0] = 'a' 226 | >>> a 227 | [['a', 3, 2, 10], [1.2, -0.2, 0, 2]] 228 | >>> list_2D 229 | [['a', 3, 2, 10], [1.2, -0.2, 0, 2]] 230 | ``` 231 | 232 | * use the [copy](https://docs.python.org/3/library/copy.html#module-copy) module instead 233 | 234 | ```python 235 | >>> import copy 236 | >>> list_2D = [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] 237 | >>> c = copy.deepcopy(list_2D) 238 | >>> c[0][0] = 'a' 239 | >>> c 240 | [['a', 3, 2, 10], [1.2, -0.2, 0, 2]] 241 | >>> list_2D 242 | [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] 243 | ``` 244 | 245 |
246 | 247 | ### List Methods and Miscellaneous 248 | 249 | * adding elements to list 250 | 251 | ```python 252 | >>> books = [] 253 | >>> books 254 | [] 255 | >>> books.append('Harry Potter') 256 | >>> books 257 | ['Harry Potter'] 258 | 259 | >>> even_numbers 260 | [2, 4, 6, 8, 10] 261 | >>> even_numbers += [12, 14, 16, 18, 20] 262 | >>> even_numbers 263 | [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 264 | 265 | >>> even_numbers = [2, 4, 6, 8, 10] 266 | >>> even_numbers.extend([12, 14, 16, 18, 20]) 267 | >>> even_numbers 268 | [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] 269 | 270 | >>> a = [[1, 3], [2, 4]] 271 | >>> a.extend([[5, 6]]) 272 | >>> a 273 | [[1, 3], [2, 4], [5, 6]] 274 | ``` 275 | 276 | * deleting elements from a list - based on index 277 | 278 | ```python 279 | >>> prime = [2, 3, 5, 7, 11] 280 | >>> prime.pop() 281 | 11 282 | >>> prime 283 | [2, 3, 5, 7] 284 | >>> prime.pop(0) 285 | 2 286 | >>> prime 287 | [3, 5, 7] 288 | 289 | >>> list_2D = [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] 290 | >>> list_2D[0].pop(0) 291 | 1 292 | >>> list_2D 293 | [[3, 2, 10], [1.2, -0.2, 0, 2]] 294 | 295 | >>> list_2D.pop(1) 296 | [1.2, -0.2, 0, 2] 297 | >>> list_2D 298 | [[3, 2, 10]] 299 | ``` 300 | 301 | * using [del](https://docs.python.org/3/reference/simple_stmts.html#del) to delete elements 302 | 303 | ```python 304 | >>> nums = [1.2, -0.2, 0, 2, 4, 23] 305 | >>> del nums[1] 306 | >>> nums 307 | [1.2, 0, 2, 4, 23] 308 | # can use slicing notation as well 309 | >>> del nums[1:4] 310 | >>> nums 311 | [1.2, 23] 312 | 313 | >>> list_2D = [[1, 3, 2, 10], [1.2, -0.2, 0, 2]] 314 | >>> del list_2D[0][1] 315 | >>> list_2D 316 | [[1, 2, 10], [1.2, -0.2, 0, 2]] 317 | 318 | >>> del list_2D[0] 319 | >>> list_2D 320 | [[1.2, -0.2, 0, 2]] 321 | ``` 322 | 323 | * clearing list 324 | 325 | ```python 326 | >>> prime = [2, 3, 5, 7, 11] 327 | >>> prime.clear() 328 | >>> prime 329 | [] 330 | ``` 331 | 332 | * deleting elements from a list - based on value 333 | 334 | ```python 335 | >>> even_numbers = [2, 4, 6, 8, 10] 336 | >>> even_numbers.remove(8) 337 | >>> even_numbers 338 | [2, 4, 6, 10] 339 | >>> even_numbers.remove(12) 340 | Traceback (most recent call last): 341 | File "", line 1, in 342 | ValueError: list.remove(x): x not in list 343 | ``` 344 | 345 | * inserting elements at a particular index 346 | 347 | ```python 348 | >>> books = ['Harry Potter', 'Sherlock Holmes', 'To Kill a Mocking Bird'] 349 | >>> books.insert(2, "The Martian") 350 | >>> books 351 | ['Harry Potter', 'Sherlock Holmes', 'The Martian', 'To Kill a Mocking Bird'] 352 | ``` 353 | 354 | * get index of an element 355 | 356 | ```python 357 | >>> even_numbers = [2, 4, 6, 8, 10] 358 | >>> even_numbers.index(6) 359 | 2 360 | >>> even_numbers.index(12) 361 | Traceback (most recent call last): 362 | File "", line 1, in 363 | ValueError: 12 is not in list 364 | ``` 365 | 366 | * checking if an element is present 367 | 368 | ```python 369 | >>> prime = [2, 3, 5, 7, 11] 370 | >>> 3 in prime 371 | True 372 | >>> 13 in prime 373 | False 374 | ``` 375 | 376 | * [sorting](https://docs.python.org/3/library/stdtypes.html#list.sort) 377 | 378 | ```python 379 | >>> a = [1, 5.3, 321, 0, 1, 2] 380 | >>> a.sort() 381 | >>> a 382 | [0, 1, 1, 2, 5.3, 321] 383 | 384 | >>> a = [1, 5.3, 321, 0, 1, 2] 385 | >>> a.sort(reverse=True) 386 | >>> a 387 | [321, 5.3, 2, 1, 1, 0] 388 | ``` 389 | 390 | Sort [based on key](https://docs.python.org/3/howto/sorting.html#sortinghowto), for example based on string length 391 | [lambda expressions](https://docs.python.org/3/tutorial/controlflow.html#lambda-expressions) are helpful to pass custom single expression, say sort based on second letter 392 | 393 | ```python 394 | >>> words = ['fuliginous', 'crusado', 'morello', 'irk', 'seam'] 395 | >>> words.sort(key=len) 396 | >>> words 397 | ['irk', 'seam', 'crusado', 'morello', 'fuliginous'] 398 | 399 | >>> words.sort(key=lambda x: x[1]) 400 | >>> words 401 | ['seam', 'morello', 'irk', 'crusado', 'fuliginous'] 402 | ``` 403 | 404 | If original list should not be changed, use [sorted](https://docs.python.org/3/library/functions.html#sorted) function 405 | 406 | ```python 407 | >>> nums = [-1, 34, 0.2, -4, 309] 408 | >>> nums_desc = sorted(nums, reverse=True) 409 | >>> nums_desc 410 | [309, 34, 0.2, -1, -4] 411 | 412 | >>> sorted(nums, key=abs) 413 | [0.2, -1, -4, 34, 309] 414 | ``` 415 | 416 | * `min` and `max` 417 | 418 | ```python 419 | >>> a = [321, 899.232, 5.3, 2, 1, -1] 420 | >>> min(a) 421 | -1 422 | >>> max(a) 423 | 899.232 424 | ``` 425 | 426 | * Number of times an item is present 427 | 428 | ```python 429 | >>> nums = [15, 99, 19, 382, 43, 19] 430 | >>> nums.count(99) 431 | 1 432 | >>> nums.count(19) 433 | 2 434 | >>> nums.count(23) 435 | 0 436 | ``` 437 | 438 | * reverse list in place 439 | 440 | ```python 441 | >>> prime = [2, 3, 5, 7, 11] 442 | >>> id(prime) 443 | 140684818102088 444 | >>> prime.reverse() 445 | >>> prime 446 | [11, 7, 5, 3, 2] 447 | >>> id(prime) 448 | 140684818102088 449 | 450 | >>> a = [1, 5.3, 321, 0, 1, 2] 451 | >>> id(a) 452 | 140684818886024 453 | >>> a = a[::-1] 454 | >>> a 455 | [2, 1, 0, 321, 5.3, 1] 456 | >>> id(a) 457 | 140684818102664 458 | ``` 459 | 460 | * [len](https://docs.python.org/3/library/functions.html#len) function to get size of lists 461 | 462 | ```python 463 | >>> prime 464 | [2, 3, 5, 7, 11] 465 | >>> len(prime) 466 | 5 467 | 468 | >>> s = len(prime) // 2 469 | >>> prime[:s] 470 | [2, 3] 471 | >>> prime[s:] 472 | [5, 7, 11] 473 | ``` 474 | 475 | * summing numeric lists 476 | 477 | ```python 478 | >>> a 479 | [321, 5.3, 2, 1, 1, 0] 480 | >>> sum(a) 481 | 330.3 482 | ``` 483 | 484 | * [all](https://docs.python.org/3/library/functions.html#all) and [any](https://docs.python.org/3/library/functions.html#any) functions 485 | 486 | ```python 487 | >>> conditions = [True, False, True] 488 | >>> all(conditions) 489 | False 490 | >>> any(conditions) 491 | True 492 | 493 | >>> conditions[1] = True 494 | >>> all(conditions) 495 | True 496 | 497 | >>> a = [321, 5.3, 2, 1, 1, 0] 498 | >>> all(a) 499 | False 500 | >>> any(a) 501 | True 502 | ``` 503 | 504 | * comparing lists 505 | 506 | ```python 507 | >>> prime 508 | [2, 3, 5, 7, 11] 509 | >>> a = [4, 2] 510 | >>> prime == a 511 | False 512 | 513 | >>> prime == [2, 3, 5, 11, 7] 514 | False 515 | >>> prime == [2, 3, 5, 7, 11] 516 | True 517 | ``` 518 | 519 | **Further Reading** 520 | 521 | * [Python docs - more on lists](https://docs.python.org/3/tutorial/datastructures.html#more-on-lists) 522 | * [Python docs - collections](https://docs.python.org/3/library/collections.html) 523 | 524 |
525 | 526 | ### Looping 527 | 528 | ```python 529 | #!/usr/bin/python3 530 | 531 | numbers = [2, 12, 3, 25, 624, 21, 5, 9, 12] 532 | odd_numbers = [] 533 | even_numbers = [] 534 | 535 | for num in numbers: 536 | odd_numbers.append(num) if(num % 2) else even_numbers.append(num) 537 | 538 | print("numbers: {}".format(numbers)) 539 | print("odd_numbers: {}".format(odd_numbers)) 540 | print("even_numbers: {}".format(even_numbers)) 541 | ``` 542 | 543 | * usually, it is enough to deal with every element of list without needing index of elements 544 | 545 | ``` 546 | $ ./list_looping.py 547 | numbers: [2, 12, 3, 25, 624, 21, 5, 9, 12] 548 | odd_numbers: [3, 25, 21, 5, 9] 549 | even_numbers: [2, 12, 624, 12] 550 | ``` 551 | 552 | * use `enumerate()` if both index and element is needed 553 | 554 | ```python 555 | #!/usr/bin/python3 556 | 557 | north_dishes = ['Aloo tikki', 'Baati', 'Khichdi', 'Makki roti', 'Poha'] 558 | 559 | print("My favorite North Indian dishes:") 560 | for idx, item in enumerate(north_dishes): 561 | print("{}. {}".format(idx + 1, item)) 562 | ``` 563 | 564 | * In this case, we get a [tuple](./Sequence_Set_Dict_data_types.md#tuples) every iteration consisting of a count (default value 0) and an item from the list 565 | * [Python docs - enumerate](https://docs.python.org/3/library/functions.html#enumerate) 566 | 567 | ``` 568 | $ ./list_looping_enumeration.py 569 | My favorite North Indian dishes: 570 | 1. Aloo tikki 571 | 2. Baati 572 | 3. Khichdi 573 | 4. Makki roti 574 | 5. Poha 575 | ``` 576 | 577 | * a start value can also be specified 578 | 579 | ```python 580 | >>> north_dishes = ['Aloo tikki', 'Baati', 'Khichdi', 'Makki roti', 'Poha'] 581 | >>> for idx, item in enumerate(north_dishes, start=1): 582 | ... print(idx, item, sep='. ') 583 | ... 584 | 1. Aloo tikki 585 | 2. Baati 586 | 3. Khichdi 587 | 4. Makki roti 588 | 5. Poha 589 | ``` 590 | 591 | * use `zip()` to iterate over two or more lists simultaneously 592 | * [Python docs - zip](https://docs.python.org/3/library/functions.html#zip) 593 | 594 | ```python 595 | >>> odd = [1, 3, 5] 596 | >>> even = [2, 4, 6] 597 | >>> for i, j in zip(odd, even): 598 | ... print(i + j) 599 | ... 600 | 3 601 | 7 602 | 11 603 | ``` 604 | 605 |
606 | 607 | ### List Comprehension 608 | 609 | ```python 610 | #!/usr/bin/python3 611 | 612 | import time 613 | 614 | numbers = list(range(1,100001)) 615 | fl_square_numbers = [] 616 | 617 | # reference time 618 | t0 = time.perf_counter() 619 | 620 | # ------------ for loop ------------ 621 | for num in numbers: 622 | fl_square_numbers.append(num * num) 623 | 624 | # reference time 625 | t1 = time.perf_counter() 626 | 627 | # ------- list comprehension ------- 628 | lc_square_numbers = [num * num for num in numbers] 629 | 630 | # performance results 631 | t2 = time.perf_counter() 632 | fl_time = t1 - t0 633 | lc_time = t2 - t1 634 | improvement = (fl_time - lc_time) / fl_time * 100 635 | 636 | print("Time with for loop: {:.4f}".format(fl_time)) 637 | print("Time with list comprehension: {:.4f}".format(lc_time)) 638 | print("Improvement: {:.2f}%".format(improvement)) 639 | 640 | if fl_square_numbers == lc_square_numbers: 641 | print("\nfl_square_numbers and lc_square_numbers are equivalent") 642 | else: 643 | print("\nfl_square_numbers and lc_square_numbers are NOT equivalent") 644 | ``` 645 | 646 | * List comprehensions is a Pythonic way for some of the common looping constructs 647 | * Usually is a more readable and time saving option than loops 648 | * In this example, not having to call `append()` method also saves lot of time in case of list comprehension 649 | * Time values in this example is indicative and not to be taken as absolute 650 | * It usually varies even between two runs, let alone different machines 651 | 652 | ``` 653 | $ ./list_comprehension.py 654 | Time with for loop: 0.0142 655 | Time with list comprehension: 0.0062 656 | Improvement: 56.36% 657 | 658 | fl_square_numbers and lc_square_numbers are equivalent 659 | ``` 660 | 661 | * conditional list comprehension 662 | 663 | ```python 664 | # using if-else conditional in list comprehension 665 | numbers = [2, 12, 3, 25, 624, 21, 5, 9, 12] 666 | odd_numbers = [] 667 | even_numbers = [] 668 | [odd_numbers.append(num) if(num % 2) else even_numbers.append(num) for num in numbers] 669 | 670 | # or a more simpler and readable approach 671 | numbers = [2, 12, 3, 25, 624, 21, 5, 9, 12] 672 | odd_numbers = [num for num in numbers if num % 2] 673 | even_numbers = [num for num in numbers if not num % 2] 674 | ``` 675 | 676 | * zip example 677 | 678 | ```python 679 | >>> p = [1, 3, 5] 680 | >>> q = [3, 214, 53] 681 | >>> [i+j for i,j in zip(p, q)] 682 | [4, 217, 58] 683 | >>> [i*j for i,j in zip(p, q)] 684 | [3, 642, 265] 685 | ``` 686 | 687 | use [generator expressions](https://docs.python.org/3/tutorial/classes.html#generator-expressions) if sequence needs to be passed onto another function 688 | 689 | ```python 690 | >>> sum(i*j for i,j in zip(p, q)) 691 | 910 692 | ``` 693 | 694 | **Further Reading** 695 | 696 | For more examples, including nested loops, check these 697 | 698 | * [Python docs - list comprehensions](https://docs.python.org/3/tutorial/datastructures.html#list-comprehensions) 699 | * [Python List Comprehensions: Explained Visually](http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/) 700 | * [are list comprehensions and functional functions faster than for loops](http://stackoverflow.com/questions/22108488/are-list-comprehensions-and-functional-functions-faster-than-for-loops) 701 | * [Python docs - perf_counter](https://docs.python.org/3/library/time.html#time.perf_counter) 702 | * [understanding perf_counter and process_time](http://stackoverflow.com/questions/25785243/understanding-time-perf-counter-and-time-process-time) 703 | * [Python docs - timeit](https://docs.python.org/3/library/timeit.html) 704 | 705 |
706 | 707 | ### Getting List as user input 708 | 709 | ```python 710 | >>> b = input('Enter strings separated by space: ').split() 711 | Enter strings separated by space: foo bar baz 712 | >>> b 713 | ['foo', 'bar', 'baz'] 714 | 715 | >>> nums = [int(n) for n in input('Enter numbers separated by space: ').split()] 716 | Enter numbers separated by space: 1 23 5 717 | >>> nums 718 | [1, 23, 5] 719 | 720 | >>> ip_str = input('Enter prime numbers separated by comma: ') 721 | Enter prime numbers separated by comma: 3,5,7 722 | >>> primes = [int(n) for n in ip_str.split(',')] 723 | >>> primes 724 | [3, 5, 7] 725 | ``` 726 | 727 | * Since user input is all treated as string, need to process based on agreed delimiter and required data type 728 | 729 |
730 | 731 | ### Getting random items from list 732 | 733 | * Get a random item 734 | 735 | ```python 736 | >>> import random 737 | >>> a = [4, 5, 2, 76] 738 | >>> random.choice(a) 739 | 76 740 | >>> random.choice(a) 741 | 4 742 | ``` 743 | 744 | * Randomly re-arrange items of list 745 | 746 | ```python 747 | >>> random.shuffle(a) 748 | >>> a 749 | [5, 2, 76, 4] 750 | ``` 751 | 752 | * Get random slice of list, doesn't modify the list variable 753 | 754 | ```python 755 | >>> random.sample(a, k=3) 756 | [76, 2, 5] 757 | 758 | >>> random.sample(range(1000), k=5) 759 | [68, 203, 15, 757, 580] 760 | ``` 761 | 762 | * Get random items from list without repetition by creating an iterable using [Python docs - iter](https://docs.python.org/3/library/functions.html#iter) function 763 | * The difference from simply using shuffled list is that this avoids the need to maintain a separate index counter and automatic exception raised if it goes out of range 764 | 765 | ```python 766 | >>> nums = [1, 3, 6, -12, 1.2, 3.14] 767 | >>> random.shuffle(nums) 768 | >>> nums_iter = iter(nums) 769 | >>> print(next(nums_iter)) 770 | 3.14 771 | >>> print(next(nums_iter)) 772 | 1.2 773 | >>> for n in nums_iter: 774 | ... print(n) 775 | ... 776 | 1 777 | 3 778 | -12 779 | 6 780 | >>> print(next(nums_iter)) 781 | Traceback (most recent call last): 782 | File "", line 1, in 783 | StopIteration 784 | ``` 785 | 786 | * [Python docs - random](https://docs.python.org/3/library/random.html) for more info 787 | * new in version 3.6 - [random.choices](https://docs.python.org/3/library/random.html#random.choices) 788 | * [Python docs - next](https://docs.python.org/3/library/functions.html#next) 789 | * See also [yield](https://stackoverflow.com/questions/231767/what-is-the-function-of-the-yield-keyword) 790 | -------------------------------------------------------------------------------- /Number_and_String_datatypes.md: -------------------------------------------------------------------------------- 1 | # Number and String data types 2 | 3 | * [Numbers](#numbers) 4 | * [String](#string) 5 | * [Constants](#constants) 6 | * [Built-in Operators](#built-in-operators) 7 | 8 | Variable data type is automatically determined by Python. They only need to be assigned some value before using it elsewhere - like print function or part of expression 9 | 10 |
11 | 12 | ### Numbers 13 | 14 | * Integer examples 15 | 16 | ```python 17 | >>> num1 = 7 18 | >>> num2 = 42 19 | >>> total = num1 + num2 20 | >>> print(total) 21 | 49 22 | >>> total 23 | 49 24 | 25 | # no limit to integer precision, only limited by available memory 26 | >>> 34 ** 32 27 | 10170102859315411774579628461341138023025901305856 28 | 29 | # using single / gives floating point output 30 | >>> 9 / 5 31 | 1.8 32 | 33 | # using double / gives only the integer portion, no rounding 34 | >>> 9 // 5 35 | 1 36 | 37 | >>> 9 % 5 38 | 4 39 | ``` 40 | 41 | * Floating point examples 42 | 43 | ```python 44 | >>> appx_pi = 22 / 7 45 | >>> appx_pi 46 | 3.142857142857143 47 | 48 | >>> area = 42.16 49 | >>> appx_pi + area 50 | 45.30285714285714 51 | 52 | >>> num1 53 | 7 54 | >>> num1 + area 55 | 49.16 56 | ``` 57 | 58 | * the [E scientific notation](https://en.wikipedia.org/wiki/Scientific_notation#E_notation) can be used as well 59 | 60 | ```python 61 | >>> sci_num1 = 3.982e5 62 | >>> sci_num2 = 9.32e-1 63 | >>> sci_num1 + sci_num2 64 | 398200.932 65 | 66 | >>> 2.13e21 + 5.23e22 67 | 5.443e+22 68 | ``` 69 | 70 | * Binary numbers are prefixed with `0b` or `0B` (i.e digit 0 followed by lower/upper case letter b) 71 | * Octal numbers are prefixed with `0o` or `0O` (i.e digit 0 followed by lower/upper case letter o) 72 | * Similarly, Hexadecimal numbers are prefixed with `0x` or `0X` 73 | 74 | ```python 75 | >>> bin_num = 0b101 76 | >>> oct_num = 0o12 77 | >>> hex_num = 0xF 78 | 79 | >>> bin_num 80 | 5 81 | >>> oct_num 82 | 10 83 | >>> hex_num 84 | 15 85 | 86 | >>> oct_num + hex_num 87 | 25 88 | ``` 89 | 90 | * `_` can be used between digits for readability 91 | * introduced in Python v3.6 92 | 93 | ```python 94 | >>> 1_000_000 95 | 1000000 96 | >>> 1_00.3_352 97 | 100.3352 98 | >>> 0xff_ab1 99 | 1047217 100 | 101 | # f-strings formatting explained in a later chapter 102 | >>> num = 34 ** 32 103 | >>> print(f'{num:_}') 104 | 10_170_102_859_315_411_774_579_628_461_341_138_023_025_901_305_856 105 | ``` 106 | 107 | **Further Reading** 108 | 109 | * [Python docs - numbers](https://docs.python.org/3/tutorial/introduction.html#numbers) 110 | * [decimal](https://docs.python.org/3/library/decimal.html) 111 | * [fractions](https://docs.python.org/3/library/fractions.html) 112 | * [complex](https://docs.python.org/3/library/functions.html#complex) 113 | * [Python docs - keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords) - do not use these as variables 114 | 115 |
116 | 117 | ### String 118 | 119 | * strings can be declared using single or double quotes 120 | * Use `\` to escape quotes which are part of string itself if the string contains both single and double quotes 121 | 122 | ```python 123 | >>> str1 = 'This is a string' 124 | >>> str1 125 | 'This is a string' 126 | >>> greeting = "Hello World!" 127 | >>> greeting 128 | 'Hello World!' 129 | 130 | >>> weather = "It's a nice and warm day" 131 | >>> weather 132 | "It's a nice and warm day" 133 | >>> print(weather) 134 | It's a nice and warm day 135 | 136 | >>> weather = 'It\'s a nice and warm day' 137 | >>> print(weather) 138 | It's a nice and warm day 139 | ``` 140 | 141 | * Escape sequences like newline character `\n` can be used within string declaration 142 | 143 | ```python 144 | >>> colors = 'Blue\nRed\nGreen' 145 | >>> colors 146 | 'Blue\nRed\nGreen' 147 | 148 | >>> print(colors) 149 | Blue 150 | Red 151 | Green 152 | ``` 153 | 154 | * Use `r` prefix (stands for **raw**) if you do not want escape sequences to be interpreted 155 | * It is commonly used with regular expressions, see [Pattern matching and extraction](./Text_Processing.md#pattern-matching-and-extraction) for examples 156 | 157 | ```bash 158 | >>> raw_str = r'Blue\nRed\nGreen' 159 | >>> print(raw_str) 160 | Blue\nRed\nGreen 161 | 162 | # to see how the string is stored internally 163 | >>> raw_str 164 | 'Blue\\nRed\\nGreen' 165 | ``` 166 | 167 | * String concatenation and repetition 168 | 169 | ```python 170 | >>> str1 = 'Hello' 171 | >>> str2 = ' World' 172 | >>> print(str1 + str2) 173 | Hello World 174 | 175 | >>> style_char = '-' 176 | >>> style_char * 10 177 | '----------' 178 | 179 | >>> word = 'buffalo ' 180 | >>> print(word * 8) 181 | buffalo buffalo buffalo buffalo buffalo buffalo buffalo buffalo 182 | 183 | # Python v3.6 allows variable interpolation with f-strings 184 | >>> msg = f'{str1} there' 185 | >>> msg 186 | 'Hello there' 187 | ``` 188 | 189 | * Triple quoted strings 190 | * like single line strings, `"""` or `'''` can be used as required as well as escape characters using `\` 191 | 192 | ```python 193 | #!/usr/bin/python3 194 | 195 | """ 196 | This line is part of multiline comment 197 | 198 | This program shows examples of triple quoted strings 199 | """ 200 | 201 | # assigning multiple line string to variable 202 | poem = """\ 203 | The woods are lovely, dark and deep, 204 | But I have promises to keep, 205 | And miles to go before I sleep, 206 | And miles to go before I sleep. 207 | """ 208 | 209 | print(poem, end='') 210 | ``` 211 | 212 | * Triple quoted strings also help in documentation, see [Docstrings](./Docstrings.md) chapter for examples 213 | 214 | ``` 215 | $ ./triple_quoted_string.py 216 | The woods are lovely, dark and deep, 217 | But I have promises to keep, 218 | And miles to go before I sleep, 219 | And miles to go before I sleep. 220 | $ 221 | ``` 222 | 223 | **Further Reading** 224 | 225 | * [Python docs - strings](https://docs.python.org/3/tutorial/introduction.html#strings) 226 | * [Python docs - f-strings](https://docs.python.org/3/reference/lexical_analysis.html#f-strings) - for more examples and caveats 227 | * [Python docs - List of Escape Sequences and more info on strings](https://docs.python.org/3/reference/lexical_analysis.html#strings) 228 | * [Python docs - Binary Sequence Types](https://docs.python.org/3/library/stdtypes.html#binary-sequence-types-bytes-bytearray-memoryview) 229 | * [formatting triple quoted strings](https://stackoverflow.com/questions/3877623/in-python-can-you-have-variables-within-triple-quotes-if-so-how) 230 | 231 |
232 | 233 | ### Constants 234 | 235 | Paraphrased from [Python docs - constants](https://docs.python.org/3/library/constants.html) 236 | 237 | * `None` The sole value of the type `NoneType` 238 | * `None` is frequently used to represent the absence of a value 239 | * `False` The false value of the `bool` type 240 | * `True` The true value of the `bool` type 241 | * [Python docs - Truth Value Testing](https://docs.python.org/3/library/stdtypes.html#truth) 242 | 243 | ```python 244 | >>> bool(2) 245 | True 246 | >>> bool(0) 247 | False 248 | >>> bool('') 249 | False 250 | >>> bool('a') 251 | True 252 | ``` 253 | 254 |
255 | 256 | ### Built-in Operators 257 | 258 | * arithmetic operators 259 | * `+` addition 260 | * `-` subtraction 261 | * `*` multiplication 262 | * `/` division (float output) 263 | * `//` division (integer output, result is not rounded) 264 | * `**` exponentiation 265 | * `%` modulo 266 | * string operators 267 | * `+` string concatenation 268 | * `*` string repetition 269 | * comparison operators 270 | * `==` equal to 271 | * `>` greater than 272 | * `<` less than 273 | * `!=` not equal to 274 | * `>=` greater than or equal to 275 | * `<=` less than or equal to 276 | * boolean logic 277 | * `and` logical and 278 | * `or` logical or 279 | * `not` logical not 280 | * bitwise operators 281 | * `&` and 282 | * `|` or 283 | * `^` exclusive or 284 | * `~` invert bits 285 | * `>>` right shift 286 | * `<<` left shift 287 | * and many more... 288 | 289 | **Further Reading** 290 | 291 | * [Python docs - Numeric types](https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex) - complete list of operations and precedence 292 | * [Python docs - String methods](https://docs.python.org/3/library/stdtypes.html#string-methods) 293 | 294 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | 3 | --- 4 | 5 | :warning: :warning: I'm archiving this repo, as I don't intend to work on this repo further. 6 | 7 | I'm re-using materials in this repo for the **100 Page Python Intro** book (https://github.com/learnbyexample/100_page_python_intro). 8 | 9 | I'm also working on **Practice Python Projects** book (https://github.com/learnbyexample/practice_python_projects), which I had intended in this repo for the `mini_projects` folder. 10 | 11 | --- 12 | 13 |


14 | 15 | # Python Basics 16 | 17 | Introduction to Python - Syntax, working with Shell commands, Files, Text Processing, and more... 18 | 19 | * Suitable for a one/two day workshop for Python beginners 20 | * Visit [Python re(gex)?](https://github.com/learnbyexample/py_regular_expressions) repo for a book on regular expressions 21 | * [Python resources for everybody](https://learnbyexample.github.io/py_resources/) for a curated and searchable collection, including resources for complete beginners to programming 22 | * For more related resources, visit [scripting course](https://github.com/learnbyexample/scripting_course) and my programming blog https://learnbyexample.github.io 23 | 24 |
25 | 26 | # Chapters 27 | 28 | * [Introduction](./Introduction.md) 29 | * Installation, Hello World example, Python Interpreter, Python Standard Library 30 | * [Number and String data types](./Number_and_String_datatypes.md) 31 | * Numbers, String, Constants, Built-in Operators 32 | * [Functions](./Functions.md) 33 | * def, print function, range function, type function, Variable Scope 34 | * [Getting User input](./User_input.md) 35 | * Integer input, Floating point input, String input 36 | * [Executing external commands](./Executing_external_commands.md) 37 | * Calling Shell commands, Calling Shell commands with expansion, Getting command output and redirections 38 | * [Control Structures](./Control_structures.md) 39 | * Condition checking, if, for, while, continue and break 40 | * [Lists](./Lists.md) 41 | * Assigning List variables, Slicing and Modifying Lists, Copying Lists, List Methods and Miscellaneous, Looping, List Comprehension, Getting List as user input, Getting random items from list 42 | * [Sequence, Set and Dict data types](./Sequence_Set_Dict_data_types.md) 43 | * Strings, Tuples, Set, Dictionary 44 | * [Text Processing](./Text_Processing.md) 45 | * String methods, Regular Expressions, Pattern matching and extraction, Search and Replace, Compiling Regular Expressions, Further Reading on Regular Expressions 46 | * [File handling](./File_handling.md) 47 | * open function, Reading files, Writing to files, Inplace editing with fileinput 48 | * [Command line arguments](./Command_line_arguments.md) 49 | * Known number of arguments, Varying number of arguments, Using program name in code, Command line switches 50 | * [Exception Handling and Debugging](./Exception_Handling_and_Debugging.md) 51 | * Exception Handling, Syntax check, pdb, Importing program 52 | * [Docstrings](./Docstrings.md) 53 | * Style guide, Palindrome example 54 | * [Testing](./Testing.md) 55 | * assert statement, Using assert to test a program, Using unittest framework, Using unittest.mock to test user input and program output, Other testing frameworks 56 | * [Exercises](./Exercises.md) 57 | * [Further Reading](./Further_Reading.md) 58 | * Standard topics not covered, Useful links on coding, Python extensions 59 | 60 |
61 | 62 | ## Contributing 63 | 64 | * Please open an issue for typos/bugs/suggestions/etc 65 | * As this repo is no longer actively worked upon, **please do not submit pull requests** 66 | * Share the repo with friends/colleagues, on social media, etc to help reach other learners 67 | * In case you need to reach me, mail me at `echo 'bGVhcm5ieWV4YW1wbGUubmV0QGdtYWlsLmNvbQo=' | base64 --decode` or send a DM via [twitter](https://twitter.com/learn_byexample) 68 | 69 |
70 | 71 | # ebook 72 | 73 | * Read as ebook on [gitbook](https://learnbyexample.gitbooks.io/python-basics/content/index.html) 74 | * All `legacy.gitbook.com` links are now automatically redirected to `gitbook.com`, so there's no longer an option to download ebooks for offline reading 75 | 76 |
77 | 78 | # Acknowledgements 79 | 80 | * [automatetheboringstuff](https://automatetheboringstuff.com/) for getting me started with Python 81 | * [/r/learnpython/](https://www.reddit.com/r/learnpython/) - helpful forum for beginners and experienced programmers alike 82 | * [stackoverflow](https://stackoverflow.com/) - for getting answers to pertinent questions as well as sharpening skills by understanding and answering questions 83 | * [Devs and Hackers](http://devup.in/) - helpful slack group 84 | * [Weekly Coders, Hackers & All Tech related thread](https://www.reddit.com/r/india/search?q=Weekly+Coders%2C+Hackers+%26+All+Tech+related+thread+author%3Aavinassh&restrict_sr=on&sort=new&t=all) - for suggestions and critique 85 | 86 |
87 | 88 | # License 89 | 90 | This work is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/) 91 | 92 | -------------------------------------------------------------------------------- /Sequence_Set_Dict_data_types.md: -------------------------------------------------------------------------------- 1 | # Sequence, Set and Dict data types 2 | 3 | * [Strings](#strings) 4 | * [Tuples](#tuples) 5 | * [Set](#set) 6 | * [Dictionary](#dictionary) 7 | 8 | We have already seen Sequence types in previous chapters - strings, ranges and lists. Tuple is another sequence type 9 | We'll see some more operations on strings followed by Tuple, Set and Dict in this chapter 10 | 11 |
12 | 13 | ### Strings 14 | 15 | * The indexing we saw for lists can be applied to strings as well 16 | * As strings are immutable, they can't be modified like lists though 17 | 18 | ```python 19 | >>> book = "Alchemist" 20 | >>> book[0] 21 | 'A' 22 | >>> book[3] 23 | 'h' 24 | >>> book[-1] 25 | 't' 26 | >>> book[10] 27 | Traceback (most recent call last): 28 | File "", line 1, in 29 | IndexError: string index out of range 30 | 31 | >>> book[2:6] 32 | 'chem' 33 | >>> book[:5] 34 | 'Alche' 35 | >>> book[5:] 36 | 'mist' 37 | >>> book[::-1] 38 | 'tsimehclA' 39 | >>> book[:] 40 | 'Alchemist' 41 | 42 | >>> list(book) 43 | ['A', 'l', 'c', 'h', 'e', 'm', 'i', 's', 't'] 44 | 45 | >>> import string 46 | >>> string.ascii_lowercase[:10] 47 | 'abcdefghij' 48 | >>> list(string.digits) 49 | ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'] 50 | ``` 51 | 52 | * looping over strings 53 | 54 | ```python 55 | >>> book 56 | 'Alchemist' 57 | 58 | >>> for char in book: 59 | ... print(char) 60 | ... 61 | A 62 | l 63 | c 64 | h 65 | e 66 | m 67 | i 68 | s 69 | t 70 | ``` 71 | 72 | * Other operations 73 | 74 | ```python 75 | >>> book 76 | 'Alchemist' 77 | 78 | >>> len(book) 79 | 9 80 | 81 | >>> book.index('A') 82 | 0 83 | >>> book.index('t') 84 | 8 85 | 86 | >>> 'A' in book 87 | True 88 | >>> 'B' in book 89 | False 90 | >>> 'z' not in book 91 | True 92 | 93 | >>> min('zealous') 94 | 'a' 95 | >>> max('zealous') 96 | 'z' 97 | ``` 98 | 99 |
100 | 101 | ### Tuples 102 | 103 | * Tuples are similar to lists but immutable and useful in other ways too 104 | * Individual elements can be both mutable/immutable 105 | 106 | ```python 107 | >>> north_dishes = ('Aloo tikki', 'Baati', 'Khichdi', 'Makki roti', 'Poha') 108 | >>> north_dishes 109 | ('Aloo tikki', 'Baati', 'Khichdi', 'Makki roti', 'Poha') 110 | 111 | >>> north_dishes[0] 112 | 'Aloo tikki' 113 | >>> north_dishes[-1] 114 | 'Poha' 115 | >>> north_dishes[6] 116 | Traceback (most recent call last): 117 | File "", line 1, in 118 | IndexError: tuple index out of range 119 | 120 | >>> north_dishes[::-1] 121 | ('Poha', 'Makki roti', 'Khichdi', 'Baati', 'Aloo tikki') 122 | 123 | >>> north_dishes[0] = 'Poori' 124 | Traceback (most recent call last): 125 | File "", line 1, in 126 | TypeError: 'tuple' object does not support item assignment 127 | ``` 128 | 129 | * Example operations 130 | 131 | ```python 132 | >>> 'roti' in north_dishes 133 | False 134 | >>> 'Makki roti' in north_dishes 135 | True 136 | 137 | >>> len(north_dishes) 138 | 5 139 | 140 | >>> min(north_dishes) 141 | 'Aloo tikki' 142 | >>> max(north_dishes) 143 | 'Poha' 144 | 145 | >>> for dish in north_dishes: 146 | ... print(dish) 147 | ... 148 | Aloo tikki 149 | Baati 150 | Khichdi 151 | Makki roti 152 | Poha 153 | ``` 154 | 155 | * Tuple is handy for multiple variable assignment and returning more than one value in functions 156 | * We have already seen example when using `enumerate` for iterating over lists 157 | 158 | ```python 159 | >>> a = 5 160 | >>> b = 20 161 | >>> a, b = b, a 162 | >>> a 163 | 20 164 | >>> b 165 | 5 166 | 167 | >>> c = 'foo' 168 | >>> a, b, c = c, a, b 169 | >>> a 170 | 'foo' 171 | >>> b 172 | 20 173 | >>> c 174 | 5 175 | 176 | >>> def min_max(arr): 177 | ... return min(arr), max(arr) 178 | ... 179 | >>> min_max([23, 53, 1, -34, 9]) 180 | (-34, 53) 181 | ``` 182 | 183 | * using `()` is not always required 184 | 185 | ```python 186 | >>> words = 'day', 'night' 187 | >>> words 188 | ('day', 'night') 189 | 190 | >>> coordinates = ((1,2), (4,3), (92,3)) 191 | >>> coordinates 192 | ((1, 2), (4, 3), (92, 3)) 193 | 194 | >>> prime = [2, 3, 5, 7, 11] 195 | >>> prime_tuple = tuple((idx + 1, num) for idx, num in enumerate(prime)) 196 | >>> prime_tuple 197 | ((1, 2), (2, 3), (3, 5), (4, 7), (5, 11)) 198 | ``` 199 | 200 | * converting other types to tuples 201 | * similar to `list()` 202 | 203 | ``` 204 | >>> tuple('books') 205 | ('b', 'o', 'o', 'k', 's') 206 | 207 | >>> a = [321, 899.232, 5.3, 2, 1, -1] 208 | >>> tuple(a) 209 | (321, 899.232, 5.3, 2, 1, -1) 210 | ``` 211 | 212 | * data types can be mixed and matched in different ways 213 | 214 | ```python 215 | >>> a = [(1,2), ['a', 'b'], ('good', 'bad')] 216 | >>> a 217 | [(1, 2), ['a', 'b'], ('good', 'bad')] 218 | 219 | >>> b = ((1,2), ['a', 'b'], ('good', 'bad')) 220 | >>> b 221 | ((1, 2), ['a', 'b'], ('good', 'bad')) 222 | ``` 223 | 224 | * [Python docs - tuple](https://docs.python.org/3/library/stdtypes.html#tuple) 225 | * [Python docs - tuple tutorial](https://docs.python.org/3/tutorial/datastructures.html#tuples-and-sequences) 226 | 227 |
228 | 229 | ### Set 230 | 231 | * Set is unordered collection of objects 232 | * Mutable data type 233 | * Typically used to maintain unique sequence, perform Set operations like intersection, union, difference, symmetric difference, etc 234 | 235 | ```python 236 | >>> nums = {3, 2, 5, 7, 1, 6.3} 237 | >>> nums 238 | {1, 2, 3, 5, 6.3, 7} 239 | 240 | >>> primes = {3, 2, 11, 3, 5, 13, 2} 241 | >>> primes 242 | {2, 3, 11, 13, 5} 243 | 244 | >>> nums.union(primes) 245 | {1, 2, 3, 5, 6.3, 7, 11, 13} 246 | 247 | >>> primes.difference(nums) 248 | {11, 13} 249 | >>> nums.difference(primes) 250 | {1, 6.3, 7} 251 | ``` 252 | 253 | * Example operations 254 | 255 | ```python 256 | >>> len(nums) 257 | 6 258 | 259 | >>> nums[0] 260 | Traceback (most recent call last): 261 | File "", line 1, in 262 | TypeError: 'set' object does not support indexing 263 | 264 | >>> book 265 | 'Alchemist' 266 | >>> set(book) 267 | {'i', 'l', 's', 'A', 'e', 'h', 'm', 't', 'c'} 268 | >>> set([1, 5, 3, 1, 9]) 269 | {1, 9, 3, 5} 270 | >>> list(set([1, 5, 3, 1, 9])) 271 | [1, 9, 3, 5] 272 | 273 | >>> nums = {1, 2, 3, 5, 6.3, 7} 274 | >>> nums 275 | {1, 2, 3, 5, 6.3, 7} 276 | >>> nums.pop() 277 | 1 278 | >>> nums 279 | {2, 3, 5, 6.3, 7} 280 | 281 | >>> nums.add(1) 282 | >>> nums 283 | {1, 2, 3, 5, 6.3, 7} 284 | 285 | >>> 6.3 in nums 286 | True 287 | 288 | >>> for n in nums: 289 | ... print(n) 290 | ... 291 | 1 292 | 2 293 | 3 294 | 5 295 | 6.3 296 | 7 297 | ``` 298 | 299 | * [Python docs - set](https://docs.python.org/3/library/stdtypes.html#set) 300 | * [Python docs - frozenset](https://docs.python.org/3/library/stdtypes.html#frozenset) 301 | * [set tutorial](http://www.programiz.com/python-programming/set) 302 | 303 |
304 | 305 | ### Dictionary 306 | 307 | * `dict` types can be thought of as unordered list of `key:value` pairs or a named list of items 308 | * up to Python v3.5 (and some implementations of v3.6) do not retain order of insertion of dict elements 309 | 310 | ```python 311 | >>> marks = {'Rahul' : 86, 'Ravi' : 92, 'Rohit' : 75} 312 | >>> marks 313 | {'Ravi': 92, 'Rohit': 75, 'Rahul': 86} 314 | 315 | >>> fav_books = {} 316 | >>> fav_books['fantasy'] = 'Harry Potter' 317 | >>> fav_books['detective'] = 'Sherlock Holmes' 318 | >>> fav_books['thriller'] = 'The Da Vinci Code' 319 | >>> fav_books 320 | {'thriller': 'The Da Vinci Code', 'fantasy': 'Harry Potter', 'detective': 'Sherlock Holmes'} 321 | 322 | >>> marks.keys() 323 | dict_keys(['Ravi', 'Rohit', 'Rahul']) 324 | 325 | >>> fav_books.values() 326 | dict_values(['The Da Vinci Code', 'Harry Potter', 'Sherlock Holmes']) 327 | ``` 328 | 329 | * looping and printing 330 | 331 | ```python 332 | >>> for book in fav_books.values(): 333 | ... print(book) 334 | ... 335 | The Da Vinci Code 336 | Harry Potter 337 | Sherlock Holmes 338 | 339 | >>> for name, mark in marks.items(): 340 | ... print(name, mark, sep=': ') 341 | ... 342 | Ravi: 92 343 | Rohit: 75 344 | Rahul: 86 345 | 346 | >>> import pprint 347 | >>> pp = pprint.PrettyPrinter(indent=4) 348 | >>> pp.pprint(fav_books) 349 | { 'detective': 'Sherlock Holmes', 350 | 'fantasy': 'Harry Potter', 351 | 'thriller': 'The Da Vinci Code'} 352 | ``` 353 | 354 | * modifying dicts and example operations 355 | 356 | ```python 357 | >>> marks 358 | {'Ravi': 92, 'Rohit': 75, 'Rahul': 86} 359 | >>> marks['Rajan'] = 79 360 | >>> marks 361 | {'Ravi': 92, 'Rohit': 75, 'Rahul': 86, 'Rajan': 79} 362 | 363 | >>> del marks['Ravi'] 364 | >>> marks 365 | {'Rohit': 75, 'Rahul': 86, 'Rajan': 79} 366 | 367 | >>> len(marks) 368 | 3 369 | 370 | >>> fav_books 371 | {'thriller': 'The Da Vinci Code', 'fantasy': 'Harry Potter', 'detective': 'Sherlock Holmes'} 372 | >>> "fantasy" in fav_books 373 | True 374 | >>> "satire" in fav_books 375 | False 376 | ``` 377 | 378 | * dict made up of lists and using random module 379 | * any change in the individual lists will also reflect in dict 380 | * output of `keys()` method has to be changed to Sequence types like `list` or `tuple` to pass on to `random.choice` 381 | 382 | ```python 383 | >>> north = ['aloo tikki', 'baati', 'khichdi', 'makki roti', 'poha'] 384 | >>> south = ['appam', 'bisibele bath', 'dosa', 'koottu', 'sevai'] 385 | >>> west = ['dhokla', 'khakhra', 'modak', 'shiro', 'vada pav'] 386 | >>> east = ['hando guri', 'litti', 'momo', 'rosgulla', 'shondesh'] 387 | >>> dishes = {'North': north, 'South': south, 'West': west, 'East': east} 388 | 389 | >>> rand_zone = random.choice(tuple(dishes.keys())) 390 | >>> rand_dish = random.choice(dishes[rand_zone]) 391 | >>> print("Try the '{}' speciality '{}' today".format(rand_zone, rand_dish)) 392 | Try the 'East' speciality 'rosgulla' today 393 | ``` 394 | 395 | * From Python v3.7 onwards, dict implementation will retain insertion order 396 | * some implementations like the reference CPython implementation for v3.6 also retains the insertion order 397 | 398 | ```python 399 | >>> marks = {'Rahul' : 86, 'Ravi' : 92, 'Rohit' : 75, 'Rajan': 79} 400 | >>> marks 401 | {'Rahul': 86, 'Ravi': 92, 'Rohit': 75, 'Rajan': 79} 402 | 403 | >>> for name, mark in marks.items(): 404 | ... print(f'{name:5s}: {mark}') 405 | ... 406 | Rahul: 86 407 | Ravi : 92 408 | Rohit: 75 409 | Rajan: 79 410 | 411 | >>> del marks['Ravi'] 412 | >>> marks 413 | {'Rahul': 86, 'Rohit': 75, 'Rajan': 79} 414 | 415 | >>> marks['Ranjit'] = 65 416 | >>> marks 417 | {'Rahul': 86, 'Rohit': 75, 'Rajan': 79, 'Ranjit': 65} 418 | ``` 419 | 420 | **Further Reading** 421 | 422 | * [Python docs - dict](https://docs.python.org/3/library/stdtypes.html#dict) 423 | * [Python docs - pprint](https://docs.python.org/3/library/pprint.html) 424 | * [detailed tutorial on dict](http://www.sharats.me/posts/the-python-dictionary/) 425 | * [Using dict to eliminate duplicates while retaining order](https://twitter.com/raymondh/status/944125570534621185) 426 | 427 | -------------------------------------------------------------------------------- /Testing.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | * [assert statement](#assert-statement) 4 | * [Using assert to test a program](#using-assert-to-test-a-program) 5 | * [Using unittest framework](#using-unittest-framework) 6 | * [Using unittest.mock to test user input and program output](#using-unittest.mock-to-test-user-input-and-program-output) 7 | * [Other testing frameworks](#other-testing-frameworks) 8 | 9 | 10 |
11 | 12 | ### assert statement 13 | 14 | * `assert` is primarily used for debugging purposes like catching invalid input or a condition that shouldn't occur 15 | * An optional message can be passed for descriptive error message than a plain **AssertionError** 16 | * It uses [raise statement](https://docs.python.org/3/tutorial/errors.html#raising-exceptions) for implementation 17 | * `assert` statements can be skipped by passing the `-O` [command line option](https://docs.python.org/3/using/cmdline.html) 18 | * **Note** that `assert` is a statement and not a function 19 | 20 | ```python 21 | >>> assert 2 ** 3 == 8 22 | >>> assert 3 > 4 23 | Traceback (most recent call last): 24 | File "", line 1, in 25 | AssertionError 26 | 27 | >>> assert 3 > 4, "3 is not greater than 4" 28 | Traceback (most recent call last): 29 | File "", line 1, in 30 | AssertionError: 3 is not greater than 4 31 | ``` 32 | 33 | Let's take a factorial function as an example: 34 | 35 | ```python 36 | >>> def fact(n): 37 | total = 1 38 | for num in range(1, n+1): 39 | total *= num 40 | return total 41 | 42 | >>> assert fact(4) == 24 43 | >>> assert fact(0) == 1 44 | >>> fact(5) 45 | 120 46 | 47 | >>> fact(-3) 48 | 1 49 | >>> fact(2.3) 50 | Traceback (most recent call last): 51 | File "", line 1, in 52 | File "", line 3, in fact 53 | TypeError: 'float' object cannot be interpreted as an integer 54 | ``` 55 | 56 | * `assert fact(4) == 24` and `assert fact(0) == 1` can be considered as sample tests to check the function 57 | 58 | Let's see how `assert` can be used to validate arguments passed to the function: 59 | 60 | ```python 61 | >>> def fact(n): 62 | assert type(n) == int and n >= 0, "Number should be zero or positive integer" 63 | total = 1 64 | for num in range(1, n+1): 65 | total *= num 66 | return total 67 | 68 | >>> fact(5) 69 | 120 70 | >>> fact(-3) 71 | Traceback (most recent call last): 72 | File "", line 1, in 73 | File "", line 2, in fact 74 | AssertionError: Number should be zero or positive integer 75 | >>> fact(2.3) 76 | Traceback (most recent call last): 77 | File "", line 1, in 78 | File "", line 2, in fact 79 | AssertionError: Number should be zero or positive integer 80 | ``` 81 | 82 |
83 | 84 | The above factorial function can also be written using [reduce](https://docs.python.org/3/library/functools.html#functools.reduce) 85 | 86 | ```python 87 | >>> def fact(n): 88 | assert type(n) == int and n >= 0, "Number should be zero or positive integer" 89 | from functools import reduce 90 | from operator import mul 91 | return reduce(mul, range(1, n+1), 1) 92 | 93 | 94 | >>> fact(23) 95 | 25852016738884976640000 96 | ``` 97 | 98 | Above examples for demonstration only, for practical purposes use `math.factorial` which also gives appropriate exceptions 99 | 100 | ```python 101 | >>> from math import factorial 102 | >>> factorial(10) 103 | 3628800 104 | 105 | >>> factorial(-5) 106 | Traceback (most recent call last): 107 | File "", line 1, in 108 | ValueError: factorial() not defined for negative values 109 | >>> factorial(3.14) 110 | Traceback (most recent call last): 111 | File "", line 1, in 112 | ValueError: factorial() only accepts integral values 113 | ``` 114 | 115 | **Further Reading** 116 | 117 | * [Python docs - assert](https://docs.python.org/3/reference/simple_stmts.html#assert) 118 | * [What is the use of assert in Python?](https://stackoverflow.com/questions/5142418/what-is-the-use-of-assert-in-python) 119 | * [Is Unit Testing worth the effort?](https://stackoverflow.com/questions/67299/is-unit-testing-worth-the-effort) 120 | 121 |
122 | 123 | ### Using assert to test a program 124 | 125 | In a limited fashion, one can use `assert` to test a program - either within the program (and later skipped using the `-O` option) or as separate test program(s) 126 | 127 | Let's try testing the **palindrome** program we saw in [Docstrings](./Docstrings.md#palindrome-example) chapter 128 | 129 | ```python 130 | #!/usr/bin/python3 131 | 132 | import palindrome 133 | 134 | assert palindrome.is_palindrome('Madam') 135 | assert palindrome.is_palindrome("Dammit, I'm mad!") 136 | assert not palindrome.is_palindrome('aaa') 137 | assert palindrome.is_palindrome('Malayalam') 138 | 139 | try: 140 | assert palindrome.is_palindrome('as2') 141 | except ValueError as e: 142 | assert str(e) == 'Characters other than alphabets and punctuations' 143 | 144 | try: 145 | assert palindrome.is_palindrome("a'a") 146 | except ValueError as e: 147 | assert str(e) == 'Less than 3 alphabets' 148 | 149 | print('All tests passed') 150 | ``` 151 | 152 | * There are four different cases tested for **is_palindrome** function 153 | * Valid palindrome string 154 | * Invalid palindrome string 155 | * Invalid characters in string 156 | * Insufficient characters in string 157 | * Both the program being tested and program to test are in same directory 158 | * To test the **main** function, we need to simulate user input. For this and other useful features, test frameworks come in handy 159 | 160 | ``` 161 | $ ./test_palindrome.py 162 | All tests passed 163 | ``` 164 | 165 |
166 | 167 | ### Using unittest framework 168 | 169 | This section requires understanding of [classes](https://docs.python.org/3/tutorial/classes.html) 170 | 171 |
172 | 173 | ```python 174 | #!/usr/bin/python3 175 | 176 | import palindrome 177 | import unittest 178 | 179 | class TestPalindrome(unittest.TestCase): 180 | 181 | def test_valid(self): 182 | # check valid input strings 183 | self.assertTrue(palindrome.is_palindrome('kek')) 184 | self.assertTrue(palindrome.is_palindrome("Dammit, I'm mad!")) 185 | self.assertFalse(palindrome.is_palindrome('zzz')) 186 | self.assertFalse(palindrome.is_palindrome('cool')) 187 | 188 | def test_error(self): 189 | # check only the exception raised 190 | with self.assertRaises(ValueError): 191 | palindrome.is_palindrome('abc123') 192 | 193 | with self.assertRaises(TypeError): 194 | palindrome.is_palindrome(7) 195 | 196 | # check error message as well 197 | with self.assertRaises(ValueError) as cm: 198 | palindrome.is_palindrome('on 2 no') 199 | em = str(cm.exception) 200 | self.assertEqual(em, 'Characters other than alphabets and punctuations') 201 | 202 | with self.assertRaises(ValueError) as cm: 203 | palindrome.is_palindrome('to') 204 | em = str(cm.exception) 205 | self.assertEqual(em, 'Less than 3 alphabets') 206 | 207 | if __name__ == '__main__': 208 | unittest.main() 209 | ``` 210 | 211 | * First we create a subclass of **unittest.TestCase** (inheritance) 212 | * Then, different type of checks can be grouped in separate functions - function names starting with **test** are automatically called by **unittest.main()** 213 | * Depending upon type of test performed, **assertTrue, assertFalse, assertRaises, assertEqual, etc** can be used 214 | * [An Introduction to Classes and Inheritance](http://www.jesshamrick.com/2011/05/18/an-introduction-to-classes-and-inheritance-in-python/) 215 | * [Python docs - unittest](https://docs.python.org/3/library/unittest.html) 216 | * [Command-Line Interface](https://docs.python.org/3/library/unittest.html#command-line-interface) 217 | * [Test Discovery](https://docs.python.org/3/library/unittest.html#test-discovery) 218 | * [Organizing test code, setUp and tearDown](https://docs.python.org/3/library/unittest.html#organizing-test-code) 219 | 220 | ``` 221 | $ ./unittest_palindrome.py 222 | .. 223 | ---------------------------------------------------------------------- 224 | Ran 2 tests in 0.001s 225 | 226 | OK 227 | 228 | $ ./unittest_palindrome.py -v 229 | test_error (__main__.TestPalindrome) ... ok 230 | test_valid (__main__.TestPalindrome) ... ok 231 | 232 | ---------------------------------------------------------------------- 233 | Ran 2 tests in 0.001s 234 | 235 | OK 236 | ``` 237 | 238 |
239 | 240 | ### Using unittest.mock to test user input and program output 241 | 242 | This section requires understanding of decorators, [do check out this wonderful intro](https://stackoverflow.com/questions/739654/how-to-make-a-chain-of-function-decorators-in-python/1594484#1594484) 243 | 244 |
245 | 246 | A simple example to see how to capture `print` output for testing 247 | 248 | ```python 249 | >>> from unittest import mock 250 | >>> from io import StringIO 251 | 252 | >>> def greeting(): 253 | print('Hi there!') 254 | 255 | >>> def test(): 256 | with mock.patch('sys.stdout', new_callable=StringIO) as mock_stdout: 257 | greeting() 258 | assert mock_stdout.getvalue() == 'Hi there!\n' 259 | 260 | >>> test() 261 | ``` 262 | 263 | One can also use `decorators` 264 | 265 | ```python 266 | >>> @mock.patch('sys.stdout', new_callable=StringIO) 267 | def test(mock_stdout): 268 | greeting() 269 | assert mock_stdout.getvalue() == 'Hi there!\n' 270 | ``` 271 | 272 |
273 | 274 | Now let's see how to emulate `input` 275 | 276 | ```python 277 | >>> def greeting(): 278 | name = input('Enter your name: ') 279 | print('Hello', name) 280 | 281 | >>> greeting() 282 | Enter your name: learnbyexample 283 | Hello learnbyexample 284 | 285 | >>> with mock.patch('builtins.input', return_value='Tom'): 286 | greeting() 287 | 288 | Hello Tom 289 | ``` 290 | 291 |
292 | 293 | Combining both 294 | 295 | ```python 296 | >>> @mock.patch('sys.stdout', new_callable=StringIO) 297 | def test_greeting(name, mock_stdout): 298 | with mock.patch('builtins.input', return_value=name): 299 | greeting() 300 | assert mock_stdout.getvalue() == 'Hello ' + name + '\n' 301 | 302 | >>> test_greeting('Jo') 303 | ``` 304 | 305 |
306 | 307 | Having seen basic input/output testing, let's apply it to main function of **palindrome** 308 | 309 | ```python 310 | #!/usr/bin/python3 311 | 312 | import palindrome 313 | import unittest 314 | from unittest import mock 315 | from io import StringIO 316 | 317 | class TestPalindrome(unittest.TestCase): 318 | 319 | @mock.patch('sys.stdout', new_callable=StringIO) 320 | def main_op(self, tst_str, mock_stdout): 321 | with mock.patch('builtins.input', side_effect=tst_str): 322 | palindrome.main() 323 | return mock_stdout.getvalue() 324 | 325 | def test_valid(self): 326 | for s in ('Malayalam', 'kek'): 327 | self.assertEqual(self.main_op([s]), s + ' is a palindrome\n') 328 | 329 | for s in ('zzz', 'cool'): 330 | self.assertEqual(self.main_op([s]), s + ' is NOT a palindrome\n') 331 | 332 | def test_error(self): 333 | em1 = 'Error: Characters other than alphabets and punctuations\n' 334 | em2 = 'Error: Less than 3 alphabets\n' 335 | 336 | tst1 = em1 + 'Madam is a palindrome\n' 337 | self.assertEqual(self.main_op(['123', 'Madam']), tst1) 338 | 339 | tst2 = em2 + em1 + 'Jerry is NOT a palindrome\n' 340 | self.assertEqual(self.main_op(['to', 'a2a', 'Jerry']), tst2) 341 | 342 | if __name__ == '__main__': 343 | unittest.main() 344 | ``` 345 | 346 | * Two test functions - one for testing valid input strings and another to check error messages 347 | * Here, **side_effect** which accepts iterable like list, compared to **return_value** where only one input value can be mocked 348 | * For valid input strings, the **palindrome** main function would need only one input value 349 | * For error conditions, the iterable comes handy as the main function is programmed to run infinitely until valid input is given 350 | * [Python docs - unittest.mock](https://docs.python.org/3/library/unittest.mock.html) 351 | * [patchers](https://docs.python.org/3/library/unittest.mock.html#the-patchers) 352 | * [An Introduction to Mocking in Python](https://www.toptal.com/python/an-introduction-to-mocking-in-python) 353 | * [PEP 0318 - decorators](https://www.python.org/dev/peps/pep-0318/) 354 | * [decorators](https://pythonconquerstheuniverse.wordpress.com/2012/04/29/python-decorators/) 355 | 356 | ``` 357 | $ ./unittest_palindrome_main.py 358 | .. 359 | ---------------------------------------------------------------------- 360 | Ran 2 tests in 0.003s 361 | 362 | OK 363 | ``` 364 | 365 |
366 | 367 | ### Other testing frameworks 368 | 369 | * [pytest](http://doc.pytest.org/en/latest/getting-started.html) 370 | * [Python docs - doctest](https://docs.python.org/3/library/doctest.html) 371 | * [Python test automation](https://github.com/atinfo/awesome-test-automation/blob/master/python-test-automation.md) 372 | * [Python Testing Tools Taxonomy](https://wiki.python.org/moin/PythonTestingToolsTaxonomy) 373 | * [Python test frameworks](http://docs.python-guide.org/en/latest/writing/tests/) 374 | 375 | Test driven development (TDD) 376 | 377 | * [Test-Driven Development with Python](http://chimera.labs.oreilly.com/books/1234000000754/index.html) 378 | * [learn Python via TDD](https://github.com/gregmalcolm/python_koans) 379 | -------------------------------------------------------------------------------- /Text_Processing.md: -------------------------------------------------------------------------------- 1 | # Text Processing 2 | 3 | * [String methods](#string-methods) 4 | * [Regular Expressions](#regular-expressions) 5 | * [Pattern matching and extraction](#pattern-matching-and-extraction) 6 | * [Search and Replace](#search-and-replace) 7 | * [Compiling Regular Expressions](#compiling-regular-expressions) 8 | * [Further Reading on Regular Expressions](#further-reading-on-regular-expressions) 9 | 10 |
11 | 12 | ### String methods 13 | 14 | * translate string characters 15 | * `str.maketrans()` to get translation table 16 | * `translate()` to perform the string mapping based on translation table 17 | * the first argument to `maketrans()` is string characters to be replaced, the second is characters to replace with and the third is characters to be mapped to `None` 18 | * [character translation examples](https://stackoverflow.com/questions/555705/character-translation-using-python-like-the-tr-command) 19 | 20 | ```python 21 | >>> greeting = '===== Have a great day =====' 22 | >>> greeting.translate(str.maketrans('=', '-')) 23 | '----- Have a great day -----' 24 | 25 | >>> greeting = '===== Have a great day!! =====' 26 | >>> greeting.translate(str.maketrans('=', '-', '!')) 27 | '----- Have a great day -----' 28 | 29 | >>> import string 30 | >>> quote = 'SIMPLICITY IS THE ULTIMATE SOPHISTICATION' 31 | >>> tr_table = str.maketrans(string.ascii_uppercase, string.ascii_lowercase) 32 | >>> quote.translate(tr_table) 33 | 'simplicity is the ultimate sophistication' 34 | 35 | >>> sentence = "Thi1s is34 a senten6ce" 36 | >>> sentence.translate(str.maketrans('', '', string.digits)) 37 | 'This is a sentence' 38 | >>> greeting.translate(str.maketrans('', '', string.punctuation)) 39 | ' Have a great day ' 40 | ``` 41 | 42 | * removing leading/trailing/both characters 43 | * only consecutive characters from start/end string are removed 44 | * by default whitespace characters are stripped 45 | * if more than one character is specified, it is treated as a set and all combinations of it are used 46 | 47 | ```python 48 | >>> greeting = ' Have a nice day :) ' 49 | >>> greeting.strip() 50 | 'Have a nice day :)' 51 | >>> greeting.rstrip() 52 | ' Have a nice day :)' 53 | >>> greeting.lstrip() 54 | 'Have a nice day :) ' 55 | 56 | >>> greeting.strip(') :') 57 | 'Have a nice day' 58 | 59 | >>> greeting = '===== Have a great day!! =====' 60 | >>> greeting.strip('=') 61 | ' Have a great day!! ' 62 | ``` 63 | 64 | * styling 65 | * width argument specifies total output string length 66 | 67 | ```python 68 | >>> ' Hello World '.center(40, '*') 69 | '************* Hello World **************' 70 | ``` 71 | 72 | * changing case and case checking 73 | 74 | ```python 75 | >>> sentence = 'thIs iS a saMple StrIng' 76 | 77 | >>> sentence.capitalize() 78 | 'This is a sample string' 79 | 80 | >>> sentence.title() 81 | 'This Is A Sample String' 82 | 83 | >>> sentence.lower() 84 | 'this is a sample string' 85 | 86 | >>> sentence.upper() 87 | 'THIS IS A SAMPLE STRING' 88 | 89 | >>> sentence.swapcase() 90 | 'THiS Is A SAmPLE sTRiNG' 91 | 92 | >>> 'good'.islower() 93 | True 94 | 95 | >>> 'good'.isupper() 96 | False 97 | ``` 98 | 99 | * check if string is made up of numbers 100 | 101 | ```python 102 | >>> '1'.isnumeric() 103 | True 104 | >>> 'abc1'.isnumeric() 105 | False 106 | >>> '1.2'.isnumeric() 107 | False 108 | ``` 109 | 110 | * check if character sequence is present or not 111 | 112 | ```python 113 | >>> sentence = 'This is a sample string' 114 | >>> 'is' in sentence 115 | True 116 | >>> 'this' in sentence 117 | False 118 | >>> 'This' in sentence 119 | True 120 | >>> 'this' in sentence.lower() 121 | True 122 | >>> 'is a' in sentence 123 | True 124 | >>> 'test' not in sentence 125 | True 126 | ``` 127 | 128 | * get number of times character sequence is present (non-overlapping) 129 | 130 | ```python 131 | >>> sentence = 'This is a sample string' 132 | >>> sentence.count('is') 133 | 2 134 | >>> sentence.count('w') 135 | 0 136 | 137 | >>> word = 'phototonic' 138 | >>> word.count('oto') 139 | 1 140 | ``` 141 | 142 | * matching character sequence at start/end of string 143 | 144 | ```python 145 | >>> sentence 146 | 'This is a sample string' 147 | 148 | >>> sentence.startswith('This') 149 | True 150 | >>> sentence.startswith('The') 151 | False 152 | 153 | >>> sentence.endswith('ing') 154 | True 155 | >>> sentence.endswith('ly') 156 | False 157 | ``` 158 | 159 | * split string based on character sequence 160 | * returns a list 161 | * to split using regular expressions, use `re.split()` instead 162 | 163 | ```python 164 | >>> sentence = 'This is a sample string' 165 | 166 | >>> sentence.split() 167 | ['This', 'is', 'a', 'sample', 'string'] 168 | 169 | >>> "oranges:5".split(':') 170 | ['oranges', '5'] 171 | >>> "oranges :: 5".split(' :: ') 172 | ['oranges', '5'] 173 | 174 | >>> "a e i o u".split(' ', maxsplit=1) 175 | ['a', 'e i o u'] 176 | >>> "a e i o u".split(' ', maxsplit=2) 177 | ['a', 'e', 'i o u'] 178 | 179 | >>> line = '{1.0 2.0 3.0}' 180 | >>> nums = [float(s) for s in line.strip('{}').split()] 181 | >>> nums 182 | [1.0, 2.0, 3.0] 183 | ``` 184 | 185 | * joining list of strings 186 | 187 | ```python 188 | >>> str_list 189 | ['This', 'is', 'a', 'sample', 'string'] 190 | >>> ' '.join(str_list) 191 | 'This is a sample string' 192 | >>> '-'.join(str_list) 193 | 'This-is-a-sample-string' 194 | 195 | >>> c = ' :: ' 196 | >>> c.join(str_list) 197 | 'This :: is :: a :: sample :: string' 198 | ``` 199 | 200 | * replace characters 201 | * third argument specifies how many times replace has to be performed 202 | * variable has to be explicitly re-assigned to change its value 203 | 204 | ```python 205 | >>> phrase = '2 be or not 2 be' 206 | >>> phrase.replace('2', 'to') 207 | 'to be or not to be' 208 | 209 | >>> phrase 210 | '2 be or not 2 be' 211 | 212 | >>> phrase.replace('2', 'to', 1) 213 | 'to be or not 2 be' 214 | 215 | >>> phrase = phrase.replace('2', 'to') 216 | >>> phrase 217 | 'to be or not to be' 218 | ``` 219 | 220 | **Further Reading** 221 | 222 | * [Python docs - string methods](https://docs.python.org/3/library/stdtypes.html#string-methods) 223 | * [python string methods tutorial](http://www.thehelloworldprogram.com/python/python-string-methods/) 224 | 225 |
226 | 227 | ### Regular Expressions 228 | 229 | * Handy reference of regular expression (RE) elements 230 | 231 | | Meta characters | Description | 232 | | ------------- | ----------- | 233 | | `\A` | anchor to restrict matching to beginning of string | 234 | | `\Z` | anchor to restrict matching to end of string | 235 | | `^` | anchor to restrict matching to beginning of line | 236 | | `$` | anchor to restrict matching to end of line | 237 | | `.` | Match any character except newline character `\n` | 238 | | | | OR operator for matching multiple patterns | 239 | | `(RE)` | capturing group | 240 | | `(?:RE)` | non-capturing group | 241 | | `[]` | Character class - match one character among many | 242 | | `\^` | prefix `\` to literally match meta characters like `^` | 243 | 244 |
245 | 246 | | Greedy Quantifiers | Description | 247 | | ------------- | ----------- | 248 | | `*` | Match zero or more times | 249 | | `+` | Match one or more times | 250 | | `?` | Match zero or one times | 251 | | `{m,n}` | Match `m` to `n` times (inclusive) | 252 | | `{m,}` | Match at least m times | 253 | | `{,n}` | Match up to `n` times (including `0` times) | 254 | | `{n}` | Match exactly n times | 255 | 256 | Appending a `?` to greedy quantifiers makes them non-greedy 257 | 258 |
259 | 260 | | Character classes | Description | 261 | | ------------- | ----------- | 262 | | `[aeiou]` | Match any vowel | 263 | | `[^aeiou]` | `^` inverts selection, so this matches any consonant | 264 | | `[a-f]` | `-` defines a range, so this matches any of abcdef characters | 265 | | `\d` | Match a digit, same as `[0-9]` | 266 | | `\D` | Match non-digit, same as `[^0-9]` or `[^\d]` | 267 | | `\w` | Match alphanumeric and underscore character, same as `[a-zA-Z0-9_]` | 268 | | `\W` | Match non-alphanumeric and underscore character, same as `[^a-zA-Z0-9_]` or `[^\w]` | 269 | | `\s` | Match white-space character, same as `[\ \t\n\r\f\v]` | 270 | | `\S` | Match non white-space character, same as `[^\s]` | 271 | | `\b` | word boundary, see `\w` for characters constituting a word | 272 | | `\B` | not a word boundary | 273 | 274 |
275 | 276 | | Flags | Description | 277 | | ------------- | ----------- | 278 | | `re.I` | Ignore case | 279 | | `re.M` | Multiline mode, `^` and `$` anchors work on lines | 280 | | `re.S` | Singleline mode, `.` will also match `\n` | 281 | | `re.X` | Verbose mode, for better readability and adding comments | 282 | 283 | See [Python docs - Compilation Flags](https://docs.python.org/3/howto/regex.html#compilation-flags) for more details and long names for flags 284 | 285 |
286 | 287 | | Variable | Description | 288 | | ------------- | ----------- | 289 | | `\1`, `\2`, `\3` ... `\99` | backreferencing matched patterns | 290 | | `\g<1>`, `\g<2>`, `\g<3>` ... | backreferencing matched patterns, prevents ambiguity | 291 | | `\g<0>` | entire matched portion | 292 | 293 | `\0` and `\100` onwards are considered as octal values, hence cannot be used as backreference. 294 | 295 |
296 | 297 | ### Pattern matching and extraction 298 | 299 | To match/extract sequence of characters, use 300 | 301 | * `re.search()` to see if input string contains a pattern or not 302 | * `re.findall()` to get a list of all matching portions 303 | * `re.finditer()` to get an iterator of `re.Match` objects of all matching portions 304 | * `re.split()` to get a list from splitting input string based on a pattern 305 | 306 | Their syntax is as follows: 307 | 308 | ```python 309 | re.search(pattern, string, flags=0) 310 | re.findall(pattern, string, flags=0) 311 | re.finditer(pattern, string, flags=0) 312 | re.split(pattern, string, maxsplit=0, flags=0) 313 | ``` 314 | 315 | * As a good practice, always use **raw strings** to construct RE, unless other formats are required 316 | * this will avoid clash of backslash escaping between RE and normal quoted strings 317 | * examples for `re.search` 318 | 319 | ```python 320 | >>> sentence = 'This is a sample string' 321 | 322 | # using normal string methods 323 | >>> 'is' in sentence 324 | True 325 | >>> 'xyz' in sentence 326 | False 327 | 328 | # need to load the re module before use 329 | >>> import re 330 | # check if 'sentence' contains the pattern described by RE argument 331 | >>> bool(re.search(r'is', sentence)) 332 | True 333 | >>> bool(re.search(r'this', sentence, flags=re.I)) 334 | True 335 | >>> bool(re.search(r'xyz', sentence)) 336 | False 337 | ``` 338 | 339 | * examples for `re.findall` 340 | 341 | ```python 342 | # match whole word par with optional s at start and e at end 343 | >>> re.findall(r'\bs?pare?\b', 'par spar apparent spare part pare') 344 | ['par', 'spar', 'spare', 'pare'] 345 | 346 | # numbers >= 100 with optional leading zeros 347 | >>> re.findall(r'\b0*[1-9]\d{2,}\b', '0501 035 154 12 26 98234') 348 | ['0501', '154', '98234'] 349 | 350 | # if multiple capturing groups are used, each element of output 351 | # will be a tuple of strings of all the capture groups 352 | >>> re.findall(r'(x*):(y*)', 'xx:yyy x: x:yy :y') 353 | [('xx', 'yyy'), ('x', ''), ('x', 'yy'), ('', 'y')] 354 | 355 | # normal capture group will hinder ability to get whole match 356 | # non-capturing group to the rescue 357 | >>> re.findall(r'\b\w*(?:st|in)\b', 'cost akin more east run against') 358 | ['cost', 'akin', 'east', 'against'] 359 | 360 | # useful for debugging purposes as well before applying substitution 361 | >>> re.findall(r't.*?a', 'that is quite a fabricated tale') 362 | ['tha', 't is quite a', 'ted ta'] 363 | ``` 364 | 365 | * examples for `re.split` 366 | 367 | ```python 368 | # split based on one or more digit characters 369 | >>> re.split(r'\d+', 'Sample123string42with777numbers') 370 | ['Sample', 'string', 'with', 'numbers'] 371 | 372 | # split based on digit or whitespace characters 373 | >>> re.split(r'[\d\s]+', '**1\f2\n3star\t7 77\r**') 374 | ['**', 'star', '**'] 375 | 376 | # to include the matching delimiter strings as well in the output 377 | >>> re.split(r'(\d+)', 'Sample123string42with777numbers') 378 | ['Sample', '123', 'string', '42', 'with', '777', 'numbers'] 379 | 380 | # use non-capturing group if capturing is not needed 381 | >>> re.split(r'hand(?:y|ful)', '123handed42handy777handful500') 382 | ['123handed42', '777', '500'] 383 | ``` 384 | 385 | * backreferencing 386 | 387 | ```python 388 | # whole words that have at least one consecutive repeated character 389 | >>> words = ['effort', 'flee', 'facade', 'oddball', 'rat', 'tool'] 390 | 391 | >>> [w for w in words if re.search(r'\b\w*(\w)\1\w*\b', w)] 392 | ['effort', 'flee', 'oddball', 'tool'] 393 | ``` 394 | 395 | * The `re.search` function returns a `re.Match` object from which various details can be extracted 396 | like the matched portion of string, location of matched portion, etc 397 | * **Note** that output here is shown for Python version **3.7** 398 | 399 | ```python 400 | >>> re.search(r'b.*d', 'abc ac adc abbbc') 401 | 402 | # retrieving entire matched portion 403 | >>> re.search(r'b.*d', 'abc ac adc abbbc')[0] 404 | 'bc ac ad' 405 | 406 | # capture group example 407 | >>> m = re.search(r'a(.*)d(.*a)', 'abc ac adc abbbc') 408 | # to get matched portion of second capture group 409 | >>> m[2] 410 | 'c a' 411 | # to get a tuple of all the capture groups 412 | >>> m.groups() 413 | ('bc ac a', 'c a') 414 | ``` 415 | 416 | * examples for `re.finditer` 417 | 418 | ```python 419 | >>> m_iter = re.finditer(r'(x*):(y*)', 'xx:yyy x: x:yy :y') 420 | >>> [(m[1], m[2]) for m in m_iter] 421 | [('xx', 'yyy'), ('x', ''), ('x', 'yy'), ('', 'y')] 422 | 423 | >>> m_iter = re.finditer(r'ab+c', 'abc ac adc abbbc') 424 | >>> for m in m_iter: 425 | ... print(m.span()) 426 | ... 427 | (0, 3) 428 | (11, 16) 429 | ``` 430 | 431 |
432 | 433 | ### Search and Replace 434 | 435 | **Syntax** 436 | 437 | ```python 438 | re.sub(pattern, repl, string, count=0, flags=0) 439 | ``` 440 | 441 | * examples 442 | * **Note** that as strings are immutable, `re.sub` will not change value of variable 443 | passed to it, has to be explicity assigned 444 | 445 | ```python 446 | >>> ip_lines = "catapults\nconcatenate\ncat" 447 | >>> print(re.sub(r'^', r'* ', ip_lines, flags=re.M)) 448 | * catapults 449 | * concatenate 450 | * cat 451 | 452 | # replace 'par' only at start of word 453 | >>> re.sub(r'\bpar', r'X', 'par spar apparent spare part') 454 | 'X spar apparent spare Xt' 455 | 456 | # same as: r'part|parrot|parent' 457 | >>> re.sub(r'par(en|ro)?t', r'X', 'par part parrot parent') 458 | 'par X X X' 459 | 460 | # remove first two columns where : is delimiter 461 | >>> re.sub(r'\A([^:]+:){2}', r'', 'foo:123:bar:baz', count=1) 462 | 'bar:baz' 463 | ``` 464 | 465 | * backreferencing 466 | 467 | ```python 468 | # remove any number of consecutive duplicate words separated by space 469 | # quantifiers can be applied to backreferences too! 470 | >>> re.sub(r'\b(\w+)( \1)+\b', r'\1', 'aa a a a 42 f_1 f_1 f_13.14') 471 | 'aa a 42 f_1 f_13.14' 472 | 473 | # add something around the matched strings 474 | >>> re.sub(r'\d+', r'(\g<0>0)', '52 apples and 31 mangoes') 475 | '(520) apples and (310) mangoes' 476 | 477 | # swap words that are separated by a comma 478 | >>> re.sub(r'(\w+),(\w+)', r'\2,\1', 'a,b 42,24') 479 | 'b,a 24,42' 480 | ``` 481 | 482 | * using functions in replace part of `re.sub()` 483 | * **Note** that Python version **3.7** is used here 484 | 485 | ```python 486 | >>> from math import factorial 487 | >>> numbers = '1 2 3 4 5' 488 | >>> def fact_num(n): 489 | ... return str(factorial(int(n[0]))) 490 | ... 491 | >>> re.sub(r'\d+', fact_num, numbers) 492 | '1 2 6 24 120' 493 | 494 | # using lambda 495 | >>> re.sub(r'\d+', lambda m: str(factorial(int(m[0]))), numbers) 496 | '1 2 6 24 120' 497 | ``` 498 | 499 | * [call functions from re.sub](https://stackoverflow.com/questions/11944978/call-functions-from-re-sub) 500 | * [replace string pattern with output of function](https://stackoverflow.com/questions/12597370/python-replace-string-pattern-with-output-of-function) 501 | * [lambda tutorial](https://pythonconquerstheuniverse.wordpress.com/2011/08/29/lambda_tutorial/) 502 | 503 |
504 | 505 | ### Compiling Regular Expressions 506 | 507 | * Regular expressions can be compiled using `re.compile` function, which gives back a 508 | `re.Pattern` object 509 | * The top level `re` module functions are all available as methods for this object 510 | * Compiling a regular expression helps if the RE has to be used in multiple 511 | places or called upon multiple times inside a loop (speed benefit) 512 | * By default, Python maintains a small list of recently used RE, so the speed benefit 513 | doesn't apply for trivial use cases 514 | 515 | ```python 516 | >>> pet = re.compile(r'dog') 517 | >>> type(pet) 518 | 519 | >>> bool(pet.search('They bought a dog')) 520 | True 521 | >>> bool(pet.search('A cat crossed their path')) 522 | False 523 | 524 | >>> remove_parentheses = re.compile(r'\([^)]*\)') 525 | >>> remove_parentheses.sub('', 'a+b(addition) - foo() + c%d(#modulo)') 526 | 'a+b - foo + c%d' 527 | >>> remove_parentheses.sub('', 'Hi there(greeting). Nice day(a(b)') 528 | 'Hi there. Nice day' 529 | ``` 530 | 531 |
532 | 533 | ### Further Reading on Regular Expressions 534 | 535 | * [Python re(gex)?](https://github.com/learnbyexample/py_regular_expressions) - a book on regular expressions 536 | * [Python docs - re module](https://docs.python.org/3/library/re.html) 537 | * [Python docs - introductory tutorial to using regular expressions](https://docs.python.org/3/howto/regex.html) 538 | * [Comprehensive reference: What does this regex mean?](https://stackoverflow.com/questions/22937618/reference-what-does-this-regex-mean) 539 | * [rexegg](https://www.rexegg.com/) - tutorials, tricks and more 540 | * [regular-expressions](https://www.regular-expressions.info/) - tutorials and tools 541 | * [CommonRegex](https://github.com/madisonmay/CommonRegex) - collection of common regular expressions 542 | * Practice tools 543 | * [regex101](https://regex101.com/) - visual aid and online testing tool for regular expressions, select flavor as Python before use 544 | * [debuggex](https://www.debuggex.com) - railroad diagrams for regular expressions, select flavor as Python before use 545 | * [regexone](https://regexone.com/) - interative tutorial 546 | * [cheatsheet](https://www.shortcutfoo.com/app/dojos/python-regex/cheatsheet) - one can also learn it [interactively](https://www.shortcutfoo.com/app/dojos/python-regex) 547 | * [regexcrossword](https://regexcrossword.com/) - practice by solving crosswords, read 'How to play' section before you start 548 | 549 | -------------------------------------------------------------------------------- /User_input.md: -------------------------------------------------------------------------------- 1 | # Getting User input 2 | 3 | * [Integer input](#integer-input) 4 | * [Floating point input](#floating-point-input) 5 | * [String input](#string-input) 6 | 7 |
8 | 9 | ### Integer input 10 | 11 | ```python 12 | #!/usr/bin/python3 13 | 14 | usr_ip = input("Enter an integer number: ") 15 | 16 | # Need to explicitly convert input string to desired type 17 | usr_num = int(usr_ip) 18 | sqr_num = usr_num * usr_num 19 | 20 | print("Square of entered number is: {}".format(sqr_num)) 21 | ``` 22 | 23 | * Let us test the program by giving an integer number and a string 24 | * [Python docs - integer-literals](https://docs.python.org/3/reference/lexical_analysis.html#integer-literals) 25 | 26 | ``` 27 | $ ./user_input_int.py 28 | Enter an integer number: 23 29 | Square of entered number is: 529 30 | 31 | $ ./user_input_int.py 32 | Enter an integer number: abc 33 | Traceback (most recent call last): 34 | File "./user_input_int.py", line 6, in 35 | usr_num = int(usr_ip) 36 | ValueError: invalid literal for int() with base 10: 'abc' 37 | ``` 38 | 39 |
40 | 41 | ### Floating point input 42 | 43 | ```python 44 | #!/usr/bin/python3 45 | 46 | usr_ip = input("Enter a floating point number: ") 47 | 48 | # Need to explicitly convert input string to desired type 49 | usr_num = float(usr_ip) 50 | sqr_num = usr_num * usr_num 51 | 52 | # Limit the number of digits after decimal points to 2 53 | print("Square of entered number is: {0:.2f}".format(sqr_num)) 54 | ``` 55 | 56 | * The [E scientific notation](https://en.wikipedia.org/wiki/Scientific_notation#E_notation) can also be used when required 57 | * [Python docs - floating-point-literals](https://docs.python.org/3/reference/lexical_analysis.html#floating-point-literals) 58 | * [Python docs - floatingpoint](https://docs.python.org/3/tutorial/floatingpoint.html) 59 | 60 | ``` 61 | $ ./user_input_float.py 62 | Enter a floating point number: 3.232 63 | Square of entered number is: 10.45 64 | 65 | $ ./user_input_float.py 66 | Enter a floating point number: 42.7e5 67 | Square of entered number is: 18232900000000.00 68 | 69 | $ ./user_input_float.py 70 | Enter a floating point number: abc 71 | Traceback (most recent call last): 72 | File "./user_input_float.py", line 6, in 73 | usr_num = float(usr_ip) 74 | ValueError: could not convert string to float: 'abc' 75 | ``` 76 | 77 |
78 | 79 | ### String input 80 | 81 | ```python 82 | #!/usr/bin/python3 83 | 84 | usr_name = input("Hi there! What's your name? ") 85 | usr_color = input("And your favorite color is? ") 86 | 87 | print("{}, I like the {} color too".format(usr_name, usr_color)) 88 | ``` 89 | 90 | * No need any type conversion for string and no newline character to be taken care unlike Perl 91 | 92 | ``` 93 | $ ./user_input_str.py 94 | Hi there! What's your name? learnbyexample 95 | And your favorite color is? blue 96 | learnbyexample, I like the blue color too 97 | ``` 98 | 99 | -------------------------------------------------------------------------------- /exercise_files/f1.txt: -------------------------------------------------------------------------------- 1 | 8 2 | 53 3 | 3.14 4 | 84 5 | 73e2 6 | 100 7 | 2937 8 | -------------------------------------------------------------------------------- /exercise_files/f2.txt: -------------------------------------------------------------------------------- 1 | Hello123 World 35 2 | 341 2 3 | Good 13day 4 | How are 1784 you 5 | -------------------------------------------------------------------------------- /exercise_files/f3.txt: -------------------------------------------------------------------------------- 1 | power.Log 2 | foo.123.txt 3 | list 4 | report_12.log 5 | baz.TXT 6 | hello.RB 7 | loop.do.rb 8 | Fav_books 9 | -------------------------------------------------------------------------------- /exercise_files/poem.txt: -------------------------------------------------------------------------------- 1 | Roses are red, 2 | Violets are blue, 3 | Sugar is sweet, 4 | And so are you. 5 | -------------------------------------------------------------------------------- /exercise_files/q2a_int_length.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def len_int(n): 4 | pass 5 | 6 | assert len_int(123) == 3 7 | assert len_int(2) == 1 8 | assert len_int(+42) == 2 9 | assert len_int(-42) == 2 10 | assert len_int(572342) == 6 11 | assert len_int(962306349871524124750813401378124) == 33 12 | 13 | try: 14 | len_int('a') 15 | except TypeError as e: 16 | assert str(e) == 'provide only integer input' 17 | 18 | print('all tests passed') 19 | -------------------------------------------------------------------------------- /exercise_files/q2b_str_comparison.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def str_cmp(s1, s2): 4 | pass 5 | 6 | assert str_cmp('abc', 'Abc') 7 | assert str_cmp('Hi there', 'hi there') 8 | assert not str_cmp('foo', 'food') 9 | assert str_cmp('nice', 'nice') 10 | assert str_cmp('GoOd DaY', 'gOOd dAy') 11 | assert not str_cmp('how', 'who') 12 | 13 | print('all tests passed') 14 | -------------------------------------------------------------------------------- /exercise_files/q2c_str_same_letters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def str_anagram(s1, s2): 4 | pass 5 | 6 | assert str_anagram('god', 'Dog') 7 | assert str_anagram('beat', 'abet') 8 | assert str_anagram('Tap', 'paT') 9 | assert not str_anagram('beat', 'table') 10 | assert not str_anagram('seat', 'teal') 11 | 12 | print('all tests passed') 13 | -------------------------------------------------------------------------------- /exercise_files/q2d_to_num.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def num(ip): 4 | pass 5 | 6 | assert num(3) == 3 7 | assert num(0x1f) == 31 8 | assert num(0b101) == 5 9 | assert num(0o10) == 8 10 | assert num(3.32) == 3.32 11 | assert num('123') == 123 12 | assert num('-78') == -78 13 | assert num(" 42 \n ") == 42 14 | assert num('3.14') == 3.14 15 | assert num('3.982e5') == 398200.0 16 | s = '56' 17 | assert num(s) + 44 == 100 18 | s = '8' * 10 19 | assert num(s) == 8888888888 20 | 21 | assert type(num('42')) == int 22 | assert type(num('1.23')) == float 23 | 24 | try: 25 | assert num('foo') 26 | except ValueError as e: 27 | assert str(e) == 'could not convert string to int or float' 28 | 29 | try: 30 | assert num(['1', '2.3']) 31 | except TypeError as e: 32 | assert str(e) == 'not a valid input' 33 | 34 | print('all tests passed') 35 | -------------------------------------------------------------------------------- /exercise_files/q3a_6by7.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def six_by_seven(num): 4 | pass 5 | 6 | assert six_by_seven(66) == 'Food' 7 | assert six_by_seven(13) == 'Oops' 8 | assert six_by_seven(42) == 'Universe' 9 | assert six_by_seven(14) == 'Good' 10 | assert six_by_seven(84) == 'Universe' 11 | assert six_by_seven(235432) == 'Oops' 12 | 13 | print('all tests passed') 14 | -------------------------------------------------------------------------------- /exercise_files/q4a_iter_product.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def product(ip_iterable): 4 | pass 5 | 6 | assert product([1, 4, 21]) == 84 7 | assert product([-4, 2.3e12, 77.23, 982, 0b101]) == -3.48863356e+18 8 | assert product((-3, 11, 2)) == -66 9 | assert product({8, 300}) == 2400 10 | assert product([234, 121, 23, 945, 0]) == 0 11 | assert product(range(1, 6)) == 120 12 | 13 | print('all tests passed') 14 | -------------------------------------------------------------------------------- /exercise_files/q4b_lowest_value.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def nth_lowest(): 4 | pass 5 | 6 | nums = [42, 23421341, 234.2e3, 21, 232, 12312, -2343] 7 | assert nth_lowest(nums, 3) == 42 8 | assert nth_lowest(nums, 5) == 12312 9 | 10 | nums = [1, -2, 4, 2, 1, 3, 3, 5] 11 | assert nth_lowest(nums) == -2 12 | assert nth_lowest(nums, 4) == 3 13 | 14 | assert nth_lowest('unrecognizable', 3) == 'c' 15 | 16 | print('all tests passed') 17 | -------------------------------------------------------------------------------- /exercise_files/q4c_word_slices.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def word_slices(s): 4 | pass 5 | 6 | assert word_slices('i') == ["i"] 7 | assert word_slices('to') == ["to"] 8 | assert word_slices('are') == ["ar", "are", "re"] 9 | assert word_slices('boat') == ["bo", "boa", "boat", "oa", "oat", "at"] 10 | assert word_slices('table') == ["ta", "tab", "tabl", "table", "ab", 11 | "abl", "able", "bl", "ble", "le"] 12 | 13 | print('all tests passed') 14 | -------------------------------------------------------------------------------- /exercise_files/q5c_sort_by_ext.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def sort_by_ext(ip): 4 | pass 5 | 6 | exp_op = ['Fav_books\n', 'list\n', 'power.Log\n', 7 | 'report_12.log\n', 'hello.RB\n', 'loop.do.rb\n', 8 | 'baz.TXT\n', 'foo.123.txt\n'] 9 | 10 | assert sort_by_ext('f3.txt') == exp_op 11 | 12 | print('test passed') 13 | 14 | -------------------------------------------------------------------------------- /exercise_files/q6a_one_char_diff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def is_one_char_diff(word1, word2): 4 | pass 5 | 6 | assert is_one_char_diff('bar', 'car') 7 | assert is_one_char_diff('bar', 'Bat') 8 | assert is_one_char_diff('bar', 'bar') 9 | assert is_one_char_diff('bar', 'baZ') 10 | assert is_one_char_diff('A', 'b') 11 | 12 | assert not is_one_char_diff('a', '') 13 | assert not is_one_char_diff('bar', 'bark') 14 | assert not is_one_char_diff('bar', 'Art') 15 | assert not is_one_char_diff('bar', 'bot') 16 | assert not is_one_char_diff('ab', '') 17 | 18 | assert is_one_char_diff('Food', 'good') 19 | assert is_one_char_diff('food', 'fold') 20 | assert not is_one_char_diff('food', 'Foody') 21 | assert not is_one_char_diff('food', 'fled') 22 | 23 | print('all tests passed') 24 | -------------------------------------------------------------------------------- /exercise_files/q6b_alpha_order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def is_alpha_order(word): 4 | pass 5 | 6 | assert is_alpha_order('bot') 7 | assert is_alpha_order('art') 8 | assert is_alpha_order('toe') 9 | assert is_alpha_order('AborT') 10 | 11 | assert not is_alpha_order('are') 12 | assert not is_alpha_order('boat') 13 | assert not is_alpha_order('Flee') 14 | 15 | #sentence 16 | def is_alpha_order_sentence(sentence): 17 | pass 18 | 19 | assert is_alpha_order_sentence('Toe got bit') 20 | assert not is_alpha_order_sentence('All is well') 21 | 22 | print('all tests passed') 23 | -------------------------------------------------------------------------------- /exercise_files/q6c_max_nested.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def max_nested_braces(expr): 4 | pass 5 | 6 | assert max_nested_braces('a*b') == 0 7 | assert max_nested_braces('a*b{') == -1 8 | assert max_nested_braces('a*{b+c}') == 1 9 | assert max_nested_braces('{a+2}*{b+c}') == 1 10 | assert max_nested_braces('a*{b+c*{e*3.14}}') == 2 11 | assert max_nested_braces('a*{b+c*{e*3.14}}}') == -1 12 | assert max_nested_braces('a*{b+c}}') == -1 13 | assert max_nested_braces('a*b+{}') == 1 14 | assert max_nested_braces('}a+b{') == -1 15 | assert max_nested_braces('{{a+2}*{b+c}+e}') == 2 16 | assert max_nested_braces('{{a+2}*{b+{c*d}}+e}') == 3 17 | assert max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') == 4 18 | assert max_nested_braces('{{a+2}*{{b}+{c*d}}+e*d}}') == -1 19 | 20 | print('all tests passed') 21 | -------------------------------------------------------------------------------- /exercise_files/q7c_longest_word.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def longest_word(): 4 | pass 5 | 6 | ip_path = 'poem.txt' 7 | assert longest_word(ip_path) == 'Violets' 8 | 9 | # The Scarlet Pimpernel 10 | ip_path = 'https://www.gutenberg.org/files/60/60.txt' 11 | assert longest_word(ip_path, True) == 'misunderstandings' 12 | 13 | print('all tests passed') 14 | 15 | -------------------------------------------------------------------------------- /exercise_solutions/f1.txt: -------------------------------------------------------------------------------- 1 | 8 2 | 53 3 | 3.14 4 | 84 5 | 73e2 6 | 100 7 | 2937 8 | -------------------------------------------------------------------------------- /exercise_solutions/f2.txt: -------------------------------------------------------------------------------- 1 | Hello123 World 35 2 | 341 2 3 | Good 13day 4 | How are 1784 you 5 | -------------------------------------------------------------------------------- /exercise_solutions/f3.txt: -------------------------------------------------------------------------------- 1 | power.Log 2 | foo.123.txt 3 | list 4 | report_12.log 5 | baz.TXT 6 | hello.RB 7 | loop.do.rb 8 | Fav_books 9 | -------------------------------------------------------------------------------- /exercise_solutions/poem.txt: -------------------------------------------------------------------------------- 1 | Roses are red, 2 | Violets are blue, 3 | Sugar is sweet, 4 | And so are you. 5 | -------------------------------------------------------------------------------- /exercise_solutions/q1a_usr_ip.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | print('Please provide the following details') 4 | name = input('Enter your name: ') 5 | dept = input('Enter your department: ') 6 | colg = input('Enter your college: ') 7 | 8 | op_fmt = '{:<11}: {}' 9 | 10 | print('\n------------------------------------') 11 | print(op_fmt.format('Name', name)) 12 | print(op_fmt.format('Department', dept)) 13 | print(op_fmt.format('College', colg)) 14 | 15 | 16 | ####### Alternate 17 | #print('Please provide the following details') 18 | #labels = ('Name', 'Department', 'College') 19 | #usr_details = [input('Enter your ' + itm + ': ') for itm in labels] 20 | # 21 | #itm_size = len(sorted(labels, key=len)[-1]) + 1 22 | #op_fmt = '{:<' + str(itm_size) + '}: {}' 23 | #print('\n------------------------------------') 24 | #for k,v in zip(labels, usr_details): 25 | # print(op_fmt.format(k, v)) 26 | -------------------------------------------------------------------------------- /exercise_solutions/q2a_int_length.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def len_int(n): 4 | if type(n) != int: 5 | raise TypeError('provide only integer input') 6 | 7 | str_n = str(abs(n)) 8 | return len(str_n) 9 | 10 | assert len_int(123) == 3 11 | assert len_int(2) == 1 12 | assert len_int(+42) == 2 13 | assert len_int(-42) == 2 14 | assert len_int(572342) == 6 15 | assert len_int(962306349871524124750813401378124) == 33 16 | 17 | try: 18 | len_int('a') 19 | except TypeError as e: 20 | assert str(e) == 'provide only integer input' 21 | 22 | print('all tests passed') 23 | -------------------------------------------------------------------------------- /exercise_solutions/q2b_str_comparison.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def str_cmp(s1, s2): 4 | return s1.lower() == s2.lower() 5 | 6 | assert str_cmp('abc', 'Abc') 7 | assert str_cmp('Hi there', 'hi there') 8 | assert not str_cmp('foo', 'food') 9 | assert str_cmp('nice', 'nice') 10 | assert str_cmp('GoOd DaY', 'gOOd dAy') 11 | assert not str_cmp('how', 'who') 12 | 13 | print('all tests passed') 14 | -------------------------------------------------------------------------------- /exercise_solutions/q2c_str_same_letters.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def str_anagram(s1, s2): 4 | return sorted(s1.lower()) == sorted(s2.lower()) 5 | 6 | assert str_anagram('god', 'Dog') 7 | assert str_anagram('beat', 'abet') 8 | assert str_anagram('Tap', 'paT') 9 | assert not str_anagram('beat', 'table') 10 | assert not str_anagram('seat', 'teal') 11 | 12 | print('all tests passed') 13 | -------------------------------------------------------------------------------- /exercise_solutions/q2d_to_num.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def num(ip): 4 | if type(ip) in (int, float): 5 | return ip 6 | elif type(ip) != str: 7 | raise TypeError('not a valid input') 8 | 9 | try: 10 | return int(ip) 11 | except ValueError: 12 | try: 13 | return float(ip) 14 | except ValueError: 15 | raise ValueError('could not convert string to int or float') 16 | 17 | assert num(3) == 3 18 | assert num(0x1f) == 31 19 | assert num(0b101) == 5 20 | assert num(0o10) == 8 21 | assert num(3.32) == 3.32 22 | assert num('123') == 123 23 | assert num('-78') == -78 24 | assert num(" 42 \n ") == 42 25 | assert num('3.14') == 3.14 26 | assert num('3.982e5') == 398200.0 27 | s = '56' 28 | assert num(s) + 44 == 100 29 | s = '8' * 10 30 | assert num(s) == 8888888888 31 | 32 | assert type(num('42')) == int 33 | assert type(num('1.23')) == float 34 | 35 | try: 36 | num('foo') 37 | except ValueError as e: 38 | assert str(e) == 'could not convert string to int or float' 39 | 40 | try: 41 | num(['1', '2.3']) 42 | except TypeError as e: 43 | assert str(e) == 'not a valid input' 44 | 45 | print('all tests passed') 46 | -------------------------------------------------------------------------------- /exercise_solutions/q3a_6by7.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def six_by_seven(num): 4 | if num % 42 == 0: 5 | return 'Universe' 6 | elif num % 7 == 0: 7 | return 'Good' 8 | elif num % 6 == 0: 9 | return 'Food' 10 | else: 11 | return 'Oops' 12 | 13 | assert six_by_seven(66) == 'Food' 14 | assert six_by_seven(13) == 'Oops' 15 | assert six_by_seven(42) == 'Universe' 16 | assert six_by_seven(14) == 'Good' 17 | assert six_by_seven(84) == 'Universe' 18 | assert six_by_seven(235432) == 'Oops' 19 | 20 | print('all tests passed') 21 | 22 | ## bonus 23 | #for num in range(1, 101): 24 | # print(num, six_by_seven(num)) 25 | -------------------------------------------------------------------------------- /exercise_solutions/q3b_dec_bin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | for n in range(1, 1001): 4 | dec_n = str(n) 5 | bin_n = format(n, 'b') 6 | if dec_n == dec_n[::-1] and bin_n == bin_n[::-1]: 7 | print(dec_n, bin_n) 8 | 9 | #oct_n = format(n, 'o') 10 | #if dec_n == dec_n[::-1] and bin_n == bin_n[::-1] and oct_n == oct_n[::-1]: 11 | # print('{0:d} {0:#b} {0:#o}'.format(n)) 12 | 13 | #oct_n = format(n, 'o') 14 | #hex_n = format(n, 'x') 15 | ##if all((dec_n == dec_n[::-1], bin_n == bin_n[::-1], oct_n == oct_n[::-1], hex_n == hex_n[::-1])): 16 | #if dec_n == dec_n[::-1] and bin_n == bin_n[::-1] and \ 17 | # oct_n == oct_n[::-1] and hex_n == hex_n[::-1]: 18 | # print('{0:d} {0:#b} {0:#o} {0:#x}'.format(n)) 19 | -------------------------------------------------------------------------------- /exercise_solutions/q4a_iter_product.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def product(ip_iterable): 4 | op = 1 5 | for n in ip_iterable: 6 | op *= n 7 | return op 8 | 9 | assert product([1, 4, 21]) == 84 10 | assert product([-4, 2.3e12, 77.23, 982, 0b101]) == -3.48863356e+18 11 | assert product((-3, 11, 2)) == -66 12 | assert product({8, 300}) == 2400 13 | assert product([234, 121, 23, 945, 0]) == 0 14 | assert product(range(1, 6)) == 120 15 | 16 | print('all tests passed') 17 | -------------------------------------------------------------------------------- /exercise_solutions/q4b_lowest_value.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def nth_lowest(ip_iterable, n=1): 4 | return sorted(set(ip_iterable))[n-1] 5 | 6 | nums = [42, 23421341, 234.2e3, 21, 232, 12312, -2343] 7 | assert nth_lowest(nums, 3) == 42 8 | assert nth_lowest(nums, 5) == 12312 9 | 10 | nums = [1, -2, 4, 2, 1, 3, 3, 5] 11 | assert nth_lowest(nums) == -2 12 | assert nth_lowest(nums, 4) == 3 13 | 14 | assert nth_lowest('unrecognizable', 3) == 'c' 15 | 16 | print('all tests passed') 17 | -------------------------------------------------------------------------------- /exercise_solutions/q4c_word_slices.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def word_slices(s): 4 | size = len(s) 5 | if size < 3: 6 | return [s] 7 | return [s[i:j+1] for i in range(size-1) for j in range(i+1, size)] 8 | 9 | assert word_slices('i') == ["i"] 10 | assert word_slices('to') == ["to"] 11 | assert word_slices('are') == ["ar", "are", "re"] 12 | assert word_slices('boat') == ["bo", "boa", "boat", "oa", "oat", "at"] 13 | assert word_slices('table') == ["ta", "tab", "tabl", "table", "ab", 14 | "abl", "able", "bl", "ble", "le"] 15 | 16 | print('all tests passed') 17 | -------------------------------------------------------------------------------- /exercise_solutions/q5a_col_sum.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | with open('f1.txt', 'r', encoding='ascii') as f: 4 | total = 0 5 | for line in f: 6 | total += float(line) 7 | 8 | assert total == 10485.14 9 | 10 | print('test passed') 11 | 12 | # for small files that can fit in memory 13 | #total = sum(float(n) for n in open('f1.txt', encoding='ascii').readlines()) 14 | -------------------------------------------------------------------------------- /exercise_solutions/q5b_sum_ints.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import re 4 | 5 | with open('f2.txt', 'r', encoding='ascii') as f: 6 | total = 0 7 | for line in f: 8 | total += sum(int(n) for n in re.findall(r'\d+', line)) 9 | 10 | assert total == 2298 11 | 12 | #assert sum(int(n) for n in re.findall(r'\d+', open('f2.txt', encoding='ascii').read())) == 2298 13 | 14 | print('test passed') 15 | -------------------------------------------------------------------------------- /exercise_solutions/q5c_sort_by_ext.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def sort_by_ext(ip): 4 | lines = open(ip, encoding='ascii').readlines() 5 | return sorted(lines, key=lambda x: (x.split('.')[-1].lower(), x.lower())) 6 | 7 | exp_op = ['Fav_books\n', 'list\n', 'power.Log\n', 8 | 'report_12.log\n', 'hello.RB\n', 'loop.do.rb\n', 9 | 'baz.TXT\n', 'foo.123.txt\n'] 10 | 11 | assert sort_by_ext('f3.txt') == exp_op 12 | 13 | print('test passed') 14 | 15 | -------------------------------------------------------------------------------- /exercise_solutions/q6a_one_char_diff.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def is_one_char_diff(word1, word2): 4 | if len(word1) != len(word2): 5 | return False 6 | 7 | word1, word2 = word1.lower(), word2.lower() 8 | for i in range(len(word1)): 9 | if word1[0:i] + word1[i+1:] == word2[0:i] + word2[i+1:]: 10 | return True 11 | 12 | return False 13 | 14 | assert is_one_char_diff('bar', 'car') 15 | assert is_one_char_diff('bar', 'Bat') 16 | assert is_one_char_diff('bar', 'bar') 17 | assert is_one_char_diff('bar', 'baZ') 18 | assert is_one_char_diff('A', 'b') 19 | 20 | assert not is_one_char_diff('a', '') 21 | assert not is_one_char_diff('bar', 'bark') 22 | assert not is_one_char_diff('bar', 'Art') 23 | assert not is_one_char_diff('bar', 'bot') 24 | assert not is_one_char_diff('ab', '') 25 | 26 | assert is_one_char_diff('Food', 'good') 27 | assert is_one_char_diff('food', 'fold') 28 | assert not is_one_char_diff('food', 'Foody') 29 | assert not is_one_char_diff('food', 'fled') 30 | 31 | print('all tests passed') 32 | -------------------------------------------------------------------------------- /exercise_solutions/q6b_alpha_order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def is_alpha_order(word): 4 | word = word.lower() 5 | return list(word) == sorted(word) or list(word) == sorted(word, reverse=True) 6 | 7 | assert is_alpha_order('bot') 8 | assert is_alpha_order('art') 9 | assert is_alpha_order('toe') 10 | assert is_alpha_order('AborT') 11 | 12 | assert not is_alpha_order('are') 13 | assert not is_alpha_order('boat') 14 | assert not is_alpha_order('Flee') 15 | 16 | # sentence 17 | def is_alpha_order_sentence(sentence): 18 | return all(is_alpha_order(word) for word in sentence.split()) 19 | 20 | assert is_alpha_order_sentence('Toe got bit') 21 | assert not is_alpha_order_sentence('All is well') 22 | 23 | print('all tests passed') 24 | -------------------------------------------------------------------------------- /exercise_solutions/q6c_max_nested.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def max_nested_braces(expr): 4 | max_count = count = 0 5 | for char in expr: 6 | if char == '{': 7 | count += 1 8 | if count > max_count: 9 | max_count = count 10 | elif char == '}': 11 | if count == 0: 12 | return -1 13 | count -= 1 14 | 15 | if count != 0: 16 | return -1 17 | return max_count 18 | 19 | assert max_nested_braces('a*b') == 0 20 | assert max_nested_braces('a*b{') == -1 21 | assert max_nested_braces('a*{b+c}') == 1 22 | assert max_nested_braces('{a+2}*{b+c}') == 1 23 | assert max_nested_braces('a*{b+c*{e*3.14}}') == 2 24 | assert max_nested_braces('a*{b+c*{e*3.14}}}') == -1 25 | assert max_nested_braces('a*{b+c}}') == -1 26 | assert max_nested_braces('a*b+{}') == 1 27 | assert max_nested_braces('}a+b{') == -1 28 | assert max_nested_braces('{{a+2}*{b+c}+e}') == 2 29 | assert max_nested_braces('{{a+2}*{b+{c*d}}+e}') == 3 30 | assert max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') == 4 31 | assert max_nested_braces('{{a+2}*{{b}+{c*d}}+e*d}}') == -1 32 | 33 | print('all tests passed') 34 | 35 | 36 | #### alternate using regular expressions 37 | 38 | #import re 39 | # 40 | #def max_nested_braces(expr): 41 | # count = 0 42 | # while True: 43 | # expr, no_of_subs = re.subn(r'\{[^{}]*\}', '', expr) 44 | # if no_of_subs == 0: 45 | # break 46 | # count += 1 47 | # 48 | # if re.search(r'[{}]', expr): 49 | # return -1 50 | # return count 51 | 52 | # for bonus, use + instead of * 53 | #assert max_nested_braces('a*b+{}') == -1 54 | #assert max_nested_braces('a*{b+{}+c*{e*3.14}}') == -1 55 | -------------------------------------------------------------------------------- /exercise_solutions/q7_misc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import subprocess 4 | # provide path to your favorite media player and file to play 5 | subprocess.call(['vlc', r'/path/to/file']) 6 | 7 | import webbrowser 8 | webbrowser.open(r'https://github.com/learnbyexample/Python_Basics') 9 | -------------------------------------------------------------------------------- /exercise_solutions/q7c_longest_word.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import urllib.request, re 4 | 5 | def longest_word(ip, url=False): 6 | if url: 7 | ip_data = urllib.request.urlopen(ip).read().decode('utf-8') 8 | else: 9 | ip_data = open(ip, encoding='utf-8').read() 10 | 11 | return sorted(re.findall(r'[a-zA-Z]+', ip_data), key=len)[-1] 12 | 13 | ip_path = 'poem.txt' 14 | assert longest_word(ip_path) == 'Violets' 15 | 16 | # The Scarlet Pimpernel 17 | ip_path = 'https://www.gutenberg.org/files/60/60.txt' 18 | assert longest_word(ip_path, True) == 'misunderstandings' 19 | 20 | print('all tests passed') 21 | 22 | -------------------------------------------------------------------------------- /mini_projects/pcalc.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | eval is used, so use at your own risk 5 | 6 | Examples: 7 | $ ./pcalc.py -vx '0b101 + 3' 8 | 0b101 + 3 = 0x8 9 | $ ./pcalc.py '0x23' 10 | 35 11 | $ ./pcalc.py -f2 '76/13' 12 | 5.85 13 | $ ./pcalc.py '27**12' 14 | 150094635296999121 15 | $ echo '97 + 232' | ./pcalc.py 16 | 329 17 | 18 | $ ./pcalc.py '42 + 2s' 19 | Error: Not a valid input expression 20 | """ 21 | 22 | import argparse, sys, fileinput 23 | 24 | parser = argparse.ArgumentParser() 25 | parser.add_argument('arith_expr', nargs='?', default=sys.stdin, help="arithmetic expression") 26 | parser.add_argument('-v', help="verbose, show both input and output in result", action="store_true") 27 | parser.add_argument('-f', type=int, help="specify floating point output precision") 28 | parser.add_argument('-b', help="output in binary format", action="store_true") 29 | parser.add_argument('-o', help="output in octal format", action="store_true") 30 | parser.add_argument('-x', help="output in hexadecimal format", action="store_true") 31 | args = parser.parse_args() 32 | 33 | if type(args.arith_expr) != str: 34 | args.arith_expr = fileinput.input().readline().strip() 35 | ip_expr = args.arith_expr 36 | 37 | try: 38 | result = eval(ip_expr) 39 | 40 | if args.f: 41 | result = "{0:.{1}f}".format(result, args.f) 42 | elif args.b: 43 | result = "{:#b}".format(int(result)) 44 | elif args.o: 45 | result = "{:#o}".format(int(result)) 46 | elif args.x: 47 | result = "{:#x}".format(int(result)) 48 | 49 | if args.v: 50 | print("{} = {}".format(args.arith_expr, result)) 51 | else: 52 | print(result) 53 | except (NameError, SyntaxError) as e: 54 | sys.exit("Error: Not a valid input expression") 55 | 56 | -------------------------------------------------------------------------------- /python_programs/calling_shell_commands.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import subprocess 4 | 5 | # Executing external command 'date' 6 | subprocess.call('date') 7 | 8 | # Passing options and arguments to command 9 | print("\nToday is ", end="", flush=True) 10 | subprocess.call(['date', '-u', '+%A']) 11 | 12 | # another example 13 | print("\nSearching for 'hello world'", flush=True) 14 | subprocess.call(['grep', '-i', 'hello world', 'hello_world.py']) 15 | -------------------------------------------------------------------------------- /python_programs/file_reading.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # open file, read line by line and print it 4 | filename = 'hello_world.py' 5 | f = open(filename, 'r', encoding='ascii') 6 | 7 | print("Contents of " + filename) 8 | print('-' * 30) 9 | for line in f: 10 | print(line, end='') 11 | 12 | f.close() 13 | 14 | # 'with' is a simpler alternative, automatically handles file closing 15 | filename = 'while_loop.py' 16 | 17 | print("\n\nContents of " + filename) 18 | print('-' * 30) 19 | with open(filename, 'r', encoding='ascii') as f: 20 | for line in f: 21 | print(line, end='') 22 | -------------------------------------------------------------------------------- /python_programs/file_reading_error.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | with open('xyz.py', 'r', encoding='ascii') as f: 4 | for line in f: 5 | print(line, end='') 6 | -------------------------------------------------------------------------------- /python_programs/file_writing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | with open('new_file.txt', 'w', encoding='ascii') as f: 4 | f.write("This is a sample line of text\n") 5 | f.write("Yet another line\n") 6 | -------------------------------------------------------------------------------- /python_programs/for_loop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | number = 9 4 | for i in range(1, 5): 5 | mul_table = number * i 6 | print("{} * {} = {}".format(number, i, mul_table)) 7 | -------------------------------------------------------------------------------- /python_programs/functions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # ----- function without arguments ----- 4 | def greeting(): 5 | print("-----------------------------") 6 | print(" Hello World ") 7 | print("-----------------------------") 8 | 9 | greeting() 10 | 11 | # ----- function with arguments ----- 12 | def sum_two_numbers(num1, num2): 13 | total = num1 + num2 14 | print("{} + {} = {}".format(num1, num2, total)) 15 | 16 | sum_two_numbers(3, 4) 17 | 18 | # ----- function with return value ----- 19 | def num_square(num): 20 | return num * num 21 | 22 | my_num = 3 23 | print(num_square(2)) 24 | print(num_square(my_num)) 25 | -------------------------------------------------------------------------------- /python_programs/functions_default_arg_value.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # ----- function with default valued argument ----- 4 | def greeting(style_char='-'): 5 | print(style_char * 29) 6 | print(" Hello World ") 7 | print(style_char * 29) 8 | 9 | print("Default style") 10 | greeting() 11 | 12 | print("\nStyle character *") 13 | greeting('*') 14 | 15 | print("\nStyle character =") 16 | greeting(style_char='=') 17 | -------------------------------------------------------------------------------- /python_programs/functions_main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # ----- function without arguments ----- 4 | def greeting(): 5 | print("-----------------------------") 6 | print(" Hello World ") 7 | print("-----------------------------") 8 | 9 | # ----- function with arguments ----- 10 | def sum_two_numbers(num1, num2): 11 | sum = num1 + num2 12 | print("{} + {} = {}".format(num1, num2, sum)) 13 | 14 | # ----- function with return value ----- 15 | def num_square(num): 16 | return num * num 17 | 18 | # ----- main ----- 19 | def main(): 20 | greeting() 21 | sum_two_numbers(3, 4) 22 | 23 | my_num = 3 24 | print(num_square(2)) 25 | print(num_square(my_num)) 26 | 27 | if __name__ == "__main__": 28 | main() 29 | -------------------------------------------------------------------------------- /python_programs/hello_world.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | print("Hello World") 4 | -------------------------------------------------------------------------------- /python_programs/if_elif_else.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | num = 45 4 | 5 | # only if 6 | if num > 25: 7 | print("Hurray! {} is greater than 25".format(num)) 8 | 9 | # if-else 10 | if num % 2 == 0: 11 | print("{} is an even number".format(num)) 12 | else: 13 | print("{} is an odd number".format(num)) 14 | 15 | # if-elif-else 16 | # any number of elif can be used 17 | if num < 0: 18 | print("{} is a negative number".format(num)) 19 | elif num > 0: 20 | print("{} is a positive number".format(num)) 21 | else: 22 | print("{} is neither postive nor a negative number".format(num)) 23 | -------------------------------------------------------------------------------- /python_programs/if_else_oneliner.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | num = 42 4 | 5 | num_type = 'even' if num % 2 == 0 else 'odd' 6 | print("{} is an {} number".format(num, num_type)) 7 | -------------------------------------------------------------------------------- /python_programs/inplace_file_editing.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import fileinput 4 | 5 | with fileinput.input(inplace=True) as f: 6 | for line in f: 7 | line = line.replace('line of text', 'line') 8 | print(line, end='') 9 | -------------------------------------------------------------------------------- /python_programs/line_count.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys, pathlib, subprocess, re 4 | 5 | if len(sys.argv) != 2: 6 | sys.exit("Error: Please provide exactly one filename as argument") 7 | 8 | program_name = sys.argv[0] 9 | filename = sys.argv[1] 10 | 11 | if not pathlib.Path(filename).is_file(): 12 | sys.exit("File '{}' not found".format(filename)) 13 | 14 | if re.search(r'line_count.py', program_name): 15 | lc = subprocess.getoutput('wc -l < ' + filename) 16 | print("No. of lines in '{}' is: {}".format(filename, lc)) 17 | elif re.search(r'word_count.py', program_name): 18 | wc = subprocess.getoutput('wc -w < ' + filename) 19 | print("No. of words in '{}' is: {}".format(filename, wc)) 20 | else: 21 | sys.exit("Program name '{}' not recognized".format(program_name)) 22 | -------------------------------------------------------------------------------- /python_programs/list_comprehension.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import time 4 | 5 | numbers = list(range(1,100001)) 6 | fl_square_numbers = [] 7 | 8 | # reference time 9 | t0 = time.perf_counter() 10 | 11 | # ------------ for loop ------------ 12 | for num in numbers: 13 | fl_square_numbers.append(num * num) 14 | 15 | # reference time 16 | t1 = time.perf_counter() 17 | 18 | # ------- list comprehension ------- 19 | lc_square_numbers = [num * num for num in numbers] 20 | 21 | # performance results 22 | t2 = time.perf_counter() 23 | fl_time = t1 - t0 24 | lc_time = t2 - t1 25 | improvement = (fl_time - lc_time) / fl_time * 100 26 | 27 | print("Time with for loop: {:.4f}".format(fl_time)) 28 | print("Time with list comprehension: {:.4f}".format(lc_time)) 29 | print("Improvement: {:.2f}%".format(improvement)) 30 | 31 | if fl_square_numbers == lc_square_numbers: 32 | print("\nfl_square_numbers and lc_square_numbers are equivalent") 33 | else: 34 | print("\nfl_square_numbers and lc_square_numbers are NOT equivalent") 35 | -------------------------------------------------------------------------------- /python_programs/list_looping.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | numbers = [2, 12, 3, 25, 624, 21, 5, 9, 12] 4 | odd_numbers = [] 5 | even_numbers = [] 6 | 7 | for num in numbers: 8 | odd_numbers.append(num) if(num % 2) else even_numbers.append(num) 9 | 10 | print("numbers: {}".format(numbers)) 11 | print("odd_numbers: {}".format(odd_numbers)) 12 | print("even_numbers: {}".format(even_numbers)) 13 | -------------------------------------------------------------------------------- /python_programs/list_looping_enumeration.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | north_dishes = ['Aloo tikki', 'Baati', 'Khichdi', 'Makki roti', 'Poha'] 4 | 5 | print("My favorite North Indian dishes:") 6 | for idx, item in enumerate(north_dishes): 7 | print("{}. {}".format(idx + 1, item)) 8 | -------------------------------------------------------------------------------- /python_programs/list_of_lists.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import random 4 | 5 | north = ['aloo tikki', 'baati', 'khichdi', 'makki roti', 'poha'] 6 | south = ['appam', 'bisibele bath', 'dosa', 'koottu', 'sevai'] 7 | west = ['dhokla', 'khakhra', 'modak', 'shiro', 'vada pav'] 8 | east = ['hando guri', 'litti', 'momo', 'rosgulla', 'shondesh'] 9 | zones = ['North', 'South', 'West', 'East'] 10 | 11 | choose_dish = [north, south, west, east] 12 | rand_zone = random.randrange(4) 13 | rand_dish = random.randrange(5) 14 | 15 | zone = zones[rand_zone] 16 | dish = choose_dish[rand_zone][rand_dish] 17 | print("Would you like to have '{}' speciality '{}' today?".format(zone, dish)) 18 | -------------------------------------------------------------------------------- /python_programs/loop_with_break.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import random 4 | 5 | while True: 6 | # as with range() function, 500 is not inclusive 7 | random_int = random.randrange(500) 8 | if random_int % 4 == 0 and random_int % 6 == 0: 9 | break 10 | print("Random number divisible by 4 and 6: {}".format(random_int)) 11 | -------------------------------------------------------------------------------- /python_programs/loop_with_continue.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | prev_num = 0 4 | curr_num = 0 5 | print("The first ten numbers in fibonacci sequence: ", end='') 6 | 7 | for num in range(10): 8 | print(curr_num, end=' ') 9 | 10 | if num == 0: 11 | curr_num = 1 12 | continue 13 | 14 | temp = curr_num 15 | curr_num = curr_num + prev_num 16 | prev_num = temp 17 | 18 | print("") 19 | -------------------------------------------------------------------------------- /python_programs/palindrome.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | Asks for user input and tells if string is palindrome or not 5 | 6 | Allowed characters: alphabets and punctuations .,;:'"-!? 7 | Minimum alphabets: 3 and cannot be all same 8 | 9 | Informs if input is invalid and asks user for input again 10 | """ 11 | 12 | import re 13 | 14 | def is_palindrome(usr_ip): 15 | """ 16 | Checks if string is a palindrome 17 | 18 | ValueError: if string is invalid 19 | 20 | Returns True if palindrome, False otherwise 21 | """ 22 | 23 | # remove punctuations & whitespace and change to all lowercase 24 | ip_str = re.sub(r'[\s.;:,\'"!?-]', r'', usr_ip).lower() 25 | 26 | if re.search(r'[^a-zA-Z]', ip_str): 27 | raise ValueError("Characters other than alphabets and punctuations") 28 | elif len(ip_str) < 3: 29 | raise ValueError("Less than 3 alphabets") 30 | else: 31 | return ip_str == ip_str[::-1] and not re.search(r'^(.)\1+$', ip_str) 32 | 33 | def main(): 34 | while True: 35 | try: 36 | usr_ip = input("Enter a palindrome: ") 37 | if is_palindrome(usr_ip): 38 | print("{} is a palindrome".format(usr_ip)) 39 | else: 40 | print("{} is NOT a palindrome".format(usr_ip)) 41 | break 42 | except ValueError as e: 43 | print('Error: ' + str(e)) 44 | 45 | if __name__ == "__main__": 46 | main() 47 | -------------------------------------------------------------------------------- /python_programs/report.log: -------------------------------------------------------------------------------- 1 | basic_test 2 | input_test 3 | error message 4 | async_test 5 | output_test 6 | input_test 7 | sync_test 8 | -------------------------------------------------------------------------------- /python_programs/shell_command_output_redirections.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import subprocess 4 | 5 | # Output includes any error messages also 6 | print("Getting output of 'pwd' command", flush=True) 7 | curr_working_dir = subprocess.getoutput('pwd') 8 | print(curr_working_dir) 9 | 10 | # Get status and output of command executed 11 | # Exit status other than '0' is considered as something gone wrong 12 | ls_command = 'ls hello_world.py xyz.py' 13 | print("\nCalling command '{}'".format(ls_command), flush=True) 14 | (ls_status, ls_output) = subprocess.getstatusoutput(ls_command) 15 | print("status: {}\noutput: '{}'".format(ls_status, ls_output)) 16 | 17 | # Suppress error messages if preferred 18 | # subprocess.call() returns status of command which can be used instead 19 | print("\nCalling command with error msg suppressed", flush=True) 20 | ls_status = subprocess.call(ls_command, shell=True, stderr=subprocess.DEVNULL) 21 | print("status: {}".format(ls_status)) 22 | -------------------------------------------------------------------------------- /python_programs/shell_expansion.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import subprocess 4 | 5 | # Executing command without shell expansion 6 | print("No shell expansion when shell=False", flush=True) 7 | subprocess.call(['echo', 'Hello $USER']) 8 | 9 | # Executing command with shell expansion 10 | print("\nshell expansion when shell=True", flush=True) 11 | subprocess.call('echo Hello $USER', shell=True) 12 | 13 | # escape quotes if it is part of command 14 | print("\nSearching for 'hello world'", flush=True) 15 | subprocess.call('grep -i \'hello world\' hello_world.py', shell=True) 16 | -------------------------------------------------------------------------------- /python_programs/sort_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse, subprocess 4 | 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument('-f', '--file', help="file to be sorted", required=True) 7 | parser.add_argument('-u', help="sort uniquely", action="store_true") 8 | args = parser.parse_args() 9 | 10 | if args.u: 11 | subprocess.call(['sort', '-u', args.file, '-o', args.file]) 12 | else: 13 | subprocess.call(['sort', args.file, '-o', args.file]) 14 | -------------------------------------------------------------------------------- /python_programs/sum2nums_argparse.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import argparse 4 | 5 | parser = argparse.ArgumentParser() 6 | parser.add_argument('num1', type=int, help="first number") 7 | parser.add_argument('num2', type=int, help="second number") 8 | args = parser.parse_args() 9 | 10 | total = args.num1 + args.num2 11 | print("{} + {} = {}".format(args.num1, args.num2, total)) 12 | -------------------------------------------------------------------------------- /python_programs/sum_of_two_numbers.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys 4 | 5 | if len(sys.argv) != 3: 6 | sys.exit("Error: Please provide exactly two numbers as arguments") 7 | else: 8 | (num1, num2) = sys.argv[1:] 9 | total = int(num1) + int(num2) 10 | print("{} + {} = {}".format(num1, num2, total)) 11 | -------------------------------------------------------------------------------- /python_programs/syntax_error.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | print "Have a nice day" 4 | -------------------------------------------------------------------------------- /python_programs/test_list.txt: -------------------------------------------------------------------------------- 1 | basic_test 2 | input_test 3 | async_test 4 | output_test 5 | input_test 6 | sync_test 7 | -------------------------------------------------------------------------------- /python_programs/test_palindrome.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import palindrome 4 | 5 | assert palindrome.is_palindrome('Madam') 6 | assert palindrome.is_palindrome("Dammit, I'm mad!") 7 | assert not palindrome.is_palindrome('aaa') 8 | assert palindrome.is_palindrome('Malayalam') 9 | 10 | try: 11 | assert palindrome.is_palindrome('as2') 12 | except ValueError as e: 13 | assert str(e) == 'Characters other than alphabets and punctuations' 14 | 15 | try: 16 | assert palindrome.is_palindrome("a'a") 17 | except ValueError as e: 18 | assert str(e) == 'Less than 3 alphabets' 19 | 20 | print('All tests passed') 21 | -------------------------------------------------------------------------------- /python_programs/triple_quoted_string.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | """ 4 | This line is part of multiline comment 5 | 6 | This program shows examples of triple quoted strings 7 | """ 8 | 9 | # assigning multiple line string to variable 10 | poem = """\ 11 | The woods are lovely, dark and deep, 12 | But I have promises to keep, 13 | And miles to go before I sleep, 14 | And miles to go before I sleep. 15 | """ 16 | 17 | print(poem, end='') 18 | -------------------------------------------------------------------------------- /python_programs/unittest_palindrome.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import palindrome 4 | import unittest 5 | 6 | class TestPalindrome(unittest.TestCase): 7 | 8 | def test_valid(self): 9 | # check valid input strings 10 | self.assertTrue(palindrome.is_palindrome('kek')) 11 | self.assertTrue(palindrome.is_palindrome("Dammit, I'm mad!")) 12 | self.assertFalse(palindrome.is_palindrome('zzz')) 13 | self.assertFalse(palindrome.is_palindrome('cool')) 14 | 15 | def test_error(self): 16 | # check only the exception raised 17 | with self.assertRaises(ValueError): 18 | palindrome.is_palindrome('abc123') 19 | 20 | with self.assertRaises(TypeError): 21 | palindrome.is_palindrome(7) 22 | 23 | # check error message as well 24 | with self.assertRaises(ValueError) as cm: 25 | palindrome.is_palindrome('on 2 no') 26 | em = str(cm.exception) 27 | self.assertEqual(em, 'Characters other than alphabets and punctuations') 28 | 29 | with self.assertRaises(ValueError) as cm: 30 | palindrome.is_palindrome('to') 31 | em = str(cm.exception) 32 | self.assertEqual(em, 'Less than 3 alphabets') 33 | 34 | if __name__ == '__main__': 35 | unittest.main() 36 | 37 | -------------------------------------------------------------------------------- /python_programs/unittest_palindrome_main.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import palindrome 4 | import unittest 5 | from unittest import mock 6 | from io import StringIO 7 | 8 | class TestPalindrome(unittest.TestCase): 9 | 10 | @mock.patch('sys.stdout', new_callable=StringIO) 11 | def main_op(self, tst_str, mock_stdout): 12 | with mock.patch('builtins.input', side_effect=tst_str): 13 | palindrome.main() 14 | return mock_stdout.getvalue() 15 | 16 | def test_valid(self): 17 | for s in ('Malayalam', 'kek'): 18 | self.assertEqual(self.main_op([s]), s + ' is a palindrome\n') 19 | 20 | for s in ('zzz', 'cool'): 21 | self.assertEqual(self.main_op([s]), s + ' is NOT a palindrome\n') 22 | 23 | def test_error(self): 24 | em1 = 'Error: Characters other than alphabets and punctuations\n' 25 | em2 = 'Error: Less than 3 alphabets\n' 26 | 27 | tst1 = em1 + 'Madam is a palindrome\n' 28 | self.assertEqual(self.main_op(['123', 'Madam']), tst1) 29 | 30 | tst2 = em2 + em1 + 'Jerry is NOT a palindrome\n' 31 | self.assertEqual(self.main_op(['to', 'a2a', 'Jerry']), tst2) 32 | 33 | if __name__ == '__main__': 34 | unittest.main() 35 | -------------------------------------------------------------------------------- /python_programs/user_input_exception.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | while True: 4 | try: 5 | usr_num = int(input("Enter an integer number: ")) 6 | break 7 | except ValueError: 8 | print("Not an integer, try again") 9 | 10 | print("Square of entered number is: {}".format(usr_num * usr_num)) 11 | -------------------------------------------------------------------------------- /python_programs/user_input_float.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | usr_ip = input("Enter a floating point number: ") 4 | 5 | # Need to explicitly convert input string to desired type 6 | usr_num = float(usr_ip) 7 | sqr_num = usr_num * usr_num 8 | 9 | # Limit the number of digits after decimal points to 2 10 | print("Square of entered number is: {0:.2f}".format(sqr_num)) 11 | -------------------------------------------------------------------------------- /python_programs/user_input_int.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | usr_ip = input("Enter an integer number: ") 4 | 5 | # Need to explicitly convert input string to desired type 6 | usr_num = int(usr_ip) 7 | sqr_num = usr_num * usr_num 8 | 9 | print("Square of entered number is: {}".format(sqr_num)) 10 | -------------------------------------------------------------------------------- /python_programs/user_input_str.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | usr_name = input("Hi there! What's your name? ") 4 | usr_color = input("And your favorite color is? ") 5 | 6 | print("{}, I like the {} color too".format(usr_name, usr_color)) 7 | -------------------------------------------------------------------------------- /python_programs/variable_scope_1.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def print_num(): 4 | print("Yeehaw! num is visible in this scope, its value is: " + str(num)) 5 | 6 | num = 25 7 | print_num() 8 | -------------------------------------------------------------------------------- /python_programs/variable_scope_2.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def square_of_num(num): 4 | sqr_num = num * num 5 | 6 | square_of_num(5) 7 | print("5 * 5 = {}".format(sqr_num)) 8 | -------------------------------------------------------------------------------- /python_programs/variable_scope_3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | def square_of_num(num): 4 | global sqr_num 5 | sqr_num = num * num 6 | 7 | square_of_num(5) 8 | print("5 * 5 = {}".format(sqr_num)) 9 | -------------------------------------------------------------------------------- /python_programs/variable_scope_4.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | sqr_num = 4 4 | 5 | def square_of_num(num): 6 | sqr_num = num * num 7 | print("5 * 5 = {}".format(sqr_num)) 8 | 9 | square_of_num(5) 10 | print("Whoops! sqr_num is still {}!".format(sqr_num)) 11 | -------------------------------------------------------------------------------- /python_programs/varying_command_line_args.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | import sys, pathlib, subprocess 4 | 5 | if len(sys.argv) < 2: 6 | sys.exit("Error: Please provide atleast one filename as argument") 7 | 8 | input_files = sys.argv[1:] 9 | files_not_found = [] 10 | 11 | for filename in input_files: 12 | if not pathlib.Path(filename).is_file(): 13 | files_not_found.append("File '{}' not found".format(filename)) 14 | continue 15 | 16 | line_count = subprocess.getoutput('wc -l < ' + filename) 17 | print("{0:40}: {1:4} lines".format(filename, line_count)) 18 | 19 | print("\n".join(files_not_found)) 20 | -------------------------------------------------------------------------------- /python_programs/while_loop.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | 3 | # continuously ask user input till it is a positive integer 4 | usr_string = 'not a number' 5 | while not usr_string.isnumeric(): 6 | usr_string = input("Enter a positive integer: ") 7 | --------------------------------------------------------------------------------