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

100 Page Python Intro ebook cover image

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