├── 100_page_python_intro.md
├── LICENSE
├── README.md
├── Version_changes.md
├── exercises
└── Exercises.md
├── images
├── Python_shell_run.png
├── debug_in_action.png
├── debug_window.png
├── hello.png
├── help_print.png
├── info.svg
├── py_intro_ls.png
├── pypi_copy_cmd.png
├── python_exercises.png
└── warning.svg
├── programs
├── cli_args
│ ├── inplace_edit.py
│ ├── ip.txt
│ ├── op.txt
│ ├── sample.txt
│ ├── sort_ext.py
│ ├── sort_ext_stdin.py
│ └── sum_two_nums.py
├── control_structures
│ ├── countdown.py
│ └── odd_even.py
├── dealing_with_files
│ ├── ip.txt
│ ├── read_file.py
│ └── write_file.py
├── exceptions
│ ├── syntax_error.py
│ ├── try_except.py
│ ├── try_except_else.py
│ └── try_except_finally.py
├── functions
│ ├── default_args.py
│ ├── no_args.py
│ └── with_args.py
├── introduction
│ └── hello.py
├── modules
│ ├── num_funcs.py
│ ├── num_funcs_module.py
│ └── rand_number.py
├── strings_user_input
│ └── triple_quotes.py
└── testing
│ ├── exception_testing.py
│ ├── nested_braces.py
│ └── nested_braces_re.py
└── sample_chapters
└── 100_page_python_intro_sample.pdf
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 Sundeep Agarwal
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # 100 Page Python Intro
2 |
3 | This book is a short, introductory guide for those already familiar with programming basics. Visit https://youtu.be/aoWJzaSs0cs for a short video about the book.
4 |
5 |
6 |
7 | The book also includes exercises to test your understanding, which are presented together as a single file in this repo — [Exercises.md](./exercises/Exercises.md).
8 |
9 | You can also use [this interactive TUI app](https://github.com/learnbyexample/TUI-apps/tree/main/PythonExercises) to practice some of the exercises from the book.
10 |
11 | See [Version_changes.md](./Version_changes.md) to keep track of changes made to the book.
12 |
13 |
14 |
15 | # E-book
16 |
17 | * You can purchase the pdf/epub versions of the book using these links:
18 | * https://leanpub.com/100pagepythonintro
19 | * https://learnbyexample.gumroad.com/l/100pagepythonintro
20 | * You can also get the book as part of these bundles:
21 | * **All books bundle** bundle from https://leanpub.com/b/learnbyexample-all-books or https://learnbyexample.gumroad.com/l/all-books
22 | * **Learn by example Python bundle** from https://leanpub.com/b/python-bundle or https://learnbyexample.gumroad.com/l/python-bundle
23 | * See https://learnbyexample.github.io/books/ for a list of other books
24 |
25 | For a preview of the book, see [sample chapters](./sample_chapters/100_page_python_intro_sample.pdf).
26 |
27 | The book can also be [viewed as a single markdown file in this repo](./100_page_python_intro.md). See my blogpost on [generating pdf/epub from markdown using pandoc](https://learnbyexample.github.io/customizing-pandoc/) if you are interested in the ebook creation process.
28 |
29 | For the web version of the book, visit https://learnbyexample.github.io/100_page_python_intro/
30 |
31 |
32 |
33 | # Testimonials
34 |
35 | >It's very thorough, written with care, and presented in a way that makes sense. Even as an intermediate Python programmer, I found use in this book.
36 | >
37 | > — feedback by [Andrew Healey](https://healeycodes.com/) on [Hacker News](https://news.ycombinator.com/item?id=26082464)
38 |
39 |
40 |
41 | # Feedback and Contributing
42 |
43 | ⚠️ ⚠️ Please DO NOT submit pull requests. Main reason being any modification requires changes in multiple places.
44 |
45 | I would highly appreciate it if you'd let me know how you felt about this book. It could be anything from a simple thank you, pointing out a typo, mistakes in code snippets, which aspects of the book worked for you (or didn't!) and so on. Reader feedback is essential and especially so for self-published authors.
46 |
47 | You can reach me via:
48 |
49 | * Issue Manager: [https://github.com/learnbyexample/100_page_python_intro/issues](https://github.com/learnbyexample/100_page_python_intro/issues)
50 | * E-mail: `echo 'bGVhcm5ieWV4YW1wbGUubmV0QGdtYWlsLmNvbQo=' | base64 --decode`
51 | * Twitter: [https://twitter.com/learn_byexample](https://twitter.com/learn_byexample)
52 |
53 |
54 |
55 | # Table of Contents
56 |
57 | 1. Preface
58 | 2. Introduction
59 | 3. Numeric data types
60 | 4. Strings and user input
61 | 5. Defining functions
62 | 6. Control structures
63 | 7. Importing and creating modules
64 | 8. Installing modules and Virtual environments
65 | 9. Exception handling
66 | 10. Debugging
67 | 11. Testing
68 | 12. Tuple and Sequence operations
69 | 13. List
70 | 14. Mutability
71 | 15. Dict
72 | 16. Set
73 | 17. Text processing
74 | 18. Comprehensions and Generator expressions
75 | 19. Dealing with files
76 | 20. Executing external commands
77 | 21. Command line arguments
78 |
79 |
80 |
81 | # Acknowledgements
82 |
83 | * [Official Python website](https://docs.python.org/3/) — documentation and examples
84 | * [stackoverflow](https://stackoverflow.com/) and [unix.stackexchange](https://unix.stackexchange.com/) — for getting answers to pertinent questions on Python, Shell and programming in general
85 | * [/r/learnpython](https://old.reddit.com/r/learnpython) and [/r/learnprogramming](https://old.reddit.com/r/learnprogramming) — helpful forum for beginners
86 | * [/r/Python/](https://old.reddit.com/r/Python/) — general Python discussion
87 | * [tex.stackexchange](https://tex.stackexchange.com/) — for help on [pandoc](https://github.com/jgm/pandoc/) and `tex` related questions
88 | * [canva](https://www.canva.com/) — cover image
89 | * [oxipng](https://github.com/shssoichiro/oxipng), [pngquant](https://pngquant.org/) and [svgcleaner](https://github.com/RazrFalcon/svgcleaner) — optimizing images
90 | * [Warning](https://commons.wikimedia.org/wiki/File:Warning_icon.svg) and [Info](https://commons.wikimedia.org/wiki/File:Info_icon_002.svg) icons by [Amada44](https://commons.wikimedia.org/wiki/User:Amada44) under public domain
91 | * **Dean Clark** and **Elijah** for catching a few typos
92 | * [mdBook](https://github.com/rust-lang/mdBook) — for web version of the book
93 | * [mdBook-pagetoc](https://github.com/JorelAli/mdBook-pagetoc) — for adding table of contents for each chapter
94 | * [minify-html](https://github.com/wilsonzlin/minify-html) — for minifying html files
95 |
96 |
97 |
98 | # License
99 |
100 | The book is licensed under a [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-nc-sa/4.0/).
101 |
102 | The code snippets are licensed under MIT, see [LICENSE](./LICENSE) file.
103 |
104 |
--------------------------------------------------------------------------------
/Version_changes.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ### 2.0
4 |
5 | * Python version updated to **3.13.0**
6 | * Added more exercises and you can now practice some of them using this [interactive TUI app](https://github.com/learnbyexample/TUI-apps/tree/main/PythonExercises)
7 | * Descriptions and external links were updated/corrected
8 | * Updated Acknowledgements section
9 | * Code snippets related to info/warning sections will now appear as a single block
10 | * New cover image
11 | * Images centered for EPUB format
12 |
13 |
14 |
15 | ### 1.2
16 |
17 | * Updated installation section with more details
18 | * Added instructions for using Python from Windows CMD
19 | * Examples for escaping `{` and `}` characters when using f-strings
20 | * Corrected various typos, improved descriptions, code snippets, comments, external links, etc
21 | * [Exercises.md](./exercises/Exercises.md) file created to collate all the exercises in one place
22 | * Fun fact: total diff count for the markdown source file is exactly `100` for this update
23 |
24 |
25 |
26 | ### 1.0
27 |
28 | * First version
29 |
30 |
--------------------------------------------------------------------------------
/exercises/Exercises.md:
--------------------------------------------------------------------------------
1 | # Exercises
2 |
3 | * [Strings and user input](#strings-and-user-input)
4 | * [Defining functions](#defining-functions)
5 | * [Control structures](#control-structures)
6 | * [Importing and creating modules](#importing-and-creating-modules)
7 | * [Exception handling](#exception-handling)
8 | * [Debugging](#debugging)
9 | * [Testing](#testing)
10 | * [Tuple and Sequence operations](#tuple-and-sequence-operations)
11 | * [List](#list)
12 | * [Mutability](#mutability)
13 | * [Dict](#dict)
14 | * [Set](#set)
15 | * [Text processing](#text-processing)
16 | * [Comprehensions and Generator expressions](#comprehensions-and-generator-expressions)
17 | * [Dealing with files](#dealing-with-files)
18 | * [Executing external commands](#executing-external-commands)
19 | * [Command line arguments](#command-line-arguments)
20 | * [More exercises](#more-exercises)
21 |
22 |
23 |
24 | # Strings and user input
25 |
26 | * Read about the **Bytes** literal from [docs.python: String and Bytes literals](https://docs.python.org/3/reference/lexical_analysis.html#strings). See also [stackoverflow: What is the difference between a string and a byte string?](https://stackoverflow.com/q/6224052/4082052)
27 | * If you check out [docs.python: int() function](https://docs.python.org/3/library/functions.html#int), you'll see that the `int()` function accepts an optional argument. Write a program that asks the user for hexadecimal number as input. Then, use the `int()` function to convert the input string to an integer (you'll need the second argument for this). Add `5` and display the result in hexadecimal format.
28 | * Write a program to accept two input values. First can be either a number or a string value. Second is an integer value, which should be used to display the first value in centered alignment. You can use any character you prefer to surround the value, other than the default space character.
29 | * What happens if you use a combination of `r`, `f` and other such valid prefix characters while declaring a string literal? For example, `rf'a\{5/2}'`. What happens if you use the raw strings syntax and provide only a single `\` character? Does the documentation describe these cases?
30 | * Try out at least two format specifiers not discussed in this chapter.
31 | * Given `a = 5`, display `'{5}'` as the output using **f-strings**.
32 |
33 |
34 |
35 | # Defining functions
36 |
37 | * Call the function before declaration and see what happens for this program:
38 |
39 | ```ruby
40 | def greeting():
41 | print('-----------------------------')
42 | print(' Hello World ')
43 | print('-----------------------------')
44 |
45 | greeting()
46 | ```
47 |
48 | * For the program shown below, try these experiments:
49 | * add print statements for `ip`, `op_length` and `styled_line` variables at the end of the program (after the function calls)
50 | * pass a numeric value to the `greeting()` function
51 | * don't pass any argument while calling the `greeting()` function
52 |
53 | ```ruby
54 | def greeting(ip):
55 | op_length = 10 + len(ip)
56 | styled_line = '-' * op_length
57 | print(styled_line)
58 | print(f'{ip:^{op_length}}')
59 | print(styled_line)
60 |
61 | greeting('hi')
62 | weather = 'Today would be a nice, sunny day'
63 | greeting(weather)
64 | ```
65 |
66 | * Modify the script shown below as per these requirements:
67 | * what do you think will happen if you call the function as `greeting(spacing=5, ip='Oh!')`
68 | * make the spacing work for multicharacter `style` argument
69 | * accept another argument with a default value of single space character that determines the character to be used around the centered `ip` value
70 |
71 | ```ruby
72 | def greeting(ip, style='-', spacing=10):
73 | op_length = spacing + len(ip)
74 | styled_line = style * op_length
75 | print(styled_line)
76 | print(f'{ip:^{op_length}}')
77 | print(styled_line)
78 |
79 | greeting('hi')
80 | greeting('bye', spacing=5)
81 | greeting('hello', style='=')
82 | greeting('good day', ':', 2)
83 | ```
84 |
85 |
86 |
87 | # Control structures
88 |
89 | * What would happen if you use `<` or `<=` or `>` or `>=` operators between numeric and string values?
90 | * Simplify the function shown below to a single statement (ternary operator won't be needed):
91 |
92 | ```ruby
93 | def isodd(n):
94 | if n % 2:
95 | return True
96 | else:
97 | return False
98 | ```
99 |
100 | * Create this arithmetic progression `-2, 1, 4, 7, 10, 13` using the `range()` function.
101 | * What value would you get during each iteration of `for c in 'hello'`?
102 | * Rewrite the below program using a `for` loop. Can you think of a scenario where you must use a `while` loop instead of `for`?
103 |
104 | ```ruby
105 | count = int(input('Enter a positive integer: '))
106 | while count > 0:
107 | print(count)
108 | count -= 1
109 |
110 | print('Go!')
111 | ```
112 |
113 | * Use appropriate `range()` logic so that the `if` statement is no longer needed for the snippet shown below.
114 |
115 | ```ruby
116 | for num in range(10):
117 | if num % 3:
118 | continue
119 | print(f'{num} * 2 = {num * 2}')
120 | ```
121 |
122 | * If you don't already know about **FizzBuzz**, check out the problem statement on [rosettacode](https://rosettacode.org/wiki/FizzBuzz) and implement it in Python. See also [Why Can't Programmers.. Program?](https://blog.codinghorror.com/why-cant-programmers-program/)
123 | * Print all numbers from `1` to `1000` (inclusive) which reads the same in reversed form in both the binary and decimal formats. For example, `33` in decimal is `100001` in binary and both of these are palindromic. You can either implement your own logic or search online for palindrome testing in Python.
124 | * Write a function that returns the maximum nested depth of curly braces for a given string input. For example, `'{{a+2}*{{b+{c*d}}+e*d}}'` should give `4`. Unbalanced or wrongly ordered braces like `'{a}*b{'` and `'}a+b{'` should return `-1`.
125 |
126 |
127 |
128 | # Importing and creating modules
129 |
130 | * For the program shown below:
131 | * add docstrings and check the output of the `help()` function using `num_funcs`, `num_funcs.fact`, etc as arguments.
132 | * check what would be the output of `num_funcs.fact()` for negative integers and floating-point numbers. Then import the `math` built-in module and repeat the process with `math.factorial()`.
133 |
134 | ```ruby
135 | def sqr(n):
136 | return n * n
137 |
138 | def fact(n):
139 | total = 1
140 | for i in range(2, n+1):
141 | total *= i
142 | return total
143 |
144 | num = 5
145 | print(f'square of {num} is {sqr(num)}')
146 | print(f'factorial of {num} is {fact(num)}')
147 | ```
148 |
149 |
150 |
151 | # Exception handling
152 |
153 | * Identify the syntax errors in the following code snippets. Try to spot them manually.
154 |
155 | ```ruby
156 | # snippet 1:
157 | def greeting()
158 | print('hello')
159 |
160 | # snippet 2:
161 | num = 5
162 | if num = 4:
163 | print('what is going on?!')
164 |
165 | # snippet 3:
166 | greeting = “hi”
167 | ```
168 |
169 | * For the function shown below, add exception handling to gracefully process negative integers and floating-point numbers.
170 |
171 | ```ruby
172 | def fact(n):
173 | total = 1
174 | for i in range(2, n+1):
175 | total *= i
176 | return total
177 | ```
178 |
179 | * Write a function `num(ip)` that accepts a single argument and returns the corresponding integer or floating-point number contained in the argument. Only `int`, `float` and `str` should be accepted as a valid input data type. Provide custom error messages if the input cannot be converted to a valid number. Examples are shown below:
180 |
181 | ```ruby
182 | # int example
183 | >>> num(0x1f)
184 | 31
185 | # float example
186 | >>> num(3.32)
187 | 3.32
188 | # str examples
189 | >>> num(' \t 52 \t')
190 | 52
191 | >>> num('3.982e5')
192 | 398200.0
193 |
194 | # wrong data type
195 | >>> num(['1', '2.3'])
196 | TypeError: not a valid input data type
197 |
198 | # string input that cannot be converted to a valid int/float number
199 | >>> num('foo')
200 | ValueError: could not convert string to int or float
201 | ```
202 |
203 |
204 |
205 | # Debugging
206 |
207 | * Create an empty file named as `math.py`. In the same directory, create another program file that imports the `math` module and then uses some feature, `print(math.pi)` for example. What happens if you execute this program?
208 |
209 |
210 |
211 | # Testing
212 |
213 | * Randomly change the logic of the `max_nested_braces()` function shown below and see if any of the tests fail.
214 |
215 | ```ruby
216 | def max_nested_braces(expr):
217 | max_count = count = 0
218 | for char in expr:
219 | if char == '{':
220 | count += 1
221 | if count > max_count:
222 | max_count = count
223 | elif char == '}':
224 | if count == 0:
225 | return -1
226 | count -= 1
227 |
228 | if count != 0:
229 | return -1
230 | return max_count
231 |
232 | def test_cases():
233 | assert max_nested_braces('a*b') == 0
234 | assert max_nested_braces('a*b+{}') == 1
235 | assert max_nested_braces('a*{b+c}') == 1
236 | assert max_nested_braces('{a+2}*{b+c}') == 1
237 | assert max_nested_braces('a*{b+c*{e*3.14}}') == 2
238 | assert max_nested_braces('{{a+2}*{b+c}+e}') == 2
239 | assert max_nested_braces('{{a+2}*{b+{c*d}}+e}') == 3
240 | assert max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') == 4
241 | assert max_nested_braces('a*b{') == -1
242 | assert max_nested_braces('a*{b+c}}') == -1
243 | assert max_nested_braces('}a+b{') == -1
244 | assert max_nested_braces('a*{b+c*{e*3.14}}}') == -1
245 | assert max_nested_braces('{{a+2}*{{b}+{c*d}}+e*d}}') == -1
246 |
247 | if __name__ == '__main__':
248 | test_cases()
249 | print('all tests passed')
250 | ```
251 |
252 |
253 |
254 | # Tuple and Sequence operations
255 |
256 | * Given `chars = tuple('hello')`, what'd be the output of the expression `chars[0]` and the statement `chars[0] = 'H'`?
257 | * Given `primes = (2, 3, 5, 7, 11)`:
258 | * what happens if you use `primes[5]` or `primes[-6]`?
259 | * what happens if you use `primes[:5]` or `primes[-6:]`?
260 | * is it possible to get the same output as `primes[::-1]` by using an explicit number for the `stop` value? If not, why not?
261 | * What do you think will happen for these cases, given `nums = (1, 2)`:
262 | * `a, b, c = nums`
263 | * `a, *b, c = nums`
264 | * `*a, *b = nums`
265 | * Instead of the function shown below, write a custom logic that iterates only once over the input sequence and calculates both the minimum and maximum values simultaneously.
266 |
267 | ```ruby
268 | def min_max(iterable):
269 | return min(iterable), max(iterable)
270 | ```
271 |
272 | * Change the below snippet such that the index starts from `1` instead of `0`.
273 |
274 | ```ruby
275 | nums = (42, 3.14, -2)
276 | for idx, val in enumerate(nums):
277 | print(f'{idx}: {val:>5}')
278 | ```
279 |
280 | * For the program shown below:
281 | * add a default valued argument `initial` which should be used to initialize `total` instead of `0` for the `sum_nums()` function. For example, `sum_nums(3, -8)` should give `-5` and `sum_nums(1, 2, 3, 4, 5, initial=5)` should give `20`.
282 | * what would happen if you use `sum_nums(initial=5, 2)` to call this function?
283 | * what would happen if you have `nums = (1, 2)` and use `sum_nums(*nums, initial=3)` to call the function?
284 | * in what ways does this function differ from the [sum()](https://docs.python.org/3/library/functions.html#sum) built-in function?
285 |
286 | ```ruby
287 | def sum_nums(*args):
288 | total = 0
289 | for n in args:
290 | total += n
291 | return total
292 | ```
293 |
294 | * Write a function `inner_product(seq1, seq2)` that returns the sum of product of corresponding elements of the two sequences. For example, the result should be `44` for `(1, 3, 5)` and `(2, 4, 6)` passed as arguments to this function.
295 |
296 |
297 |
298 | # List
299 |
300 | * What happens if you pass an iterable to the `append()` method and a non-iterable value to the `extend()` method on a `list` object? Also, what happens if you pass multiple values to both these methods?
301 | * Check what happens if you pass a `list` value to the `insert()` method. Also, what happens if you pass more than one object?
302 | * Read [docs.python HOWTOs: Sorting](https://docs.python.org/3/howto/sorting.html) and implement the below examples using the `operator` module instead of `lambda` expressions.
303 |
304 | ```ruby
305 | # based on the second element of each item
306 | >>> items = [('bus', 10), ('car', 20), ('jeep', 3), ('cycle', 5)]
307 | >>> sorted(items, key=lambda e: e[1], reverse=True)
308 | [('car', 20), ('bus', 10), ('cycle', 5), ('jeep', 3)]
309 |
310 | # based on the number of words, assuming space as the word separator
311 | >>> dishes = ('Poha', 'Aloo tikki', 'Baati', 'Khichdi', 'Makki roti')
312 | >>> sorted(dishes, key=lambda s: s.count(' '), reverse=True)
313 | ['Aloo tikki', 'Makki roti', 'Poha', 'Baati', 'Khichdi']
314 | ```
315 |
316 | * Given `nums = [1, 4, 5, 2, 51, 3, 6, 22]`, determine and implement the sorting condition based on the required output shown below:
317 | * `[4, 2, 6, 22, 1, 5, 51, 3]`
318 | * `[2, 4, 6, 22, 1, 3, 5, 51]`
319 | * `[22, 6, 4, 2, 51, 5, 3, 1]`
320 | * For the `random.sample()` method, see what happens if you pass a slice size greater than the number of elements present in the input sequence.
321 | * Write a function that returns the product of a sequence of numbers. Empty sequence or sequence containing non-numerical values should raise `TypeError`.
322 | * `product([-4, 2.3e12, 77.23, 982, 0b101])` should give `-3.48863356e+18`
323 | * `product(range(2, 6))` should give `120`
324 | * `product(())` and `product(['a', 'b'])` should raise `TypeError`
325 | * Write a function that removes dunder names from the `dir()` output.
326 |
327 | ```ruby
328 | >>> remove_dunder(list)
329 | ['append', 'clear', 'copy', 'count', 'extend', 'index',
330 | 'insert', 'pop', 'remove', 'reverse', 'sort']
331 | >>> remove_dunder(tuple)
332 | ['count', 'index']
333 | ```
334 |
335 |
336 |
337 | # Mutability
338 |
339 | * Use the `id()` function to verify that the identity of the last two elements of the `nums_2d` variable in the below example is the same as the identity of both the elements in the `last_two` variable.
340 |
341 | ```ruby
342 | nums_2d = ([1, 3, 2, 10], [1.2, -0.2, 0, 2], [100, 200])
343 | last_two = nums_2d[-2:]
344 | ```
345 | * Create a deepcopy of only the first two elements of the `nums_2d` object shown below.
346 |
347 | ```ruby
348 | nums_2d = [[1, 3, 2, 10], [1.2, -0.2, 0, 2], [100, 200]]
349 | ```
350 |
351 |
352 |
353 | # Dict
354 |
355 | * Given `fruits = dict(banana=12, papaya=5, mango=10, fig=100)`, what do you think will happen when you use `a, *b, c = fruits`?
356 | * Given `nums = [1, 4, 6, 22, 3, 5, 4, 3, 6, 2, 1, 51, 3, 1]`, keep only the first occurrences of a value from this list without changing the order of elements. The output should be `[1, 4, 6, 22, 3, 5, 2, 51]`.
357 |
358 |
359 |
360 | # Set
361 |
362 | * Write a function that checks whether an iterable has duplicate values or not.
363 |
364 | ```ruby
365 | >>> has_duplicates('pip')
366 | True
367 | >>> has_duplicates((3, 2))
368 | False
369 | ```
370 |
371 | * What does your function return for `has_duplicates([3, 2, 3.0])`?
372 |
373 |
374 |
375 | # Text processing
376 |
377 | * Check what happens if you pass multiple string values separated by comma to the `join()` string method instead of an iterable.
378 | * Read the documentation for:
379 | * `str` methods `translate()` and `maketrans()`
380 | * built-in function `ord()`
381 | * [string module](https://docs.python.org/3/library/string.html)
382 | * From `str` documentation, go through all the methods that start with **is**.
383 | * Read the documentation for the `str` methods `rsplit()`, `partition()` and `rpartition()`
384 | * Write a function that checks if two strings are anagrams irrespective of case (assume that the input is made up of alphabets only).
385 |
386 | ```ruby
387 | >>> anagram('god', 'Dog')
388 | True
389 | >>> anagram('beat', 'table')
390 | False
391 | >>> anagram('Beat', 'abet')
392 | True
393 | ```
394 |
395 | * Read the documentation and implement these formatting examples with the equivalent `str` methods.
396 |
397 | ```ruby
398 | >>> fruit = 'apple'
399 |
400 | >>> f'{fruit:=>10}'
401 | '=====apple'
402 | >>> f'{fruit:=<10}'
403 | 'apple====='
404 | >>> f'{fruit:=^10}'
405 | '==apple==='
406 |
407 | >>> f'{fruit:^10}'
408 | ' apple '
409 | ```
410 |
411 | * Write a function that returns a `list` of words present in the input string.
412 |
413 | ```ruby
414 | >>> words('"Hi", there! How *are* you? All fine here.')
415 | ['Hi', 'there', 'How', 'are', 'you', 'All', 'fine', 'here']
416 | >>> words('This-Is-A:Colon:Separated,Phrase')
417 | ['This', 'Is', 'A', 'Colon', 'Separated', 'Phrase']
418 | ```
419 |
420 |
421 |
422 | # Comprehensions and Generator expressions
423 |
424 | * Write a function that returns a dictionary sorted by values in ascending order.
425 |
426 | ```ruby
427 | >>> marks = dict(Rahul=86, Ravi=92, Rohit=75, Rajan=79, Ram=92)
428 | >>> sort_by_value(marks)
429 | {'Rohit': 75, 'Rajan': 79, 'Rahul': 86, 'Ravi': 92, 'Ram': 92}
430 | ```
431 |
432 | * Write a function that returns a `list` of string slices as per the following rules:
433 | * return the input string as the only element if its length is less than 3 characters
434 | * otherwise, return all slices that have 2 or more characters
435 |
436 | ```ruby
437 | >>> word_slices('')
438 | ['']
439 | >>> word_slices('i')
440 | ['i']
441 | >>> word_slices('to')
442 | ['to']
443 | >>> word_slices('table')
444 | ['ta', 'tab', 'tabl', 'table', 'ab', 'abl', 'able', 'bl', 'ble', 'le']
445 | ```
446 |
447 | * Square even numbers and cube odd numbers. For example, `[321, 1, -4, 0, 5, 2]` should give `[33076161, 1, 16, 0, 125, 4]` as the output.
448 | * Calculate sum of squares of the numbers, only if the square value is less than `50`. Output for `(7.1, 1, -4, 8, 5.1, 12)` should be `43.01`.
449 |
450 |
451 |
452 | # Dealing with files
453 |
454 | * Write a program that reads a known filename `f1.txt` which contains a single column of numbers. Your task is to display the sum of these numbers, which is `10485.14` for the given example.
455 |
456 | ```bash
457 | $ cat f1.txt
458 | 8
459 | 53
460 | 3.14
461 | 84
462 | 73e2
463 | 100
464 | 2937
465 | ```
466 |
467 | * Read the documentation for `glob.glob()` and write a program to list all files ending with `.txt` in the current directory as well as sub-directories, recursively.
468 |
469 |
470 |
471 | # Executing external commands
472 |
473 | * Read the [subprocess.run documentation](https://docs.python.org/3/library/subprocess.html#subprocess.run) and modify the below `ls` example to:
474 | * redirect the `stderr` stream to `/dev/null`
475 | * automatically raise an exception when the exit status is non-zero
476 |
477 | ```ruby
478 | >>> import subprocess
479 |
480 | >>> process = subprocess.run(('ls', 'xyz.txt'))
481 | ls: cannot access 'xyz.txt': No such file or directory
482 | >>> process.returncode
483 | 2
484 | ```
485 |
486 |
487 |
488 | # Command line arguments
489 |
490 | * Modify the `sum_two_nums.py` program to handle `TypeError` exceptions as well. Instead of the output shown below, inform the user about the error using `sys.exit()` method.
491 |
492 | ```ruby
493 | # sum_two_nums.py
494 | import ast
495 | import sys
496 |
497 | try:
498 | num1, num2 = sys.argv[1:]
499 | total = ast.literal_eval(num1) + ast.literal_eval(num2)
500 | except ValueError:
501 | sys.exit('Error: Please provide exactly two numbers as arguments')
502 | else:
503 | print(f'{num1} + {num2} = {total}')
504 | ```
505 |
506 | ```bash
507 | $ python3.13 sum_two_nums.py 2 [1]
508 | Traceback (most recent call last):
509 | File "/home/learnbyexample/Python/programs/sum_two_nums.py", line 6, in
510 | total = ast.literal_eval(num1) + ast.literal_eval(num2)
511 | ~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~
512 | TypeError: unsupported operand type(s) for +: 'int' and 'list'
513 | ```
514 |
515 | * Write a program that accepts one or more numbers as CLI arguments. Calculate and display the following details about the input — sum, product and average.
516 | * Add an optional argument `-o, --output` to store the output in a file for the program shown below.
517 |
518 | ```ruby
519 | import argparse, sys
520 |
521 | parser = argparse.ArgumentParser()
522 | parser.add_argument('file', nargs='?',
523 | type=argparse.FileType('r'), default=sys.stdin,
524 | help="input file to be sorted")
525 | parser.add_argument('-u', '--unique', action='store_true',
526 | help="sort uniquely")
527 | args = parser.parse_args()
528 |
529 | ip_lines = args.file.readlines()
530 | if args.unique:
531 | ip_lines = set(ip_lines)
532 |
533 | op_lines = sorted(ip_lines, key=lambda s: (s.rsplit('.', 1)[-1], s))
534 | for line in op_lines:
535 | print(line, end='')
536 | ```
537 |
538 |
539 |
540 | # More exercises
541 |
542 | If you'd like even more exercises to test your understanding, check out these excellent resources:
543 |
544 | * [Exercism](https://exercism.org/tracks/python/exercises), [Hackinscience](https://www.hackinscience.org/exercises/) and [Practicepython](https://www.practicepython.org/) — beginner friendly
545 | * [PythonExercises](https://github.com/learnbyexample/TUI-apps/tree/main/PythonExercises) — my interactive TUI app
546 | * [Adventofcode](https://adventofcode.com/), [Codewars](https://www.codewars.com/), [Python Morsels](https://www.pythonmorsels.com/) — for intermediate to advanced level users
547 | * [Checkio](https://py.checkio.org/), [Codingame](https://www.codingame.com/start) — gaming based challenges
548 | * [/r/dailyprogrammer](https://old.reddit.com/r/dailyprogrammer) — interesting challenges
549 |
550 | See also [Python Programming Exercises, Gently Explained](https://inventwithpython.com/pythongently/) — a free ebook that includes gentle explanations of the problem, the prerequisite coding concepts you'll need to understand the solution, etc.
551 |
552 |
--------------------------------------------------------------------------------
/images/Python_shell_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/Python_shell_run.png
--------------------------------------------------------------------------------
/images/debug_in_action.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/debug_in_action.png
--------------------------------------------------------------------------------
/images/debug_window.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/debug_window.png
--------------------------------------------------------------------------------
/images/hello.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/hello.png
--------------------------------------------------------------------------------
/images/help_print.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/help_print.png
--------------------------------------------------------------------------------
/images/info.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/images/py_intro_ls.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/py_intro_ls.png
--------------------------------------------------------------------------------
/images/pypi_copy_cmd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/pypi_copy_cmd.png
--------------------------------------------------------------------------------
/images/python_exercises.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/images/python_exercises.png
--------------------------------------------------------------------------------
/images/warning.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/programs/cli_args/inplace_edit.py:
--------------------------------------------------------------------------------
1 | import fileinput
2 |
3 | with fileinput.input(inplace=True) as f:
4 | for ip_line in f:
5 | op_line = ip_line.rstrip('\n').capitalize() + '.'
6 | print(op_line)
7 |
8 |
--------------------------------------------------------------------------------
/programs/cli_args/ip.txt:
--------------------------------------------------------------------------------
1 | hi there
2 | today is sunny
3 | have a nice day
4 |
--------------------------------------------------------------------------------
/programs/cli_args/op.txt:
--------------------------------------------------------------------------------
1 | this is a sample line of text
2 | yet another line
3 |
--------------------------------------------------------------------------------
/programs/cli_args/sample.txt:
--------------------------------------------------------------------------------
1 | input.log
2 | basic.test
3 | input.log
4 | out.put.txt
5 | sync.py
6 | input.log
7 | async.txt
8 |
--------------------------------------------------------------------------------
/programs/cli_args/sort_ext.py:
--------------------------------------------------------------------------------
1 | import argparse
2 |
3 | parser = argparse.ArgumentParser()
4 | parser.add_argument('-f', '--file', required=True,
5 | help="input file to be sorted")
6 | parser.add_argument('-u', '--unique', action='store_true',
7 | help="sort uniquely")
8 | args = parser.parse_args()
9 |
10 | ip_lines = open(args.file).readlines()
11 | if args.unique:
12 | ip_lines = set(ip_lines)
13 |
14 | op_lines = sorted(ip_lines, key=lambda s: (s.rsplit('.', 1)[-1], s))
15 | for line in op_lines:
16 | print(line, end='')
17 |
--------------------------------------------------------------------------------
/programs/cli_args/sort_ext_stdin.py:
--------------------------------------------------------------------------------
1 | import argparse, sys
2 |
3 | parser = argparse.ArgumentParser()
4 | parser.add_argument('file', nargs='?',
5 | type=argparse.FileType('r'), default=sys.stdin,
6 | help="input file to be sorted")
7 | parser.add_argument('-u', '--unique', action='store_true',
8 | help="sort uniquely")
9 | args = parser.parse_args()
10 |
11 | ip_lines = args.file.readlines()
12 | if args.unique:
13 | ip_lines = set(ip_lines)
14 |
15 | op_lines = sorted(ip_lines, key=lambda s: (s.rsplit('.', 1)[-1], s))
16 | for line in op_lines:
17 | print(line, end='')
18 |
--------------------------------------------------------------------------------
/programs/cli_args/sum_two_nums.py:
--------------------------------------------------------------------------------
1 | import ast
2 | import sys
3 |
4 | try:
5 | num1, num2 = sys.argv[1:]
6 | total = ast.literal_eval(num1) + ast.literal_eval(num2)
7 | except ValueError:
8 | sys.exit('Error: Please provide exactly two numbers as arguments')
9 | else:
10 | print(f'{num1} + {num2} = {total}')
11 |
12 |
--------------------------------------------------------------------------------
/programs/control_structures/countdown.py:
--------------------------------------------------------------------------------
1 | count = int(input('Enter a positive integer: '))
2 | while count > 0:
3 | print(count)
4 | count -= 1
5 |
6 | print('Go!')
7 |
--------------------------------------------------------------------------------
/programs/control_structures/odd_even.py:
--------------------------------------------------------------------------------
1 | def isodd(n):
2 | if n % 2:
3 | return True
4 | else:
5 | return False
6 |
7 | print(f'{isodd(42) = }')
8 | print(f'{isodd(-21) = }')
9 | print(f'{isodd(123) = }')
10 |
11 | # can be simplified as shown below
12 | #def isodd(n):
13 | # return not n % 2 == 0
14 |
15 |
--------------------------------------------------------------------------------
/programs/dealing_with_files/ip.txt:
--------------------------------------------------------------------------------
1 | hi there
2 | today is sunny
3 | have a nice day
4 |
--------------------------------------------------------------------------------
/programs/dealing_with_files/read_file.py:
--------------------------------------------------------------------------------
1 | with open('ip.txt', mode='r', encoding='ascii') as f:
2 | for ip_line in f:
3 | op_line = ip_line.rstrip('\n').capitalize() + '.'
4 | print(op_line)
5 |
6 |
--------------------------------------------------------------------------------
/programs/dealing_with_files/write_file.py:
--------------------------------------------------------------------------------
1 | with open('op.txt', mode='w', encoding='ascii') as f:
2 | f.write('this is a sample line of text\n')
3 | f.write('yet another line\n')
4 |
5 |
--------------------------------------------------------------------------------
/programs/exceptions/syntax_error.py:
--------------------------------------------------------------------------------
1 | print('hello')
2 |
3 | def main():
4 | num = 5
5 | total = num + 09
6 | print(total)
7 |
8 | main)
9 |
10 |
--------------------------------------------------------------------------------
/programs/exceptions/try_except.py:
--------------------------------------------------------------------------------
1 | from math import factorial
2 |
3 | while True:
4 | try:
5 | num = int(input('Enter a positive integer: '))
6 | print(f'{num}! = {factorial(num)}')
7 | break
8 | except ValueError:
9 | print('Not a positive integer, try again')
10 |
11 |
--------------------------------------------------------------------------------
/programs/exceptions/try_except_else.py:
--------------------------------------------------------------------------------
1 | while True:
2 | try:
3 | num = int(input('Enter an integer number: '))
4 | except ValueError:
5 | print('Not an integer, try again')
6 | else:
7 | print(f'Square of {num} is {num ** 2}')
8 | break
9 |
10 |
--------------------------------------------------------------------------------
/programs/exceptions/try_except_finally.py:
--------------------------------------------------------------------------------
1 | try:
2 | num = int(input('Enter a positive integer: '))
3 | if num < 0:
4 | raise ValueError
5 | except ValueError:
6 | print('Not a positive integer, run the program again')
7 | else:
8 | print(f'Square root of {num} is {num ** 0.5:.3f}')
9 | finally:
10 | print('\nThanks for using the program, have a nice day')
11 |
12 |
--------------------------------------------------------------------------------
/programs/functions/default_args.py:
--------------------------------------------------------------------------------
1 | def greeting(ip, style='-', spacing=10):
2 | op_length = spacing + len(ip)
3 | styled_line = style * op_length
4 | print(styled_line)
5 | print(f'{ip:^{op_length}}')
6 | print(styled_line)
7 |
8 | greeting('hi')
9 | greeting('bye', spacing=5)
10 | greeting('hello', style='=')
11 | greeting('good day', ':', 2)
12 |
13 |
--------------------------------------------------------------------------------
/programs/functions/no_args.py:
--------------------------------------------------------------------------------
1 | def greeting():
2 | print('-----------------------------')
3 | print(' Hello World ')
4 | print('-----------------------------')
5 |
6 | greeting()
7 |
--------------------------------------------------------------------------------
/programs/functions/with_args.py:
--------------------------------------------------------------------------------
1 | def greeting(ip):
2 | op_length = 10 + len(ip)
3 | styled_line = '-' * op_length
4 | print(styled_line)
5 | print(f'{ip:^{op_length}}')
6 | print(styled_line)
7 |
8 | greeting('hi')
9 | weather = 'Today would be a nice, sunny day'
10 | greeting(weather)
11 |
12 |
--------------------------------------------------------------------------------
/programs/introduction/hello.py:
--------------------------------------------------------------------------------
1 | print('*************')
2 | print('Hello there!')
3 | print('*************')
4 |
--------------------------------------------------------------------------------
/programs/modules/num_funcs.py:
--------------------------------------------------------------------------------
1 | def sqr(n):
2 | return n * n
3 |
4 | def fact(n):
5 | total = 1
6 | for i in range(2, n+1):
7 | total *= i
8 | return total
9 |
10 | num = 5
11 | print(f'square of {num} is {sqr(num)}')
12 | print(f'factorial of {num} is {fact(num)}')
13 |
14 |
--------------------------------------------------------------------------------
/programs/modules/num_funcs_module.py:
--------------------------------------------------------------------------------
1 | def sqr(n):
2 | return n * n
3 |
4 | def fact(n):
5 | total = 1
6 | for i in range(2, n+1):
7 | total *= i
8 | return total
9 |
10 | if __name__ == '__main__':
11 | num = 5
12 | print(f'square of {num} is {sqr(num)}')
13 | print(f'factorial of {num} is {fact(num)}')
14 |
15 |
--------------------------------------------------------------------------------
/programs/modules/rand_number.py:
--------------------------------------------------------------------------------
1 | import random
2 |
3 | # gives back an integer between 0 and 10 (inclusive)
4 | rand_int = random.randrange(11)
5 |
6 | print('I have thought of a number between 0 and 10.')
7 | print('Can you guess it within 4 attempts?\n')
8 | for _ in range(4):
9 | guess = int(input('Enter your guess: '))
10 | if guess == rand_int:
11 | print('Wow, you guessed it right!')
12 | break
13 | elif guess > rand_int:
14 | print('Oops, your guess is too high.')
15 | else:
16 | print('Oops, your guess is too low.')
17 | else:
18 | print('\nOh no! You are out of chances. Better luck next time.')
19 |
20 |
--------------------------------------------------------------------------------
/programs/strings_user_input/triple_quotes.py:
--------------------------------------------------------------------------------
1 | print('''hi there.
2 | how are you?''')
3 |
4 | student = '''\
5 | Name:\tlearnbyexample
6 | Age:\t25
7 | Dept:\tCSE'''
8 |
9 | print(student)
10 |
--------------------------------------------------------------------------------
/programs/testing/exception_testing.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | def sum2nums(n1, n2):
4 | types_allowed = (int, float)
5 | assert type(n1) in types_allowed, 'only int/float allowed'
6 | assert type(n2) in types_allowed, 'only int/float allowed'
7 | return n1 + n2
8 |
9 | def test_valid_values():
10 | assert sum2nums(3, -2) == 1
11 | # see https://stackoverflow.com/q/5595425
12 | from math import isclose
13 | assert isclose(sum2nums(-3.14, 2), -1.14)
14 |
15 | def test_exception():
16 | with pytest.raises(AssertionError) as e:
17 | sum2nums('hi', 3)
18 | assert 'only int/float allowed' in str(e.value)
19 |
20 | with pytest.raises(AssertionError) as e:
21 | sum2nums(3.14, 'a')
22 | assert 'only int/float allowed' in str(e.value)
23 |
24 |
--------------------------------------------------------------------------------
/programs/testing/nested_braces.py:
--------------------------------------------------------------------------------
1 | def max_nested_braces(expr):
2 | max_count = count = 0
3 | for char in expr:
4 | if char == '{':
5 | count += 1
6 | if count > max_count:
7 | max_count = count
8 | elif char == '}':
9 | if count == 0:
10 | return -1
11 | count -= 1
12 |
13 | if count != 0:
14 | return -1
15 | return max_count
16 |
17 | def test_cases():
18 | assert max_nested_braces('a*b') == 0
19 | assert max_nested_braces('a*b+{}') == 1
20 | assert max_nested_braces('a*{b+c}') == 1
21 | assert max_nested_braces('{a+2}*{b+c}') == 1
22 | assert max_nested_braces('a*{b+c*{e*3.14}}') == 2
23 | assert max_nested_braces('{{a+2}*{b+c}+e}') == 2
24 | assert max_nested_braces('{{a+2}*{b+{c*d}}+e}') == 3
25 | assert max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') == 4
26 | assert max_nested_braces('a*b{') == -1
27 | assert max_nested_braces('a*{b+c}}') == -1
28 | assert max_nested_braces('}a+b{') == -1
29 | assert max_nested_braces('a*{b+c*{e*3.14}}}') == -1
30 | assert max_nested_braces('{{a+2}*{{b}+{c*d}}+e*d}}') == -1
31 |
32 | if __name__ == '__main__':
33 | test_cases()
34 | print('all tests passed')
35 |
36 |
--------------------------------------------------------------------------------
/programs/testing/nested_braces_re.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | def max_nested_braces(expr):
4 | count = 0
5 | while True:
6 | expr, no_of_subs = re.subn(r'\{[^{}]*\}', '', expr)
7 | if no_of_subs == 0:
8 | break
9 | count += 1
10 |
11 | if re.search(r'[{}]', expr):
12 | return -1
13 | return count
14 |
15 | def test_cases():
16 | assert max_nested_braces('a*b') == 0
17 | assert max_nested_braces('a*b+{}') == 1
18 | assert max_nested_braces('a*{b+c}') == 1
19 | assert max_nested_braces('{a+2}*{b+c}') == 1
20 | assert max_nested_braces('a*{b+c*{e*3.14}}') == 2
21 | assert max_nested_braces('{{a+2}*{b+c}+e}') == 2
22 | assert max_nested_braces('{{a+2}*{b+{c*d}}+e}') == 3
23 | assert max_nested_braces('{{a+2}*{{b+{c*d}}+e*d}}') == 4
24 | assert max_nested_braces('a*b{') == -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+c*{e*3.14}}}') == -1
28 | assert max_nested_braces('{{a+2}*{{b}+{c*d}}+e*d}}') == -1
29 |
30 | if __name__ == '__main__':
31 | test_cases()
32 | print('all tests passed')
33 |
34 |
--------------------------------------------------------------------------------
/sample_chapters/100_page_python_intro_sample.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/learnbyexample/100_page_python_intro/47f92dfae336a650849e5598deeb9f37e2d05996/sample_chapters/100_page_python_intro_sample.pdf
--------------------------------------------------------------------------------