├── 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 |
--------------------------------------------------------------------------------