├── .editorconfig
├── .github
└── PULL_REQUEST_TEMPLATE.md
├── .gitignore
├── .vscode
├── extensions.json
└── launch.json
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── algorithms
├── README.md
├── searching
│ ├── binary_search.py
│ └── linear_search.py
└── sorting
│ ├── bubble_sort.py
│ ├── insertion_sort.py
│ ├── merge_sort.py
│ ├── quick_sort.py
│ └── selection_sort.py
├── data_structures
├── README.md
├── binary_tree.py
├── doubly_linked_list.py
├── linked_list.py
├── queue.py
└── stack.py
├── design_patterns
├── README.md
├── adapter.py
├── builder.py
├── command.py
├── decorator.py
├── factory.py
└── singleton.py
├── notes
├── c01_basics
│ ├── Chapter-1.1-Basics.md
│ ├── README.md
│ ├── code
│ │ ├── c0101_hello_world.py
│ │ └── c0102_comments.py
│ └── quiz
│ │ ├── README.md
│ │ └── solution
│ │ ├── q0101.py
│ │ └── q0102.py
├── c02_basic_data_types
│ ├── Chapter 2.1 Variables.md
│ ├── Chapter 2.2 Numeric Data Types.md
│ ├── Chapter 2.3 Strings.md
│ ├── Chapter 2.4 string formatting.md
│ ├── Chapter 2.5 Operations.md
│ ├── Chapter 2.6 Typecasting.md
│ ├── README.md
│ ├── code
│ │ ├── c0201_variables.py
│ │ ├── c0202_basic_data_types.py
│ │ ├── c0203_strings.py
│ │ ├── c0204_string_formatting.py
│ │ ├── c0205_arithmetic.py
│ │ ├── c0206_relational.py
│ │ ├── c0207_logical.py
│ │ ├── c0208_identity.py
│ │ ├── c0209_membership.py
│ │ ├── c0210_bitwise.py
│ │ ├── c0211_assignment.py
│ │ └── c0212_typecasting.py
│ └── quiz
│ │ ├── README.md
│ │ └── solution
│ │ ├── q0201.py
│ │ ├── q0202.py
│ │ ├── q0203.py
│ │ ├── q0204.py
│ │ └── q0205.py
├── c03_advanced_data_types
│ ├── README.md
│ ├── chapter 3.1 list.md
│ ├── chapter 3.2 tuple.md
│ ├── chapter 3.3 dictionary.md
│ ├── chapter 3.4 set.md
│ ├── chapter 3.5 nesting.md
│ ├── code
│ │ ├── c0301_lists.py
│ │ ├── c0302_tuples.py
│ │ ├── c0303_dictionaries.py
│ │ ├── c0304_sets.py
│ │ └── c0305_type_hinting.py
│ └── quiz
│ │ ├── README.md
│ │ └── solution
│ │ ├── q0301.py
│ │ ├── q0302.py
│ │ └── q0303.py
├── c04_decision_making
│ ├── README.md
│ ├── code
│ │ ├── c0401_if.py
│ │ └── c0402_match.py
│ └── quiz
│ │ └── README.md
├── c05_loops
│ ├── Chapter 5.1 while loop.md
│ ├── Chapter 5.2 for loop.md
│ ├── Chapter 5.3 Pattern Generation.md
│ ├── Chapter 5.4 Comprehensions.md
│ ├── README.md
│ ├── code
│ │ ├── c0501_while.py
│ │ ├── c0502_for.py
│ │ ├── c0503_nested_loops.py
│ │ ├── c0504_pattern_generation.py
│ │ └── c0505_list_comprehension.py
│ └── quiz
│ │ └── README.md
├── c06_functions
│ ├── Chapter 6.1 function.md
│ ├── Chapter 6.2 default arguments.md
│ ├── Chapter 6.3 args kwargs.md
│ ├── Chapter 6.4 recursive functions.md
│ ├── Chapter 6.5 lambda.md
│ ├── README.md
│ ├── code
│ │ ├── c0601_function_basics.py
│ │ ├── c0602_default_parameters.py
│ │ ├── c0603_args_kwargs.py
│ │ ├── c0604_recursive_function.py
│ │ └── c0605_lambda.py
│ └── quiz
│ │ └── README.md
├── c07_oop
│ ├── Chapter-7.1-oop.md
│ ├── Chapter-7.2-Class-Methods-and-Static-Methods.md
│ ├── Chapter-7.3-Operator-Overloading.md
│ ├── Chapter-7.4-Encapsulation.md
│ ├── Chapter-7.5-Inheritance-and-Polymorphism.md
│ ├── README.md
│ ├── code
│ │ ├── c0701_oop_intro.py
│ │ ├── c0702_class.py
│ │ ├── c0703_classmethod_staticmethod.py
│ │ ├── c0704_encapsulation.py
│ │ ├── c0705_property.py
│ │ ├── c0706_operator_overloading.py
│ │ ├── c0707_inheritance.py
│ │ ├── c0708_multiple_inheritance.py
│ │ └── c0709_monkey_patching.py
│ ├── quiz
│ │ ├── README.md
│ │ ├── q0701.py
│ │ ├── q0702.py
│ │ ├── q0703.py
│ │ ├── q0704.py
│ │ ├── q0705.py
│ │ ├── q0706.py
│ │ ├── q0707.py
│ │ └── q0708.py
│ └── quiz_solution
│ │ ├── q0701.py
│ │ ├── q0702.py
│ │ ├── q0703.py
│ │ ├── q0704.py
│ │ ├── q0705.py
│ │ ├── q0706.py
│ │ ├── q0707.py
│ │ └── q0708.py
├── c08_modules_packages
│ ├── README.md
│ ├── chapter-8.1-modules.md
│ ├── chapter-8.2-packages.md
│ ├── chapter-8.3-datetime.md
│ ├── chapter-8.4-random.md
│ ├── chapter-8.5-json.md
│ ├── chapter-8.6-math.md
│ ├── chapter-8.7-complex-and-cmath.md
│ ├── code
│ │ ├── animal.py
│ │ ├── c0805_json_module.py
│ │ └── c08_01_module_intro.py
│ └── quiz
│ │ └── README.md
├── c09_file
│ ├── README.md
│ ├── code
│ │ ├── .gitignore
│ │ ├── reading_file.py
│ │ ├── reading_file_with.py
│ │ ├── updating_file.py
│ │ └── writing_file.py
│ └── quiz
│ │ └── README.md
├── c10_exceptions
│ ├── README.md
│ ├── chapter-10.1-exceptions.md
│ ├── chapter-10.2-exception-handling.md
│ ├── chapter-10.3-custom-exceptions.md
│ ├── code
│ │ ├── c1001_custom_exception.py
│ │ └── c1002_lentth_exception.py
│ └── quiz
│ │ └── README.md
├── c11_pip
│ ├── README.md
│ ├── chapter-11.1-semver.md
│ └── chapter-11.2-pip.md
├── c12_virtual_environment
│ ├── README.md
│ ├── chapter-12.1-virtual-environment-intro.md
│ ├── chapter-12.2-venv.md
│ ├── chapter-12.3-pipenv.md
│ └── chapter-12.4-poetry.md
├── c13_advanced_functions
│ ├── README.md
│ ├── chapter-13.1-groupby.md
│ ├── chapter-13.2-sorted.md
│ ├── chapter-13.3-filter.md
│ ├── chapter-13.4-map.md
│ ├── chapter-13.5-reduce.md
│ └── code
│ │ ├── c0501_groupby.py
│ │ ├── c0502_sorted.py
│ │ └── c0503_filter.py
├── c14_regex
│ ├── README.md
│ ├── chapter-14.1-regular-expressions.md
│ ├── chapter-14.2-regex-special-characters.md
│ ├── chapter-14.3-the-re-module.md
│ └── code
│ │ └── ch1400_regex.py
├── c15_type_hinting
│ ├── README.md
│ └── example
│ │ ├── ch1501_basic_type_hinting.py
│ │ ├── ch1502_function_hinting.py
│ │ ├── ch1503_typing_module.py
│ │ └── ch1504_advanced_hinting.py
├── c16_decorators
│ ├── README.md
│ └── code
│ │ ├── ch1401_decorator.py
│ │ ├── ch1402_decorator_factory.py
│ │ ├── ch1403_class_based_decorator.py
│ │ └── ch1404_decorating_class.py
├── c17_mixins
│ ├── README.md
│ └── example
│ │ ├── ch1701_mixin.py
│ │ └── ch1702_multiple_mixins.py
├── c18_python_http
│ ├── README.md
│ └── example
│ │ └── ch1801_http_client.py
├── c19_requests
│ └── README.md
└── c21_testing
│ ├── README.md
│ ├── chapter-21.1-intro.md
│ ├── chapter-21.2-unittest.md
│ ├── chapter-21.3-pytest.md
│ └── code
│ ├── c21_001_intro.py
│ ├── c21_002_unittest.py
│ └── c21_003_setup_teardown.py
├── poetry.lock
├── problem_solving
├── README.md
├── basic
│ ├── gcd.py
│ ├── matrix_multiplication.py
│ ├── median.py
│ ├── practical_number.py
│ └── reverse_digits.py
└── dp
│ ├── coin_change.py
│ └── fibonacci.py
├── projects
├── README.md
├── solutions
│ ├── hangman
│ │ ├── README.md
│ │ ├── game_data.py
│ │ └── hangman.py
│ ├── rock_paper_scissor
│ │ ├── README.md
│ │ └── game.py
│ └── school_manager
│ │ ├── README.md
│ │ ├── main.py
│ │ ├── school.py
│ │ └── utils.py
├── srs_hangman.md
├── srs_rock_paper_scissor.md
└── srs_school_manager.md
├── pyproject.toml
├── requirements.txt
└── uv.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | indent_size = 4
6 | indent_style = space
7 | insert_final_newline = true
8 | trim_trailing_whitespace = true
9 | max_line_length = 120
10 | tab_width = 4
11 | charset = utf-8
12 |
13 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 | # SUMMARY
3 |
4 |
5 |
6 | ## Feature Type
7 |
8 | - [ ] New Feature
9 | - [ ] Refactoring
10 | - [ ] Bug Fix
11 | - [ ] Documentation
12 | - [ ] Unit Tests
13 |
14 |
15 |
16 | ## TARGET
17 |
18 | - [ ] Note
19 | - [ ] Quiz
20 | - [ ] Algorithm
21 | - [ ] Project
22 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | temp*.py
105 |
106 | # Environments
107 | **/.env
108 | **/.venv
109 | **/env/
110 | **/venv/
111 | **/ENV/
112 | **/env.bak/
113 | **/venv.bak/
114 |
115 | # Spyder project settings
116 | .spyderproject
117 | .spyproject
118 |
119 | # Rope project settings
120 | .ropeproject
121 |
122 | # mkdocs documentation
123 | /site
124 |
125 | # mypy
126 | .mypy_cache/
127 | .dmypy.json
128 | dmypy.json
129 |
130 | # Pyre type checker
131 | .pyre/
132 |
133 | # ide settings
134 | .idea
135 |
136 | **/records/
137 | tmp/
138 |
139 | # pdf exports of markdown files
140 | course/**/*.pdf
141 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "charliermarsh.ruff",
4 | "ms-python.python",
5 | "aaron-bond.better-comments",
6 | "fill-labs.dependi",
7 | "tamasfe.even-better-toml"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 |
8 | {
9 | "name": "Python Debugger: Current File",
10 | "type": "debugpy",
11 | "request": "launch",
12 | "program": "${file}",
13 | "console": "integratedTerminal"
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # CONTRIBUTING
2 |
3 | **TABLE OF CONTENTS**:
4 |
5 | - [CONTRIBUTING](#contributing)
6 | - [Getting Started](#getting-started)
7 | - [Code of Conduct](#code-of-conduct)
8 | - [Purpose](#purpose)
9 | - [Expected Behavior](#expected-behavior)
10 | - [Prohibited Behavior](#prohibited-behavior)
11 | - [Additional Notes](#additional-notes)
12 | - [Submitting a Pull Request](#submitting-a-pull-request)
13 |
14 | ## Getting Started
15 |
16 | - Fork the repository on Github
17 | - clone your _forked repository_ to your local machine
18 | - create an appropriate branch to make changes.
19 | - Add proper message to your commits.
20 | - publish changes to the forked repository
21 | - Create a Pull Request (PR) – Go to the original repository,
22 | click "New Pull Request," and describe your changes.
23 |
24 | > **NOTE**: _Instructions on creating pull requests is explained in_
25 | > _[Submitting a Pull Request](#submitting-a-pull-request) section below._
26 |
27 | ## Code of Conduct
28 |
29 | ### Purpose
30 |
31 | This Code of Conduct outlines the expected behavior for all participants in the
32 | **_Rust Raid_** community. The goal is to foster a positive, inclusive, and
33 | respectful environment where everyone feels welcome and valued.
34 |
35 | ### Expected Behavior
36 |
37 | - **Respect**: Treat everyone with respect, regardless of their background or
38 | opinions.
39 | - **Inclusiveness**: Promote a welcoming and inclusive environment for all.
40 | - **Constructive** Communication: Engage in constructive and respectful
41 | discussions, avoiding personal attacks or insults.
42 | - **Openness**: Be open to feedback and criticism, and be willing to learn from
43 | others.
44 | - **Collaboration**: Work together towards the common goal of improving the
45 | project.
46 |
47 | ### Prohibited Behavior
48 |
49 | - **Harassment**: Any form of harassment, including but not limited to:
50 | - Sexual harassment
51 | - Discrimination
52 | - Threats
53 | - Bullying
54 | - **Hate Speech**: Language that promotes hatred or discrimination against any group of people.
55 | - **Personal Attacks**: Attacks on a person's character or abilities.
56 | - **Plagiarism**: Claiming credit for work that is not your own.
57 |
58 | ### Additional Notes
59 |
60 | > This Code of Conduct is a living document and may be updated from time to time.
61 |
62 | ## Submitting a Pull Request
63 |
64 | - Navigate to your forked repository on GitHub.
65 | - Click the "Compare & pull request" button.
66 | - Add a descriptive title and provide details about your changes. You can see the PR template at `.github/PULL_REQUEST_TEMPLATE.md` file.
67 | - Submit the pull request.
68 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 Sudip Ghimire
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 |
--------------------------------------------------------------------------------
/algorithms/README.md:
--------------------------------------------------------------------------------
1 | # Algorithms
2 |
3 | This module contains some common sorting algorithms that can be implemented
4 | using python programming language
5 |
6 | **Note**:
7 | _All examples might not be optimum for production use since we will try to_
8 | _make it as simpler as possible to understand the basic concept in each topic._
9 |
10 |
11 | ## Contents
12 |
13 | ### Searching Algorithms
14 | - [Linear Search](searching/linear_search.py)
15 | - [Binary Search](searching/binary_search.py)
16 |
17 | ### Sorting Algorithms
18 | - [Bubble Sort](sorting/bubble_sort.py)
19 | - [Insertion Sort](sorting/insertion_sort.py)
20 | - [Quick Sort](sorting/quick_sort.py)
21 | - [Selection Sort](sorting/selection_sort.py)
22 | - [Merge Sort](sorting/merge_sort.py)
23 |
--------------------------------------------------------------------------------
/algorithms/searching/binary_search.py:
--------------------------------------------------------------------------------
1 | from typing import TypeVar
2 |
3 |
4 | def binary_search(sorted_list: list, item: int) -> int | None:
5 | """# Binary Search
6 | Binary Searching algorithm uses divide and conquer method to recursively find
7 | out elements of the item.
8 |
9 | It first compares middle most item of an array and recursively divides the
10 | array into sub-arrays virtually until it finds out the exact element.
11 |
12 | It works recursively and non-recursively, but as recursive functions are not
13 | good for larger arrays, we implemented while loop.
14 |
15 | The Time complexity of a binary search is O(log(n))
16 |
17 | ## NOTE:
18 | The binary search works only on the sorted array. If an array is not sorted,
19 | we must sort it before we implement this algorithm.
20 |
21 | We can use python's sort() method to sort the list before feeding it to this
22 | function for now.
23 | If you want to learn more about sorting algorithm, you can check
24 | `src/algorithms/sorting/`.
25 |
26 | ## Example,
27 |
28 | we have an array [1, 3, 4, 6, 7, 8, 9] and we want to find out an index of 7
29 |
30 | - step 1: compare the middle most item with 7 [6 < 7]
31 | - step 2: split an array and take the right slice [7 8 9] (take left if the comparison was different)
32 | - step 3: compare the middle most item of [7 8 9], i.e [8 > 7] (take the left slice)
33 | - step 4: compare the middle most item fo [7]: i.e [7 == 7]; return index of that number
34 | """
35 | #
36 | left = 0
37 | right = sorted_list.__len__() - 1
38 |
39 | while left < right:
40 | mid = (left + right) // 2
41 | mid_item = sorted_list[mid]
42 | print(f"index: {mid}, item:{sorted_list[mid]}")
43 | if mid_item == item:
44 | return mid
45 | elif mid_item < item:
46 | left = mid + 1
47 | else:
48 | right = mid - 1
49 | return None
50 |
51 |
52 | if __name__ == "__main__":
53 | sorted_list = [1, 4, 7, 8, 9, 10, 11, 12, 15, 20]
54 | print(f"list: {sorted_list}")
55 | item = int(input("Enter item to search: "))
56 | if index := binary_search(sorted_list, item):
57 | print(f'The item "{item}" is at index {index}')
58 | else:
59 | print(f'The item "{item}" does not exist in the list')
60 |
--------------------------------------------------------------------------------
/algorithms/searching/linear_search.py:
--------------------------------------------------------------------------------
1 | def linear_search(data, item) -> int | None:
2 | """# Linear Search
3 |
4 | Linear searching algorithm is a sequential searching algorithm, where we
5 | traverse through each item by index and compare every element to the desired
6 | element and return the index of the element.
7 |
8 | For a linear search, we do not need the list/array to be sorted since we
9 | check each item one by one.
10 |
11 | The Time complexity of the linear search is O(n)
12 |
13 | ## Example:
14 |
15 | Given array: [1, 5, 9, 2, 4], we want to search 9.
16 | - step 1: check first item (index = 0), item = 1 , != 9
17 | - step 2: check second item (index = 1), item = 5 , != 9
18 | - step 3: check third item (index = 2), item = 9 , == 9 [return index : 2]
19 | """
20 | for index, _item in enumerate(data):
21 | if _item == item:
22 | return index
23 | return None
24 |
25 |
26 | if __name__ == "__main__":
27 | data = [1, 4, 7, 8, 9, 10, 11, 12, 15, 20]
28 | print(f"list: {data}")
29 | item = int(input("Enter item to search: "))
30 | if index := linear_search(data, item):
31 | print(f'The item "{item}" is at index {index}')
32 | else:
33 | print(f'The item "{item}" does not exist in the list')
34 |
--------------------------------------------------------------------------------
/algorithms/sorting/bubble_sort.py:
--------------------------------------------------------------------------------
1 | def bubble_sort(unsorted: list):
2 | """
3 | # Bubble Sorting Algorithm
4 |
5 | Bubble sorting technique compares and swaps value one by one by traversing
6 | through the list.
7 |
8 | If there are no swaps, the sorting is complete.
9 |
10 | ## example:
11 |
12 | ### step 1
13 | - (4 2) 9 0 7 5 1 <- Swap 4 and 2
14 | - 2 (4 9) 0 7 5 1
15 | - 2 4 (9 0) 7 5 1 <- Swap 9 and 0
16 | - 2 4 0 (9 7) 5 1 <- Swap 9 and 7
17 | - 2 4 0 7 (9 5) 1 <- Swap 9 and 5
18 | - 2 4 0 7 5 (9 1) <- Swap 9 and 1
19 | - 2 4 0 7 5 1 [9] <- [9] is sorted
20 |
21 | ### Step 2 : 9 is sorted so sort remaining
22 |
23 | - (2 4) 0 7 5 1 [9]
24 | - 2 (4 0) 7 5 1 [9] <- sort 4 and 0
25 | - ...
26 | - 2 0 4 5 1 [7 9] <- [7, 9] are sorted
27 |
28 | * do this process until every elements are sorted
29 | """
30 | for i in range(0, unsorted.__len__()):
31 | swapped = False
32 | for j in range(0, unsorted.__len__() - i - 1):
33 | if unsorted[j] > unsorted[j + 1]:
34 | unsorted[j], unsorted[j + 1] = unsorted[j + 1], unsorted[j]
35 | swapped = True
36 | # If no items swapped in the internal loop, then all items are sorted
37 | # and there is no longer need to continue the loop.
38 | if not swapped:
39 | break
40 |
41 |
42 | if __name__ == "__main__":
43 | my_list = [4, 2, 9, 0, 7, 5, 1]
44 | print(f"Unsorted: {my_list}")
45 | bubble_sort(my_list)
46 | print(f"Sorted: {my_list}")
47 |
--------------------------------------------------------------------------------
/algorithms/sorting/insertion_sort.py:
--------------------------------------------------------------------------------
1 | def insertion_sort(unsorted: list):
2 | """
3 | # Insertion Sorting Algorithm
4 |
5 | Bubble sorting technique compares and swaps value one by one by traversing
6 | Insertion Sort is the sorting algorithm, that virtually creates sub-arrays
7 | of sorted and unsorted elements. Values from unsorted part are picked and
8 | inserted at the correct position of the sorted element.
9 |
10 | Initially, first 2 elements of an array are compared and sorted if needed.
11 | after that, second item is compared with the third element and sorted if
12 | necessary (which may need sorting again within the sorted sub-array again)
13 |
14 | ## For example:
15 |
16 | * `[ 4 2 9 0 7 5 1]`
17 |
18 | * [4 2] [9 0 7 5 1] <- Sort 4 and 2 and add it to the sub array
19 | * ^ ^
20 | * [2 4] [9 0 7 5 1]
21 | * ^ ^
22 | * [2 4 9] [0 7 5 1] <- Compare 4 and 9 as they do not need swapping, keep at is it
23 | * ^ ^
24 | * [2 4 0 9] [7 5 1] <- Compare and sort 9 and 0 (the sorted sub-array again needs sorting)
25 | * [2 0 4 9] [7 5 1] <- Compare and sort 4 and 0 (the sorted sub-array again needs sorting)
26 | * [0 2 4 9] [7 5 1] <- Compare and sort 2 and 0 (the sorted sub-array again needs sorting)
27 | * ...
28 | insert and sort until all items in an array is sorted
29 | """
30 | # start from second index since we need at least 2 items in a sub-array ex: [0, 1]
31 | for index in range(1, unsorted.__len__()):
32 | # 1 new item is added at last to the sorted sub-array to check it so, we
33 | # start checking it from reverse.
34 | # if it is not sorted, we swap item with the previous index
35 | for pointer in range(index, 0, -1):
36 | if unsorted[pointer] > unsorted[pointer - 1]:
37 | break
38 | unsorted[pointer], unsorted[pointer - 1] = (
39 | unsorted[pointer - 1],
40 | unsorted[pointer],
41 | )
42 |
43 |
44 | if __name__ == "__main__":
45 | my_list = [4, 2, 9, 0, 7, 5, 1]
46 | print(f"Unsorted: {my_list}")
47 | insertion_sort(my_list)
48 | print(f"Sorted: {my_list}")
49 |
--------------------------------------------------------------------------------
/algorithms/sorting/merge_sort.py:
--------------------------------------------------------------------------------
1 | def merge(left: list, right: list):
2 | merged = []
3 |
4 | while True:
5 | left_exhausted = not left.__len__()
6 | right_exhausted = not right.__len__()
7 | # print("⛔ left: ", left, "right: ", right, "Merged: ", merged)
8 |
9 | if left_exhausted and right_exhausted:
10 | break
11 |
12 | if left_exhausted:
13 | merged.append(right.pop(0))
14 |
15 | elif right_exhausted:
16 | merged.append(left.pop(0))
17 |
18 | elif left[0] < right[0]:
19 | merged.append(left.pop(0))
20 | else:
21 | merged.append(right.pop(0))
22 |
23 | return merged
24 |
25 |
26 | def merge_sort(data: list):
27 | """
28 | # Merge Sort
29 | ------------
30 |
31 | Merge sort is an efficient sorting algorithm, which works in a divide and
32 | conquer method. It first splits the sub-array until single element is left
33 | in each split, after that, it starts merging all one by one.
34 |
35 | Example:
36 | We have an array [ 7, 3 10, 2], we start splitting it in the middle until
37 | we get single element in an array
38 |
39 | * STEP 1(split): [ 7, 3] [10, 2]
40 | * STEP 2(further split): [7] [3] [10] [2]
41 | we have single element, so we start merging arrays by sorting it
42 | At this point, we compare each element and select smallest to put at index 0
43 | for this we start comparing each item at index 0 since it is the smallest in
44 | each array.
45 | after that, we put another element by comparing
46 |
47 | * STEP 3(merge singles): [3, 7] [2, 10]
48 | ... repeat until the whole array is sorted
49 | * STEP 4(merge doubles): [2, 3, 7, 10]
50 | ...
51 |
52 | * Time complexity of Merge sort is O(n log(n) )
53 |
54 | *Tradeoffs:
55 | - It has more space complexity since it requires storage to store temporary
56 | results
57 | - It requires copying sub arrays from the original array since we can not
58 | directly swap elements in place.
59 | """
60 |
61 | if data.__len__() <= 1:
62 | return data
63 |
64 | mid_ptr = data.__len__() // 2
65 | # split and merge
66 | left, right = data[:mid_ptr], data[mid_ptr:]
67 | return merge(merge_sort(left), merge_sort(right))
68 |
69 |
70 | if __name__ == "__main__":
71 | unsorted = [4, 2, 9, 0, 7, 5, 1]
72 | sorted = merge_sort(unsorted)
73 | print("Unsorted Data: ", unsorted)
74 | print("Sorted Data: ", sorted)
75 |
--------------------------------------------------------------------------------
/algorithms/sorting/quick_sort.py:
--------------------------------------------------------------------------------
1 | def quick_sort(unsorted: list):
2 | """
3 | `Quick sort` is an efficient algorithm that follows divide-and-conquer
4 | approach
5 |
6 | Quick sorting process consists of the following steps:
7 |
8 | 1. Pivot selection : Choose pivot point/element from the array
9 | 2. Partitioning : Rearrange the array
10 | 3. Recursion : Split and select pivot point until sub-array contains single element
11 | 4. Combination : Rearrange and combine all single items
12 | """
13 | if len(unsorted) <= 1:
14 | return unsorted # Base case: already sorted
15 |
16 | # Choose the middle element as a pivot element
17 | pivot = unsorted[len(unsorted) // 2]
18 |
19 | # partitioning
20 | left, middle, right = [], [], []
21 |
22 | for element in unsorted:
23 | if element < pivot:
24 | # add all elements to the left that are smaller than pivot
25 | left.append(element)
26 | elif element > pivot:
27 | # add all elements to the right that are greater than pivot
28 | right.append(element)
29 | else:
30 | # if element is same as pivot element, add it to the middle
31 | middle.append(pivot)
32 |
33 | # recursively perform sorting and merge all left to right partitions
34 | return [*quick_sort(left), *middle, *quick_sort(right)]
35 |
36 |
37 | if __name__ == "__main__":
38 | my_list = [4, 2, 9, 0, 7, 5, 1]
39 | print(f"Unsorted: {my_list}")
40 | sorted = quick_sort(my_list)
41 | print(f"Sorted: {sorted}")
42 |
--------------------------------------------------------------------------------
/algorithms/sorting/selection_sort.py:
--------------------------------------------------------------------------------
1 | def selection_sort(unsorted: list):
2 | """
3 | # Selection Sort
4 |
5 | Selection sort is another sorting algorithm, which compares smallest of all
6 | values and and put it in the first position. Similarly, after swapping value,
7 | it will find out second most smallest value and swap it with second item in
8 | the array.
9 |
10 | Unlike bubble sort, the sorting will be done from the beginning.
11 |
12 | ## Example:
13 |
14 | - 4 2 9 0 7 5 1 <- Swap 4 and 0
15 | - ^ ^
16 | - [0] 2 9 4 7 5 1 <- 0 is sorted
17 | - [0] 2 9 4 7 5 1 <- Swap 2 and 1
18 | - ^ ^
19 | -...
20 | Swap values until all values are sorted
21 | """
22 | for index in range(0, unsorted.__len__()):
23 | smallest = index
24 | for target in range(index, unsorted.__len__()):
25 | if unsorted[smallest] > unsorted[target]:
26 | smallest = target
27 | if smallest != index:
28 | unsorted[index], unsorted[smallest] = (
29 | unsorted[smallest],
30 | unsorted[index],
31 | )
32 |
33 |
34 | if __name__ == "__main__":
35 | my_list = [4, 2, 9, 0, 7, 5, 1]
36 | print(f"Unsorted: {my_list}")
37 | selection_sort(my_list)
38 | print(f"Sorted: {my_list}")
39 |
--------------------------------------------------------------------------------
/data_structures/README.md:
--------------------------------------------------------------------------------
1 | # Data Structures
2 |
3 | This module contains some basic and complex Data structures that can be
4 | implemented using python.
5 |
6 | **Note**:
7 | _All examples might not be optimum for production use since we will try to_
8 | _make it as simpler as possible to understand the basic concept in each topic._
9 |
10 | ## Contents
11 |
12 | - [Linked List](linked_list.py)
13 | - [Doubly Linked List](doubly_linked_list.py)
14 | - [Binary Tree](binary_tree.py)
15 | - [Queue](queue.py)
16 | - [Stack](stack.py)
17 |
--------------------------------------------------------------------------------
/data_structures/binary_tree.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Literal
2 |
3 | TraverseOrder = Literal["pre", "in", "post"]
4 |
5 |
6 | class Node:
7 | value: Any
8 | left: "Node | None"
9 | right: "Node | None"
10 |
11 | def __init__(self, value) -> None:
12 | self.value = value
13 | self.left = None
14 | self.right = None
15 |
16 | @property
17 | def size(self):
18 | size = 1
19 | if self.left:
20 | size += self.left.size
21 | if self.right:
22 | size += self.right.size
23 | return size
24 |
25 | def traverse(self, order: TraverseOrder = "in"):
26 | left = self.left.traverse(order) if self.left else None
27 | right = self.right.traverse(order) if self.right else None
28 |
29 | data = f"{self.value}"
30 | match order:
31 | case "pre":
32 | if left:
33 | data = f"{data} -> ({left})"
34 | if right:
35 | data = f"{data} --> ({right})"
36 | case "post":
37 | if right:
38 | data = f"({right}) <- {data}"
39 | if left:
40 | data = f"({left}) <-- {data}"
41 | case _:
42 | if left:
43 | data = f"({left}) <- {data}"
44 | if right:
45 | data = f"{data} -> ({right})"
46 | return data
47 |
48 |
49 | class BinaryTree:
50 | """
51 | A Binary tree is a data type in which a parent node can have at most 2
52 | children. Each node contains 3 different items:
53 |
54 | 1. data
55 | 2. Reference to the left child
56 | 2. Reference to the right child
57 |
58 | The basic methods in queue are:
59 |
60 | - length
61 | - enqueue
62 | - dequeue
63 |
64 | """
65 |
66 | def __init__(self, root: Node) -> None:
67 | self.root = root
68 |
69 | @property
70 | def size(self):
71 | return self.root.size
72 |
73 | def traverse(self, order: TraverseOrder = "in"):
74 | return self.root.traverse(order)
75 |
76 |
77 | """
78 | Visualization
79 | 5
80 |
81 | 51 52
82 |
83 | 511 512 521 522
84 |
85 | """
86 |
87 | if __name__ == "__main__":
88 | tree = BinaryTree(Node(5))
89 | tree.root.left = Node(51)
90 | tree.root.right = Node(52)
91 | tree.root.left.left = Node(511)
92 | tree.root.left.right = Node(512)
93 | tree.root.right.left = Node(521)
94 | tree.root.right.right = Node(522)
95 |
96 | print(f"Size of a tree: {tree.size}")
97 | print(f"tree [in order]: {tree.traverse()}")
98 | print(f"tree [pre order]: {tree.traverse('pre')}")
99 | print(f"tree [post order]: {tree.traverse('post')}")
100 |
101 | # output:
102 |
103 | # Size of a tree: 7
104 | # tree [in order]: ((511) <- 51 -> (512)) <- 5 -> ((521) <- 52 -> (522))
105 | # tree [pre order]: 5 -> (51 -> (511) --> (512)) --> (52 -> (521) --> (522))
106 | # tree [post order]: ((511) <-- (512) <- 51) <-- ((521) <-- (522) <- 52) <- 5
107 |
--------------------------------------------------------------------------------
/data_structures/linked_list.py:
--------------------------------------------------------------------------------
1 | class Node:
2 | next: "Node"
3 |
4 | def __init__(self, data) -> None:
5 | self.data = data
6 | self.next = None # type: ignore
7 |
8 | def push(self, node: "Node"):
9 | if self.length > 1:
10 | self.next.push(node)
11 | else:
12 | self.next = node
13 |
14 | def pop(self):
15 | match self.length:
16 | case 1:
17 | return None
18 | case 2:
19 | popped = self.next
20 | self.next = None # type: ignore
21 | return popped
22 | case _:
23 | return self.next.pop()
24 |
25 | @property
26 | def length(self):
27 | if self.next is not None:
28 | return 1 + self.next.length
29 | return 1
30 |
31 | def __str__(self) -> str:
32 | if self.length > 1:
33 | return f"Node({self.data}) -> {self.next}"
34 | else:
35 | return f"Node({self.data})"
36 |
37 | def __repr__(self) -> str:
38 | return self.__str__()
39 |
40 |
41 | class LinkedList:
42 | head: Node
43 |
44 | def __init__(self, head: Node) -> None:
45 | self.head = head
46 |
47 | @property
48 | def length(self):
49 | if self.head is not None:
50 | return self.head.length
51 | return 0
52 |
53 | def push(self, node: Node):
54 | if self.head is None:
55 | self.head = node
56 | else:
57 | self.head.push(node)
58 |
59 | def pop(self):
60 | match self.length:
61 | case 0:
62 | raise IndexError("The linked list is Empty")
63 | case 1:
64 | popped = self.head
65 | self.head = None # type: ignore
66 | return popped
67 | case _:
68 | return self.head.pop()
69 |
70 | def __str__(self) -> str:
71 | if self.head is None:
72 | return "LinkedList([])"
73 | return f"LinkedList([ {self.head} ])"
74 |
75 | def __repr__(self) -> str:
76 | return self.__str__()
77 |
78 |
79 | if __name__ == "__main__":
80 | linked = LinkedList(Node(5))
81 | linked.push(Node(2))
82 | linked.push(Node(7))
83 | linked.push(Node(3))
84 | linked.push(Node(8))
85 | print("Linked List:", linked)
86 | print(f"Popped: {linked.pop()}") # Node(8) is popped out
87 | print(f"Popped: {linked.pop()}") # Node(3) is popped out
88 | print(f"Popped: {linked.pop()}") # Node(7) is popped out
89 | print(f"Popped: {linked.pop()}") # Node(2) is popped out
90 | print(f"Popped: {linked.pop()}") # Node(5) is popped out
91 |
92 | try:
93 | linked.pop()
94 | except IndexError as e:
95 | print(f"error: {e}") # The linked list is Empty
96 |
--------------------------------------------------------------------------------
/data_structures/queue.py:
--------------------------------------------------------------------------------
1 | class Queue:
2 | """
3 | A queue is a linear data type that follows First-In First-Out (FIFO) pattern.
4 |
5 | There is already a built-in queue data type in python, however for
6 | demonstration purpose, we create a Queue data type in python using a list.
7 |
8 | The basic methods in queue are:
9 |
10 | - length
11 | - enqueue
12 | - dequeue
13 |
14 | """
15 |
16 | def __init__(self):
17 | self.__data = []
18 |
19 | def __str__(self) -> str:
20 | return f"Queue({self.__data})"
21 |
22 | @property
23 | def length(self):
24 | return self.__data.__len__()
25 |
26 | def enqueue(self, data):
27 | self.__data.insert(0, data)
28 |
29 | def dequeue(self):
30 | return self.__data.pop()
31 |
32 |
33 | if __name__ == "__main__":
34 | queue = Queue()
35 | queue.enqueue(1) # 1
36 | queue.enqueue(2) # 2, 1
37 | queue.enqueue(3) # 3,2,1
38 | queue.enqueue(4) # 4,3,2,1
39 |
40 | print(f"Queue: {queue}")
41 | print(f"length of queue: {queue.length}")
42 |
43 | print(f"dequeue: {queue.dequeue()}") # 1 is popped
44 | print(f"dequeue: {queue.dequeue()}") # 2 is popped
45 | print(f"dequeue: {queue.dequeue()}") # 3 is popped
46 |
--------------------------------------------------------------------------------
/data_structures/stack.py:
--------------------------------------------------------------------------------
1 | class Stack:
2 | """
3 | Stack is a linear data type that follows Last-In First-Out (LIFO) pattern.
4 |
5 | We can think a stack as a stack of biscuits on a packet or a stack of plates
6 | in the kitchen. The last item to add to the top of the stack will be taken
7 | out first to be used.
8 |
9 | Another good example of stack is a gift box with multiple wraps. The wrap
10 | that we wrapped last is always unwrapped first.
11 |
12 | For demonstration purpose, we create a Stack data type in python using a list.
13 |
14 | The basic methods in stack are:
15 |
16 | - length
17 | - push: add item to the stack
18 | - pop: remove item from the stack
19 | - peek: see the item on the top of the stack
20 |
21 | """
22 |
23 | def __init__(self):
24 | self.__data = []
25 |
26 | def __str__(self) -> str:
27 | return f"Stack({self.__data})"
28 |
29 | @property
30 | def length(self):
31 | return self.__data.__len__()
32 |
33 | def peek(self):
34 | return self.__data[-1]
35 |
36 | def push(self, data):
37 | self.__data.append(data)
38 |
39 | def pop(self):
40 | return self.__data.pop()
41 |
42 |
43 | if __name__ == "__main__":
44 | stack = Stack()
45 | stack.push(1) # 1
46 | stack.push(2) # 1, 2
47 | stack.push(3) # 1, 2, 3
48 | stack.push(4) # 1, 2, 3, 4
49 |
50 | print(f"Stack: {stack}")
51 | print(f"Peek: {stack.peek()}") # The last item to push is peek first from the stack
52 | print(f"length of the Stack: {stack.length}")
53 |
54 | # Similarly, the first item to be popped from the stack is the last item to be pushed
55 | print(f"pop: {stack.pop()}") # 4 is popped
56 | print(f"pop: {stack.pop()}") # 3 is popped
57 | print(f"pop: {stack.pop()}") # 2 is popped
58 |
--------------------------------------------------------------------------------
/design_patterns/README.md:
--------------------------------------------------------------------------------
1 | # Design patterns
2 |
3 | This module contains Some of the popular design patterns that can be implemented
4 | in Python Programming language.
5 |
6 | **Note**:
7 | _All examples might not be optimum for production use since we will try to_
8 | _make it as simpler as possible to understand the basic concept in each topic._
9 |
10 |
11 | ## Contents
12 |
13 | - [Adapter Pattern](adapter.py)
14 | - [Builder Pattern](builder.py)
15 | - [Command Pattern](command.py)
16 | - [Decorator Pattern](decorator.py)
17 | - [Factory Pattern](factory.py)
18 | - Observer Pattern
19 | - [Singleton Pattern](singleton.py)
20 | - Strategy Pattern
21 |
--------------------------------------------------------------------------------
/design_patterns/adapter.py:
--------------------------------------------------------------------------------
1 | """
2 | An adapter design pattern is a structural design pattern that allows different
3 | and incompatible objects to work together
4 |
5 | A good example of an adapter design pattern is using different parsers with
6 | completely incompatible methods and calls.
7 |
8 | The example below uses JsonAdapter and XmlAdapter to adapt the `parse` method.
9 | """
10 |
11 | from typing import Any
12 | from xml.etree import ElementTree
13 | import json
14 |
15 |
16 | class JsonParser:
17 | def load_json(self, data: str):
18 | return json.loads(data)
19 |
20 |
21 | class XmlParser:
22 | def parse_xml(self, data: str):
23 | root = ElementTree.fromstring(data)
24 | # root = tree.getroot()
25 | return {child.tag: child.text for child in root}
26 |
27 |
28 | # Adapter Interface
29 | class ParserAdapter:
30 | parser: Any
31 |
32 | def parse(self, data: str):
33 | raise NotImplementedError
34 |
35 |
36 | # Adapters for each parsers
37 | class JSONAdapter(ParserAdapter):
38 | def __init__(self, json_parser):
39 | self.parser = json_parser
40 |
41 | def parse(self, data):
42 | return self.parser.load_json(data)
43 |
44 |
45 | class XMLAdapter(ParserAdapter):
46 | def __init__(self, xml_parser):
47 | self.parser = xml_parser
48 |
49 | def parse(self, data):
50 | return self.parser.parse_xml(data)
51 |
52 |
53 | if __name__ == "__main__":
54 | # defining an adapter with different parser
55 | json_parser = JSONAdapter(JsonParser())
56 | xml_parser = XMLAdapter(XmlParser())
57 |
58 | json_parsed_dict = json_parser.parse('{"name": "John Doe", "age":20}')
59 | print(json_parsed_dict) # {'name': 'John Doe', 'age': 20}
60 |
61 | xml_parsed_dict = xml_parser.parse("John Doe20")
62 | print(xml_parsed_dict) # {'name': 'John Doe', 'age': '20'}
63 |
--------------------------------------------------------------------------------
/design_patterns/builder.py:
--------------------------------------------------------------------------------
1 | """
2 | Builder Design Pattern
3 | -----------------------
4 |
5 | A builder design pattern is a creation design pattern that separates the object
6 | construction process in different scenarios.
7 |
8 | You can think a builder pattern as a regular PC builder in which different
9 | components are attached when required. For different user, same brand PC might
10 | have different specifications.
11 | """
12 |
13 | from typing import Any, Literal
14 |
15 |
16 | class PC:
17 | def __init__(self) -> None:
18 | self.processor: str | None = None
19 | self.memory = 0
20 | self.storage = 0
21 |
22 | def __str__(self) -> str:
23 | return f"PC with {self.processor} processor ({self.memory}GB RAM, {self.storage}GB HDD)"
24 |
25 |
26 | class PCBuilder:
27 | def __init__(self) -> None:
28 | self.pc = PC()
29 |
30 | def set_processor(self, processor: Literal["amd", "intel", "arm"]):
31 | self.pc.processor = processor
32 | return self
33 |
34 | def set_memory(self, value: int):
35 | self.pc.memory = value
36 | return self
37 |
38 | def set_storage(self, value: int):
39 | self.pc.storage = value
40 | return self
41 |
42 | def build(self):
43 | return self.pc
44 |
45 |
46 | if __name__ == "__main__":
47 | builder = PCBuilder()
48 |
49 | gaming_pc = (
50 | builder.set_processor("amd") # set AMD processor
51 | .set_memory(32)
52 | .set_storage(1024)
53 | .build()
54 | )
55 |
56 | print("Gaming PC: ", gaming_pc) # PC with amd processor (32GB RAM, 1024GB HDD)
57 |
58 | mobile_pc = (
59 | builder.set_processor("arm") # set ARM processor
60 | .set_memory(value=8)
61 | .set_storage(256)
62 | .build()
63 | )
64 | print("Mobile PC: ", mobile_pc) # PC with arm processor (8GB RAM, 256GB HDD)
65 |
--------------------------------------------------------------------------------
/design_patterns/decorator.py:
--------------------------------------------------------------------------------
1 | """
2 | A decorator design pattern is a structural design pattern that allows to
3 | dynamically attach new behavior to the existing object without changing the
4 | original implementation.
5 |
6 | A decorator creates a wrapper around the original implementation so that it can
7 | be reused in another implementation too.
8 |
9 | We can implement decorator pattern where we want some conditional results such
10 | as displaying logs while debugging.
11 | """
12 |
13 |
14 | class User:
15 | def __init__(self, name, age):
16 | self.name = name
17 | self.age = age
18 |
19 |
20 | class PrivateProfileDecorator:
21 | def __init__(self, user: User) -> None:
22 | self.user = user
23 |
24 | def display(self):
25 | print(f"[Private Profile] Name: {self.user.name}")
26 |
27 |
28 | class PublicProfileDecorator:
29 | def __init__(self, user: User) -> None:
30 | self.user = user
31 |
32 | def display(self):
33 | print(f"[Public Profile] Name: {self.user.name}, age: {self.user.age}")
34 |
35 |
36 | """
37 | In python, we can also use functional decorators to work on a decorator pattern.
38 | A basic logger with decorator design pattern is as follows:
39 | """
40 |
41 |
42 | def logger(func):
43 | def __inner(*args, **kwargs):
44 | print(f"[ LOG ] calling function [{func.__name__}]")
45 | return func(*args, **kwargs)
46 |
47 | return __inner
48 |
49 |
50 | @logger
51 | def add(x: int, y: int):
52 | print(f"The sum is: {x+y}")
53 |
54 |
55 | if __name__ == "__main__":
56 | user = User("John Doe", 20)
57 |
58 | private = PrivateProfileDecorator(user)
59 | private.display()
60 |
61 | public = PublicProfileDecorator(user)
62 | public.display()
63 |
64 | # logger decorated add function
65 | add(5, 10)
66 | """
67 | OUTPUT:
68 |
69 | [Private Profile] Name: John Doe
70 | [Public Profile] Name: John Doe, age: 20
71 |
72 | [ LOG ] calling function [add]
73 | The sum is: 15
74 | """
75 |
--------------------------------------------------------------------------------
/design_patterns/factory.py:
--------------------------------------------------------------------------------
1 | """
2 | A Factory Design Pattern is a design pattern that allows user to instantiate
3 | different object based on the constraints. In this design pattern a factory
4 |
5 | The components of Factory pattern are as follows:
6 |
7 | 1. client: part of code that creates object
8 | 2. Factory: interface that defines methods for a client code
9 | 3. Product: it is an instance that factory creates
10 |
11 | """
12 |
13 | from abc import ABC, abstractmethod
14 | from typing import Literal
15 |
16 |
17 | class PaymentGateway(ABC):
18 | """
19 | The PaymentGateway class is an abstract class that provides blueprint for
20 | all the payment methods.
21 | """
22 |
23 | @abstractmethod
24 | def pay(self, amount: float):
25 | raise NotImplementedError()
26 |
27 |
28 | class Paypal(PaymentGateway):
29 | def pay(self, amount: float):
30 | print(f"Paid '${amount}' with paypal")
31 |
32 |
33 | class Stripe(PaymentGateway):
34 | def pay(self, amount: float):
35 | print(f"Paid '${amount}' with stripe")
36 |
37 |
38 | class PaymentGatewayFactory:
39 | """
40 | This is a factory class that provides the interface to instantiate a paypal
41 | or stripe object depending on the parameter of the `create` method.
42 |
43 | The create method returns the initialized object of the proper
44 | """
45 |
46 | @classmethod
47 | def create(cls, method: Literal["paypal", "stripe"]):
48 | """
49 | This method is a client code that initializes the respective object.
50 | """
51 | match method:
52 | case "paypal":
53 | return Paypal()
54 | case "stripe":
55 | return Stripe()
56 | case _:
57 | raise ValueError("Invalid gateway name")
58 |
59 |
60 | if __name__ == "__main__":
61 | """
62 | here, `stripe` and `paypal` objects are products that are instantiated by
63 | the PaymentGatewayFactory.
64 | """
65 | stripe = PaymentGatewayFactory.create("stripe")
66 | stripe.pay(20)
67 |
68 | paypal = PaymentGatewayFactory.create("paypal")
69 | paypal.pay(30)
70 |
--------------------------------------------------------------------------------
/design_patterns/singleton.py:
--------------------------------------------------------------------------------
1 | """
2 | Singleton Design pattern is useful when we want to initialize the object only
3 | once. This design pattern is useful when we need to share the object state
4 | across the whole application.
5 |
6 | If we try to re-instantiate the new object, it always provide the previously
7 | initialized object instead of creating the one.
8 |
9 | The best example of the singleton design pattern is a db connection pool.
10 | In this case, if we want to connection to the database, trying to initialize
11 | the db connection pool always returns the same object.
12 | """
13 |
14 | from datetime import datetime
15 | from time import sleep
16 |
17 |
18 | class ConnectionPool:
19 | _instance: "ConnectionPool"
20 | created_at: float
21 | name: str
22 |
23 | def __init__(self) -> None:
24 | self.created_at = datetime.now().timestamp()
25 | self.name = ""
26 |
27 | def __new__(cls):
28 | if hasattr(cls, "_instance"):
29 | print("Reusing old 'ConnectionPool' instance")
30 | return cls._instance
31 | print("Initializing new 'ConnectionPool' instance")
32 | cls._instance = super(ConnectionPool, cls).__new__(cls)
33 | return cls._instance
34 |
35 | @classmethod
36 | def _get_object(cls):
37 | try:
38 | return getattr(cls, "pool")
39 | except AttributeError:
40 | cls.pool = cls()
41 | return cls.pool
42 |
43 |
44 | if __name__ == "__main__":
45 | pool1 = ConnectionPool() # Initializing new 'ConnectionPool' instance
46 | sleep(2)
47 |
48 | pool2 = ConnectionPool() # Reusing old 'ConnectionPool' instance
49 | sleep(2)
50 |
51 | pool3 = ConnectionPool() # Reusing old 'ConnectionPool' instance
52 |
53 | """
54 | As the state of the singleton is shared across objects, changing one object
55 | will update the state of all instantiated objects.
56 | """
57 | print(pool1.created_at)
58 | print(pool2.created_at) # created timestamp will be same
59 | print(pool3.created_at) # created timestamp will be same
60 |
61 | pool2.name = "Pool 2"
62 |
63 | print(pool1.name) # Pool 2
64 | print(pool3.name) # Pool 2
65 |
--------------------------------------------------------------------------------
/notes/c01_basics/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 1 Fundamentals of Python
2 |
3 | **Table of Contents**:
4 |
5 | - [Introduction to Python](Chapter-1.1-Basics.md)
6 | - [Python Environment Setup, IDE Setup](Chapter-1.1-Basics.md#installing-python)
7 | - [Hello World in Python](Chapter-1.1-Basics.md#hello-world-with-idle)
8 | - [Running Python Programs](Chapter-1.1-Basics.md#creating-editing-and-running-python-files)
9 | - [comments and documentation](Chapter-1.1-Basics.md#comment-lines)
10 | - Single Line Comments
11 | - inline Comments
12 | - Multiline Comments
13 | - Docstrings
14 | - indentation
15 |
16 | ## Quiz
17 |
18 | - [Chapter 1 Quiz](quiz)
19 |
--------------------------------------------------------------------------------
/notes/c01_basics/code/c0101_hello_world.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Hello world Program
5 |
6 | Note: Please refer to the file "chapter 1 Basics.md" for this note
7 |
8 | This file contains the basic hello world program structure that can be written in either of:
9 |
10 | 1. Python File
11 | 2. Python Console
12 |
13 | """
14 |
15 |
16 | # in python files
17 | print('hello world')
18 |
19 | # in consoles
20 |
21 | # >>> 'hello world'
22 | #
23 | # or
24 | #
25 | # >>> print('hello world')
26 |
27 | # print the value of 2+2
28 |
29 | print(2 + 2)
30 |
31 | # or in console, >>> 2+2
32 |
33 |
34 | '''
35 | things to note:
36 |
37 | 1. print
38 | 2. print()
39 | 3. 'hello world'
40 | 4. 2
41 | 5. """
42 |
43 | '''
44 |
--------------------------------------------------------------------------------
/notes/c01_basics/code/c0102_comments.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Comments in python
5 |
6 | - Comments are the lines or the part of code that is ignored by the python interpreter.
7 | - If you want to describe what you're doing or what the line below states, you might want to add comments
8 | that makes other users easy to understand the code
9 | - comments are not interpretted and contains no syntax inside the comment line
10 | - comments can also be added after expressions
11 | - Sometimes If some lines of codes are not necessary, we just comment the lines for future use
12 |
13 | - Single line comments can be added using #
14 | - multiline comments can be added using triple single or double quotes
15 | """
16 |
17 | # This is a single line comment. When I go to next line the comment scope ends.
18 | """
19 | This is a multiline comment.
20 | I can write any lines of code inside these quotation marks and will
21 | still be counted as comment
22 | Multiline comment should be enclosed within triple single or double quotes
23 | """
24 |
25 | print("Hello World!") # Single line comment can be at the end of the statement too.
26 |
27 |
28 | # Multiline comments can also be used as documentation purpose.
29 | # Please ignore the structure of a program since we're going to learn this in the future
30 | def add_me(a, b):
31 | """
32 |
33 | Parameters:
34 |
35 | - `a`: The first argument that can accept either integer or floating point numbers
36 | - `b`: The second argument that can accept either integer or floating point numbers
37 |
38 | Returns:
39 |
40 | the sum of 2 numbers `a` and `b`
41 |
42 |
43 | """
44 | return a + b
45 |
--------------------------------------------------------------------------------
/notes/c01_basics/quiz/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Chapter 1 Quiz
3 |
4 | Please read the note carefully and try to solve the problem below:
5 |
6 | **Table of contents**:
7 |
8 | - [Chapter 1 Quiz](#chapter-1-quiz)
9 | - [Quiz: Q0101](#quiz-q0101)
10 | - [Quiz: Q0102](#quiz-q0102)
11 |
12 | ## Quiz: Q0101
13 |
14 | 1. Try to replicate the line below and change `'hello world'` to `[your name]`
15 | and `print` it in the terminal.
16 |
17 | ```python
18 | print("hello world")
19 | ```
20 |
21 | 2. Try adding two numbers and print the value in the terminal. (example: `print(5+6)`)
22 | 3. Try adding more than 2 numbers and see the results
23 | Example: `print(3+4+5)`
24 | 4. Try printing hello world without quotation `''` or inverted comma and see if any error occurs
25 | Example: `print(Hello World)`
26 |
27 | 5. If error occurs in any of the above step, please note the line number and the message where the exception is thrown.
28 |
29 | **[Click](solution/q0101.py)** to see the solution
30 |
31 | ## Quiz: Q0102
32 |
33 | 1. Print `"Hello World"` using print() function
34 | 2. Comment the line from the above solution and try running the program again. Explain what happens and why it happens
35 | 3. Create a multiline comment using Triple single `'''` and double quotes `"""` and add your content.
36 |
37 | **[Click](solution/q0102.py)** to see the solution
38 |
--------------------------------------------------------------------------------
/notes/c01_basics/quiz/solution/q0101.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0101 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | # Answers
8 |
9 | # 1
10 | print('John Doe')
11 |
12 | # 2
13 | print(10 + 20)
14 |
15 | # 3
16 | print(10 + 20 + 30)
17 |
18 | # 4
19 | # print(Hello World)
20 | """
21 | Since the strings needs to be quoted with single or double
22 | quotation marks, the above line gives error when the code
23 | is uncommented
24 | """
25 |
--------------------------------------------------------------------------------
/notes/c01_basics/quiz/solution/q0102.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0102 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | # 1
8 | print("Hello World")
9 |
10 | # 2
11 | # print("Hello World")
12 |
13 | """
14 | When above line is commented, Nothing gets printed since the comment line is ignored by the interpreter.
15 | """
16 |
17 | # 3
18 | """
19 | This is a multiline comment
20 | created using triple double-quotation marks
21 | """
22 |
23 | '''
24 | This is a multiline comment
25 | created using triple single-quotation marks
26 | '''
27 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/Chapter 2.2 Numeric Data Types.md:
--------------------------------------------------------------------------------
1 | # Chapter 2.2: Numeric Data types
2 |
3 | **Table of Contents**:
4 |
5 | - [Chapter 2.2: Numeric Data types](#chapter-22-numeric-data-types)
6 | - [1. Integers `int`](#1-integers-int)
7 | - [2. Floating Point Numbers `float`](#2-floating-point-numbers-float)
8 | - [3. Complex data types `complex`](#3-complex-data-types-complex)
9 | - [4. Boolean Data types `bool`](#4-boolean-data-types-bool)
10 |
11 | ## 1. Integers `int`
12 |
13 | Integers are those data types that contains either positive or negative numbers without any decimal points.
14 |
15 | ```python
16 | num1 = 5 # decimal number
17 |
18 | # We can also assign binary, octal, or hexadecimal numbers
19 |
20 | num2 = 0x4a # hexadecimal number
21 | num3 = 0b101 # binary number
22 | num4 = 0o447 # Octal number
23 |
24 | num5: int = -5 # optional type hinting
25 | ```
26 |
27 | ## 2. Floating Point Numbers `float`
28 |
29 | Floating point numbers are those data types that can contain decimal values.
30 |
31 | ```python
32 | num1 = 5.5
33 | num2 = -5.5
34 | num3: float = 5.5 # optional type hinting
35 | ```
36 |
37 | ## 3. Complex data types `complex`
38 |
39 | Unlike other programming languages, python comes with built in complex data types. we store the data in format:
40 |
41 | `real` + `imaginary` `j`
42 |
43 | ```python
44 | comp1 = 5 + 4j
45 | comp2 = -5j
46 | comp3: complex = 8 - 5j # optional type hinting
47 | ```
48 |
49 | ## 4. Boolean Data types `bool`
50 |
51 | We can count Boolean data types as both numeric and logical data types since `True` represents `1` and `False` represents `0`. In python we have keywords assigned for Boolean data types.
52 |
53 | ```python
54 | engaged = True
55 | married = False
56 |
57 | alive: bool = True # optional type hinting
58 | ```
59 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/Chapter 2.6 Typecasting.md:
--------------------------------------------------------------------------------
1 | # Chapter 2.6. Typecasting
2 |
3 | **Table of Contents**:
4 |
5 | - [Chapter 2.6. Typecasting](#chapter-26-typecasting)
6 | - [Implicit typecasting](#implicit-typecasting)
7 | - [Explicit typecasting](#explicit-typecasting)
8 |
9 | In case of python there are 2 types of type conversion
10 |
11 | 1. Implicit typecasting
12 | 2. Explicit typecasting
13 |
14 | ## Implicit typecasting
15 |
16 | - It is the type of typecasting when interpreter automatically converts data type while performing calculation
17 | - It is done when we perform operations between lower and higher precision quantities.
18 | - Implicit type casting always casts lower precision data type to higher precision one.
19 | - example: `int` can be implicitly type casted to `float`
20 |
21 | Example:
22 |
23 | ```python
24 | print(5 + 4.5)
25 | ```
26 |
27 | In this case, the integer, `5` is automatically converted into `float` data
28 | type and is then added to `4.5` since 4.5 is higher precision number.
29 |
30 | While Using F-Strings, data automatically gets typecasted to display the correct representation.
31 |
32 | Example:
33 |
34 | ```python
35 | x = 45
36 | sentence = f'The value of x is {x}'
37 | ```
38 |
39 | In this case, the value of x will automatically be typecasted into `str` and replaced inside `{}`.
40 |
41 | ## Explicit typecasting
42 |
43 | If we want to manually convert one data type into another, then it is known as
44 | explicit typecasting.
45 |
46 | - it is a manual process where we want to convert one type of data to another
47 | - it is more useful when we want to convert numeric to non-numeric and vice-versa.
48 |
49 | **Example**: _Converting string input to integer while parsing value from command line_
50 |
51 | ```python
52 | rate = 2
53 | quantity = input('Enter the number of apples you want to buy: ')
54 |
55 | # here, the value of quantity when entered by user will be taken as string
56 |
57 | quantity = int(quantity) # Explicit type conversion from str to int
58 |
59 | amount = quantity * rate
60 | print (f'The total payable amount is: {amount}.)
61 |
62 | ```
63 |
64 | Type casting from higher precision data-types to lower-precision data types can also
65 | lead to data loss. so we have to make sure that we actually need to convert the
66 | higher precision data type into lower precision one.
67 |
68 | **example**: _float to int type casting omits all numbers after the decimal point._
69 |
70 | **Example 1:** Explicit typecasting before adding `45.5` and `54.5` will result `99` instead of `100.0` in which the difference might be negligible.
71 |
72 | ```python
73 | x = 45.5
74 | y = 54.5
75 | print(x + y) # 100.0
76 | print(int(x) + int(y)) # 99
77 | ```
78 |
79 | **Example 2:** Explicit typecasting before taking power of `10.9` by `5.7` will result `100000` instead of `777168.88` in which the difference is significant and will lead to wrong results.
80 |
81 | ```python
82 | x = 10.9
83 | y = 5.7
84 | print(x ** y) # 777168.8877963118
85 | print(int(x) ** int(y)) # 100000
86 | ```
87 |
88 | So before typecasting, we need to be sure that we're doing it right and is not significantly changing the expected output.
89 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 2 Variables, basic data type and operations
2 |
3 | Data Types are the variables that we use to reserve some space in memory. Data types define how the data is stored and how it behaves in different situations. For example `1` + `1` equals `2`, but `A` + `B` can not be `C`. This kind of properties are defined by data types.
4 |
5 | Unlike other programming languages, we do not declare data type explicitly. Since python 3.6 Optional type-hinting is introduced in python.
6 |
7 | **Table of contents**:
8 |
9 | - [2.1. Variables](Chapter%202.1%20Variables.md)
10 | - [2.2. Numeric Data Types](Chapter%202.2%20Numeric%20Data%20Types.md)
11 | - [2.3. Strings](Chapter%202.3%20Strings.md)
12 | - [2.4. String Formatting](Chapter%202.4%20string%20formatting.md)
13 | - [2.5. Basic Operations](Chapter%202.5%20Operations.md)
14 | - [2.6. Type Casting/conversion](Chapter%202.6%20Typecasting.md)
15 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0201_variables.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Variables in Python
4 |
5 | let us suppose we have a statement as follows:
6 | x = 5
7 |
8 | here, `x` is a variable.
9 | The word used in a variable is also known as identifier
10 | """
11 |
12 | a = 5 # integer
13 | b = 5.45 # floating point numbers (floats)
14 | c = "Hello There+-!#$....." # string
15 |
16 | # the keyword = is used for assignment
17 |
18 | # statement
19 | x = a + 10 # x = 15
20 |
21 | x = x + 1 # x = 16
22 | # It might be confusing to people when we see `x = x+1` in the statement since it mathematically
23 | # violates the left hand and right hand values. We can think `=` as the update to the previous value, which makes it
24 | # visually similar to `x <- x+1` or x is updated to be x + 1 from now onwards
25 |
26 |
27 | # defining multiple variable at once in python
28 | # let us suppose we have 3 variable declarations
29 | v1 = 5
30 | v2 = 6
31 | v3 = 7
32 | # we can achieve it in the single line with the following statement
33 | v1, v2, v3 = 5, 6, 7
34 |
35 | # or
36 | (v1, v2, v3) = (5, 6, 7)
37 |
38 |
39 | # rules for defining an identifier (name of the variable)
40 |
41 | """
42 | 1. it can start with alphabetical characters
43 | 2. it can start with _ character
44 | 3. it can have numeric characters in between or at the end but not in the beginning
45 | 4. we can not use special characters like space, tab, `+`, `-`, etc.
46 | 5. we can separate different words with underscores `_`
47 | 6. we use snake_case for variable definition
48 |
49 | lists of valid and invalid identifiers are shown below:
50 |
51 | 1. name = 'cow' -> Valid
52 | 2. Name = 'cow -> Valid but not recommended by PEP (Always use snake case)
53 | 3. name one = 'cow' -> Invalid since it can not contain special characters except `_`
54 | 4. 1name = 'cow' -> Invalid since it can not start with numeric characters
55 | 5. name1 = 'cow' -> Valid
56 | 6. name_1 = 'cow' -> Valid
57 | 7. first_name = 'John' -> Valid
58 | 8. FirstName = 'John' -> Valid bud not recommended by PEP (Camel case is recommended for classes)
59 | """
60 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0202_basic_data_types.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Numeric Data Types in Python
4 |
5 | to run the file, we can go to the terminal and just type
6 | python 02_data_types/02_basic_data_types.py
7 |
8 | """
9 | x: complex = -5j
10 | x = 5 # integer
11 | x = 5.5 # float
12 | x = True # Boolean
13 |
14 | # simple interest
15 | # p -> principal amount 10000
16 | # r -> rate 10.5% oof interest
17 | # t -> time (years) 1.5
18 |
19 | p = 10000 # integer
20 | r = 10.5 # float
21 | t = 1.45 # float
22 |
23 | print(type(p))
24 |
25 | # practice
26 | simple_interest = (p * t * r) / 100
27 | print(simple_interest)
28 |
29 |
30 | # String Data Types in Python
31 | string_one = 'Hello World'
32 |
33 | # Lists
34 | '''
35 | - They are similar to arrays in other programming languages
36 | - They are containers for multiple data
37 | - Items are ordered
38 | - First index of the list is 0
39 | - The list with n number of items has last index of n-1
40 |
41 | - Differences between arrays in other programming languages and lists in python are:
42 | - It can have more than one type of data
43 | [1, 1, 'a', 1.5, [1,'a'], True]
44 | - Lists are mutable
45 |
46 | - Lists can have duplicate values
47 | '''
48 |
49 | # Initializing a list
50 | # we initialize a list using a large bracket or Square bracket []
51 | # Each item is separated by comma
52 | # It is recommended to use a space after comma
53 | # It is also possible to assign it in multiple lines
54 |
55 | list_one = [1, 2, 3, 4, 5]
56 | list_one.append(6)
57 |
58 | wild_animals = [
59 | 'Tiger',
60 | 'Lion',
61 | 'Leopard',
62 | 'Elephant',
63 | 'Rhino'
64 | ]
65 |
66 |
67 | # Tuple Data Type
68 |
69 | """
70 | - Tuples are similar as lists, but the values are immutable
71 | - Tuples are ordered
72 | - Tuples index starts with 0
73 | - Tuples can have more than one data types
74 | - Tuples can be non-unique
75 | - Tuples can not be changed or modified once initialized
76 | - We can not add or remove elements from tuple
77 | - Tuples are represented by small brackets.
78 | - Tuples are faster than lists
79 | """
80 |
81 | # initializing a tuple
82 | tuple_one = (1, 2.4, 3, 4, 4, 'Cat', [1, 2, 3])
83 |
84 | wild_animals_tuple = ('Tiger', 'Lion', 'Leopard', 'Elephant', 'Rhino')
85 |
86 |
87 | # Sets
88 | """
89 | - Sets are similar to lists except they have unique data
90 | - Sets are represented by braces {}
91 | - Sets are not ordered
92 | - Sets can not be accessed with indices.
93 | - Set items can not be changed individually, but can be added to it
94 |
95 | - Mathematical operations
96 | - Union
97 | - Intersection
98 | - Difference, ...
99 |
100 | """
101 |
102 | # initializing a set
103 |
104 | set_one = {1, 2, 3, 4}
105 |
106 | set_2 = {1, 2, 3, 4, 4}
107 |
108 | print(set_2)
109 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0203_strings.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Strings in Python
4 |
5 | - single quotes
6 | - double quotes
7 | - escape characters
8 | - quotation inside quotes
9 | - concatenation
10 | - length of the string
11 | - capitalization
12 | - isdecimal
13 |
14 | Formatting strings:
15 | - %s
16 | - `format()` function
17 | - f-strings
18 |
19 | """
20 |
21 | # We can use Single quote for strings
22 | name = 'John'
23 |
24 | # We can use double quotes for strings
25 | name = "John"
26 |
27 | # We can use single quotes inside double or vice versa
28 | x = "I am at John's"
29 | x = 'He said, "John is a cool guy!"'
30 |
31 | # If we want to have special characters or
32 | # We want single inside single quotes or double quotes inside double quotes, we have to use escape characters
33 |
34 | # We can achieve escape characters by using backslash `\` before the character.
35 | x = 'I am at John\'s' # this will output : I am at John's
36 |
37 | # some of the examples of escape characters and their use cases are as follows:
38 | """
39 | \t => tabs
40 | \n => new lines
41 | \" => double quotes
42 | \' => single quotes
43 | \\ => backslash
44 | \a => ascii bell
45 | \b => backspace
46 | \r => carriage return
47 | \v => vertical tab
48 | for more detail about escape characters, please see https://docs.python.org
49 | """
50 |
51 | two_lines = "This is the first line.\nThis is the second line."
52 |
53 | paragraph = "Microsoft and our third-party\n vendors use cookies to store and access information such as unique IDs to deliver, maintain and improve our\n services and ads. If you agree, MSN and Microsoft Bing will personalize the content and ads that you see. You can select \"I Accept\" to consent to these uses or click on \"Manage preferences\" to review your options You can change your selection under \"Manage\n Preferences\" at the bottom of this page.\n\n\n"
54 |
55 | print(paragraph)
56 |
57 | hobbies = "\n\n1.\tsinging\n2.\tdancing\n3.\tcoding"
58 | print(hobbies)
59 |
60 | # Concatenation
61 | # it is the process of adding or joining 2 strings together
62 |
63 | first_name = "Mohammad"
64 | last_name = "Ibrahim"
65 |
66 | full_name = "Mohammad" + "Ibrahim" # MohammadIbrahim
67 |
68 | print("My name is " + full_name) # My name is MohammadIbrahim
69 |
70 | full_name = "My name is " + first_name + " " + last_name # My name is Mohammad Ibrahim
71 |
72 | # length of the string
73 | """
74 | we can get the length of string with `__len__()` method
75 |
76 | the method gives the length of string including all the white spaces and special characters
77 | """
78 |
79 | length = paragraph.__len__()
80 |
81 | print("the length of the paragraph is: " + str(length))
82 |
83 | print("Mohammad".__len__()) # 8
84 |
85 | print("John Doe".__len__()) # 8
86 |
87 | # capitalization, Upper Case, and Lower Case
88 |
89 | print("john".capitalize()) # John
90 |
91 | print("john".upper()) # JOHN
92 | print("JOHN".lower()) # john
93 |
94 | # isdecimal()
95 | value = "10"
96 | print(value.isdecimal()) # True
97 | print("123stu".isdecimal()) # False
98 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0205_arithmetic.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Arithmetic operations
4 | - Addition
5 | - Subtraction
6 | - Multiplication
7 | - Division
8 | - Modulus
9 | - Exponentiation
10 | - Integer Division
11 | """
12 |
13 | # %% Addition
14 | a1 = 5 + 6 # integer and integer
15 | a2 = 5.5 + 2.3 # float and float
16 | a3 = 4.7 + 2 # integer and float
17 | a4 = "Hello " + "world" # string and string
18 | a5 = (5 + 4j) + (6 + 5j) # complex and complex
19 |
20 | # a6 = 5 + 'Hello there' # numeric and non-numeric not supported
21 |
22 |
23 | # %% Subtraction
24 | b1 = 5 - 6 # integer and integer
25 | b2 = 5.5 - 2.3 # float and float
26 | b3 = 4.7 - 2 # integer and float
27 | # b4 = "Hello " - "world" # string subtraction not supported
28 | b5 = (5 + 4j) - (6 + 5j) # complex and complex
29 |
30 | # %% Multiplication
31 | c1 = 5 * 6 # integer and integer
32 | c2 = 5.5 * 2.3 # float and float
33 | c3 = 4.7 * 2 # integer and float
34 | c4 = "Hello " * 5 # string and int
35 | c5 = (5 + 4j) * (6 + 5j) # complex and complex
36 |
37 | # %% Division
38 | d1 = 5 / 6 # integer and integer
39 | d2 = 5.5 / 2.3 # float and float
40 | d3 = 4.7 / 2 # integer and float
41 | d4 = (5 + 4j) / (6 + 5j) # complex and complex
42 |
43 | # %% Modulus
44 | e1 = 55 % 4 # integer and integer
45 | e2 = 5.5 % 2.3 # float and float
46 | e3 = 4.7 % 2 # integer and float
47 |
48 | # %% Exponentiation
49 |
50 | f1 = 5 ** 4 # integer and integer
51 | f2 = 5.5 ** 2.3 # float and float
52 | f3 = 4.7 ** 2 # integer and float
53 |
54 | # %% Integer Division
55 | print(5//2)
56 | g1 = 45//2
57 | g2 = 45.8//5.1 # 8.0 # integer equivalent of float
58 |
59 | # %%
60 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0206_relational.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Relational Operations
4 |
5 | equals (==)
6 | not equals(!=)
7 | less than (<)
8 | less than or equals (<=)
9 | greater than (>)
10 | greater than or equals (>=)
11 |
12 | Relational operation always returns either True or False
13 |
14 | """
15 |
16 |
17 | # %% equals ( == )
18 | print(5 == 6) # False
19 | print(4+1 == 6-1) # True
20 |
21 |
22 | # %% not equals( != )
23 | print(5 != 6) # True
24 | print(4+1 != 6-1) # False
25 |
26 | # %% less than ( < )
27 |
28 | print(4 < 5) # True
29 | print(4 < 4) # False
30 | print(5 < 4) # False
31 |
32 | # %% less than or equals ( <= )
33 | print(4 <= 5) # True
34 | print(4 <= 4) # True
35 | print(5 <= 4) # False
36 |
37 |
38 | # %% greater than ( > )
39 | print(4 > 5) # False
40 | print(4 > 4) # False
41 | print(5 > 4) # True
42 |
43 | # %% greater than or equals ( >= )
44 | print(4 >= 5) # False
45 | print(4 >= 4) # True
46 | print(5 >= 4) # True
47 | print('A' > 'B') # False
48 |
49 | # ASCII
50 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0207_logical.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Logical Operators
4 | - logical operators are used to combined 2 or more relational operators or conditions
5 | - They use human readable words as operators
6 | - and
7 | - or
8 | - not
9 | """
10 |
11 | # %% and
12 | """
13 | - it is used between two statements or conditions
14 | - the value is true if both of the conditions are true
15 | """
16 | print(True and True) # True
17 | print(True and False) # False
18 | print(False and True) # False
19 | print(False and False) # False
20 |
21 |
22 | # %% or
23 | """
24 | - it is used between two statements or conditions
25 | - the value is true if any of the conditions is true
26 |
27 | """
28 | print(True or True) # True
29 | print(True or False) # True
30 | print(False or True) # True
31 | print(False or False) # False
32 |
33 |
34 | # %% not
35 | """
36 | - it is used before a condition or statement
37 | - the value is opposite of the condition
38 | """
39 | print(not True) # False
40 | print(not False) # True
41 |
42 |
43 | # %% we can combine multiple conditions using brackets
44 |
45 | print(not((4 < 5) or (5 < 3)))
46 |
47 | # Explanation
48 | # not( (4<5) or (5<3) )
49 | # not( True or (5<3) )
50 | # not( True or False )
51 | # not( True )
52 | # False
53 |
54 | # %%
55 | # we can assign the values first and perform operations later
56 |
57 | x = 4 < 5 or 6 > 7
58 | y = True and 10 > 12
59 |
60 | print(x and y)
61 |
62 |
63 | # %% complex Logical operation example
64 | can_cat_fly = False
65 | can_snake_walk = False
66 | can_dog_bark = True
67 | can_john_speak_French = True
68 |
69 | print(
70 | not(
71 | (not(can_cat_fly or can_dog_bark)or(can_snake_walk))
72 | and can_john_speak_French)
73 | )
74 |
75 | # %%
76 | # If we do not use brackets in complex logical operations, we
77 | # might not get the expected output
78 |
79 | print(not True or not False) # True
80 | print(not (True or not False)) # False
81 | print((not True) or (not False)) # True
82 | # %%
83 |
84 |
85 |
86 | # if it is cloudy today and if it rained yesterday, then it rains today
87 | # in other conditions it doesn't rain
88 |
89 | cloudy = False
90 | rained = True
91 |
92 | rains = cloudy and rained
93 |
94 | if rains:
95 | print("it rains today")
96 | else:
97 | print("It does not rain today")
98 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0208_identity.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Identity operations
4 | - used to compare whether they are same object or not
5 | - they are not used to compare for equality
6 | - identity operators are:
7 | 1. `is`
8 | 2. `is not`
9 | """
10 |
11 | # %% The right method
12 |
13 | print(type('abc') is str) # True
14 |
15 | list1 = ['abc', 'def']
16 | list2 = list1 # same object is referenced here
17 | list3 = list1.copy() # shallow copy is made here
18 |
19 | print(list1 is list2) # True
20 |
21 | # == is not same as is
22 | print(list1 is list3) # False
23 | print(list1 == list3) # True
24 |
25 | print(type(list1) is not int) # True
26 |
27 |
28 | # %% the wrong method
29 | print(1 + 4 is 6-1)
30 | # this gives equality but it shows warning to use double equals
31 | # rather than `is` since it is not intended to compare
32 |
33 | # %%
34 | # We will be dealing with identity operator when we
35 | # start object oriented programming
36 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0209_membership.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | # %% [markdown]
3 | """
4 | # Python Membership Operations
5 |
6 | Operators:
7 |
8 | - Membership operators are used to check if the element is present in the specified object or not.
9 | - basic membership operators:
10 | - `in` (format: `a in x`)
11 | - `not in` (format: `a not in x`)
12 | - membership operators return either `True` or `False`
13 |
14 | """
15 | # %% `in` operator
16 |
17 | x = [1, 2, 3, 4, 5]
18 |
19 | print(5 in x) # True
20 | print(10 in x) # False
21 |
22 | people = ['John', 'Jane', 'Janet']
23 |
24 | print('John' in people) # True
25 | print('Mohammad' in people) # False
26 |
27 |
28 | # %% `not in` operator
29 | # this is just opposite of `in` operator
30 | print('John' not in people) # False
31 |
32 | print(5 not in x) # False
33 | print(10 not in x) # True
34 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0210_bitwise.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | # %% [markdown]
3 | """
4 | # Bitwise Operators
5 |
6 | - bitwise operators, as suggested by name, work on bit.
7 | - bitwise operator works only with numeric values
8 | - operation is done with each bit position rather than the value as a whole
9 | - suppose we have 2 numbers `5` and `6`
10 | We need to understand binary to decimal conversion and vice versa to understand bitwise operation
11 |
12 | binary numbers are also known as base-2 numbers
13 |
14 | ** not just valid for python
15 |
16 | - `5` in decimal is `101` in binary
17 | - `16` in decimal is `10000` in binary
18 |
19 | for uint8 or u8,
20 |
21 | 5 = 0 0 0 0 0 1 0 1
22 | 16 = 0 0 0 1 0 0 0 0
23 |
24 | for uint32 or u32,
25 |
26 | 5 = `0000 0000 0000 0000 0000 0000 0000 0101`
27 |
28 | 16 = `0000 0000 0000 0000 0000 0000 0001 0000`
29 |
30 | in the above example, we perform bitwise operation to same positioned digit
31 | of the both numbers
32 | - the first digit of the first value interacts with the first digit of second value
33 | - the second digit of the first value interacts with the second digit of second value,
34 | ...
35 | - the last digit of the first value interacts with the the last digit of second value
36 |
37 | you can always check the binary value of a number with
38 | `bin()` function.
39 |
40 |
41 | Some Bitwise Operations
42 |
43 | - Bitwise AND operator `&`
44 | - Bitwise OR operator `|`
45 | - Bitwise NOT operator `~`
46 | - Bitwise XOR operator `^`
47 | - Bitwise SHIFT LEFT operator `<<`
48 | - Bitwise SHIFT RIGHT operator `>>`
49 |
50 |
51 | """
52 |
53 | # %% Bitwise AND operator `&`
54 |
55 | '''
56 | Here 5 is 101 and 6 is 110
57 | Starting from the last digit of both 5 and 6,
58 | we do bitwise AND Operation
59 | Bitwise means,
60 | '''
61 | x = 5 # 0 0 1 0 1
62 | y = 21 # 1 0 1 0 1
63 | print(x & y) # 0 0 1 0 1 5
64 |
65 | # job married children
66 | # 1 0 0
67 | # 4
68 | # %% Bitwise OR operator `|`
69 |
70 | x = 5 # 0 0 1 0 1
71 | y = 16 # 1 0 0 0 0
72 | print(x | y) # 1 0 1 0 1 21
73 |
74 | # %% Bitwise NOT operator `~`
75 |
76 | x = 5 # 0 1 0 1
77 | print(~5) # 1 0 1 0
78 |
79 | # %% Bitwise XOR operator `^`
80 |
81 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0211_assignment.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 | """
3 | Assignment operations:
4 | =
5 | +=
6 | -=
7 | *=
8 | /=
9 | %=
10 | //=
11 | **=
12 | &=
13 | |=
14 | ^=
15 | >>=
16 | <<=
17 | """
18 |
19 | # %% = operator
20 | x = 50
21 | print(x)
22 |
23 | x, y = 5, 10
24 | # %% += operator
25 |
26 | x += 2 # x = x + 2
27 | print(x)
28 |
29 | # %% -= operator
30 |
31 | x -= 2 # x = x - 2
32 | print(x)
33 |
34 | # %% *= operator
35 | x *= 3 # x = x * 2
36 | print(x)
37 |
38 | # %% /= operator
39 | x /= 3 # x = x / 2
40 | print(x)
41 |
42 | # %% %= operator
43 |
44 | x %= 30 # x = x % 30
45 | print(x)
46 | # %% //= operator
47 |
48 | x //= 2 # x = x // 2
49 | print(x)
50 |
51 | # %% **= operator
52 |
53 | x **= 5 # x = x ** 2
54 | print(x)
55 |
56 | # %% &= operator
57 | x = 5 # 101
58 | x &= 6 # 110 # x = x & 2
59 | print(x) # 100
60 |
61 | # %% |= operator
62 | x = 5 # 101
63 | x |= 6 # 110 # x = x | 2
64 | print(x) # 111
65 |
66 | # %% ^= operator
67 | x = 5 # 101
68 | x ^= 6 # 110 # x = x ^ 2
69 | print(x) # 011
70 |
71 | # %% >>= operator
72 | x = 5 # 0000 0101
73 | x >>= 1 # -> shift right 1 time # x = x >> 1
74 | print(x) # [ 0000 0010 ] 1 <- this is discarded
75 |
76 | # %% <<= operator
77 | x = 5 # 0000 0101
78 | x <<= 2 # <- shift left 2 times # x = x << 2
79 | print(x) # 00[00 010100] <- 2 more zeros added in the end
80 |
81 | # %%
82 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/code/c0212_typecasting.py:
--------------------------------------------------------------------------------
1 | # https://sudipghimire.com.np
2 |
3 | """
4 | Type Conversion
5 | - Type casting or type conversion is the process of converting the data from one
6 | one type to another.
7 | - We perform type casting to unify the data types for better and efficient calculation
8 |
9 | In case of python there are 2 types of type conversion
10 |
11 | 1. Implicit type casting
12 | - it is automatically converted by the interpreter while performing calculation
13 | - it is done when we perform operations between lower and higher precision quantities.
14 | - Implicit type casting always casts lower precision data type to higher precision one.
15 | - example: int can be implicitly type casted to float
16 |
17 | 2. explicit type casting
18 | - it is a manual process where we want to convert one type of data to another
19 | - it is more useful when we want to convert numeric to non-numeric and vice-versa.
20 | - type casting from higher precision data-types to lower-precision data types can also
21 | lead to the data loss. so we have to make sure that we actually need to convert the
22 | current data type.
23 | example: float to int type casting omits all numbers after the decimal.
24 |
25 | """
26 |
27 | # %% Implicit Type Casting Example
28 | from typing import Type
29 |
30 |
31 | x = 5
32 | y = 10.5
33 | print(x+y) # here interpreter automatically casts x from 5 to 5.0
34 | # in case of other programming languages we might have to
35 | # convert the integer type data to floating poing before
36 | # performing such operations.
37 |
38 | # %% Explicit Type Casting
39 | # integer to float type casting
40 |
41 | x = 5 # x is int (5)
42 | x = float(x) # x is float (5.0)
43 | print(type(x))
44 |
45 | # %% integer to string
46 | x = 5 # 5
47 | x = str(5) # '5'
48 | print(type(x))
49 |
50 | # %% string to integer
51 |
52 | x = "45"
53 | x = int(45)
54 | print(type(x))
55 |
56 | # %% string to float
57 |
58 | PI = "3.1415"
59 | PI = float(PI)
60 | print(type(PI))
61 |
62 | # %% trying to convert incompatible data type gives us value error
63 | PI = "3.1415"
64 | PI = int(float(PI)) # gives Value Error
65 |
66 | # %% we can always check if it can be typecasted
67 |
68 | PI = "3.1415"
69 | if PI.isdigit(): # it checks if PI is int or not
70 | PI = int(PI)
71 | else:
72 | PI = float(PI)
73 | print(type(PI))
74 | # %%
75 |
76 | x = [5.7, 8.12, 6, 18, 12.99, 45.86, 33.44]
77 |
78 | # no type casting
79 | result = 0
80 | for a in x:
81 | result = result + a
82 | print(result)
83 |
84 | # typecasting in the loop
85 | result = 0
86 | for a in x:
87 | result = result + int(a)
88 | print(result)
89 |
90 | # typecasting in the result
91 | result = 0
92 | for a in x:
93 | result = result + a
94 | print(int(result))
95 | # %%
96 | # application 1 : removing the duplicates
97 | x = [1, 3, 5, 2, 7, 8, 3, 0, 1, 6, 3, 9, 3]
98 |
99 | x = list(set(x))
100 | print(x)
101 |
102 |
103 | # %%
104 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/quiz/solution/q0201.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0201 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | # 1
8 | x = 55
9 | print(x)
10 | # 2
11 | x = 60
12 | print(x)
13 | # 3
14 | x = x+10
15 | print(x)
16 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/quiz/solution/q0202.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0202 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | #1
8 | x = 10
9 | print(x)
10 |
11 | #2
12 | y = 10.5
13 | print(x)
14 |
15 | # 3
16 | print(type(x))
17 | print(type(y))
18 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/quiz/solution/q0203.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0203 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | first_name = "John"
8 | last_name = "Doe"
9 |
10 | # 1
11 | full_name = "My Name is " + first_name + " " + last_name
12 | print(full_name)
13 |
14 | # 2
15 |
16 | full_name_formatted = "My Name is {} {}".format(first_name, last_name)
17 |
18 | print(full_name_formatted)
19 |
20 | # 3
21 | full_name_fstring = f"My Name is {first_name} {last_name}"
22 | print(full_name_fstring)
23 |
24 | # 4
25 | full_name_s = "My Name is %s %s" % (first_name, last_name)
26 | print(full_name_s)
27 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/quiz/solution/q0204.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0204 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | PI = 3.14159265
8 |
9 | # 1
10 | print(f"{PI: 5.3f}")
11 |
12 | # 2
13 | print(f"{PI: 10.2f}")
14 |
15 | # 3
16 | print(f"The value of PIE is{PI: 12.2f}")
17 |
--------------------------------------------------------------------------------
/notes/c02_basic_data_types/quiz/solution/q0205.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0205 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | name = input("What is your name?: ")
8 | age = input("What is our age?: ")
9 | print(f"Hello {age} years old {name}!!")
10 |
--------------------------------------------------------------------------------
/notes/c03_advanced_data_types/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 3. Advanced Data Types and Operations
2 |
3 | **Table of Contents**:
4 |
5 | - **[3.1. List](chapter%203.1%20list.md)**
6 | - **[3.2. Tuple](chapter%203.2%20tuple.md)**
7 | - **[3.3. Dictionary](chapter%203.3%20dictionary.md)**
8 | - **[3.4. Set](chapter%203.4%20set.md)**
9 | - **[3.5. Nesting](chapter%203.5%20nesting.md)**
10 |
11 | **Quiz**:
12 |
13 | - **[Chapter 3 Quiz](quiz/README.md)**
14 |
--------------------------------------------------------------------------------
/notes/c03_advanced_data_types/code/c0305_type_hinting.py:
--------------------------------------------------------------------------------
1 | # %%
2 | # https://sudipghimire.com.np
3 | """
4 | Type Hinting in Python
5 |
6 | - Introduced first in python 3.6
7 | - Hinting is not strict so the statement can contain different datatypes
8 | - Hintings are useful specifically for the development purpose rather than execution
9 | - Hintings do not make python statically typed, but adds confidence to the programmers
10 | - it makes programmers productive by hinting the exact data type so that we do not need to browse the source
11 |
12 | """
13 | from typing import Dict, List
14 |
15 |
16 | # %% Some Examples of hinting in statements
17 |
18 | a: int = 5 # same as a = 5
19 |
20 | x: str = "ssdsd"
21 |
22 | b: float = 5.5 # same as b = 5.5
23 | c: str = 'Tyrion Lannister'
24 | d: list = [1, 2, 3, 4, 5, 'abc']
25 | e: tuple = (1, 2, 3)
26 |
27 | # List
28 |
29 | # %% Compound Types
30 | import typing
31 | from typing import List
32 | l1: List['int'] = [4, 5]
33 |
34 |
35 | l2: List = ['abc', 45]
36 |
37 | d1: Dict[str, str] = {'k': 'v'}
38 |
39 |
40 | # these are the basic Type hintings.
41 | # Creating custom types will be discussed in the python L2 course since
42 | # this part requires the knowledge of classes and inheritance
43 |
44 | import math
45 | def add_int(x, y):
46 |
47 | return( math.floor(x) + y)
48 |
--------------------------------------------------------------------------------
/notes/c03_advanced_data_types/quiz/solution/q0301.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0301 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | my_list = []
8 |
9 | # 1
10 | my_list.append(5)
11 | my_list.append(9)
12 | print(f"The list is {my_list}")
13 |
14 |
15 | # 2
16 | num = input("Enter a number to append: ")
17 | my_list.append(int(num))
18 | print(f"The list after appending {num} is {my_list}")
19 |
20 | # 3
21 | more_items = [3, 4, 5]
22 | my_list.extend(more_items)
23 | print(f"The list after extending with {more_items} is {my_list}")
24 |
25 | # 4
26 | print(f"Length of the list is {len(my_list)}") # using len() function
27 | print(f"Length of the list is {my_list.__len__()}") # using class method
28 |
29 | # 5
30 | print("List before popping out second item:", my_list)
31 | my_list.pop(1)
32 | print("List after popping out second item:", my_list)
33 |
--------------------------------------------------------------------------------
/notes/c03_advanced_data_types/quiz/solution/q0302.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0302 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 |
7 | wild = ['tiger', 'lion', 'deer', 'bear', 'zebra']
8 |
9 | # a
10 |
11 | wild.sort()
12 | print("List after sorting: ", wild)
13 |
14 |
15 | # b
16 | wild.reverse()
17 | print("List after Reversing: ", wild)
18 |
19 | # c
20 | wild.extend(['leopard', 'elephant', 'rhino'])
21 | print("List after extending more animals: ", wild)
22 |
23 | # d
24 | print("the index of leopard is: ", wild.index('leopard'))
25 |
26 | index = wild.index('leopard')
27 | wild.pop(index)
28 | print("List after removing leopard: ", wild)
29 |
30 | # index = wild.index('leopard') #this gives error since 'leopard' has already been removed from the list
31 |
32 | # e
33 | wild.insert(2, "leopard")
34 | print("List after re-inserting leopard: ", wild)
35 | # f
36 | wild.remove('rhino')
37 | print("List after removing rhino: ", wild)
38 |
--------------------------------------------------------------------------------
/notes/c03_advanced_data_types/quiz/solution/q0303.py:
--------------------------------------------------------------------------------
1 | """
2 | Q0303 solutions
3 |
4 | https://sudipghimire.com.np
5 | """
6 | multi = [[12, 52, 37], [46, 51, 16], [17, 82, 39]]
7 |
8 | # a
9 | print(multi[1][1])
10 |
11 | # b
12 | print(multi[-1][-2])
13 |
14 | # c
15 | multi.append([40, 61, 10])
16 | print(multi)
17 |
18 | # d
19 | for item in multi:
20 | print(f'the item is: {item}')
21 | for inner_item in item:
22 | print("the inner item is: ", inner_item)
23 |
24 | # e
25 | multi.clear()
26 | print("the list after clearing is: ", multi)
27 |
--------------------------------------------------------------------------------
/notes/c04_decision_making/code/c0402_match.py:
--------------------------------------------------------------------------------
1 |
2 | # Simple pattern matching example
3 | error_code = 500
4 |
5 | match error_code:
6 | case 400:
7 | print('looks like something is missing in your input')
8 | case 404:
9 | print("Could not find what you're searching.")
10 |
11 | case 500:
12 | print('This is a server side error!!')
13 | case _:
14 | print('I could not find what the problem is!!')
15 |
16 |
17 | # Pattern Matching with Or
18 | error_code = 403
19 |
20 | match error_code:
21 | case 400:
22 | print('looks like something is missing in your input')
23 | case 401 | 403:
24 | print("I think you have permission issues.")
25 | case _:
26 | print('I could not find what the problem is!!')
27 |
28 |
29 | # pattern matching with wildcards
30 | (x, y) = (5, 7)
31 | match (x, y):
32 | case (0, 0):
33 | print("The point lies in the origin.")
34 | case (_, 0):
35 | print("The point lies in the x-axis.")
36 | case (0, _):
37 | print("The point lies in the y-axis.")
38 | case (_, _):
39 | print("The point lies somewhere between.")
40 |
--------------------------------------------------------------------------------
/notes/c04_decision_making/quiz/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 4 Decision Making Quiz
2 |
3 | Please read the note carefully and try to solve the problem below:
4 |
5 | **Table of contents**:
6 |
7 | - [Chapter 4 Decision Making Quiz](#chapter-4-decision-making-quiz)
8 | - [Quiz Q0401](#quiz-q0401)
9 | - [Quiz Q0402](#quiz-q0402)
10 | - [Quiz Q0403](#quiz-q0403)
11 | - [Quiz Q0404](#quiz-q0404)
12 | - [Quiz Q0405](#quiz-q0405)
13 |
14 | ## Quiz Q0401
15 |
16 | Write a program to input a number from the terminal and check whether a number is an integer or not.
17 |
18 | ## Quiz Q0402
19 |
20 | Write a program to input a word and find out if it is palindrome.
21 |
22 | A word is palindrome if it reads the same from both backward and forward. `EVE` `HANNAH` `BOB`, `ROTATOR`, `ANNA`, etc. are some of palindrome words
23 |
24 | The output in the console should look like below:
25 |
26 | ```shell
27 | >> Enter a word [press cancel to exit]: Hannah
28 | >> palindrome
29 |
30 | >> Enter a word [press cancel to exit]: John
31 | >> not palindrome
32 | ```
33 |
34 | ## Quiz Q0403
35 |
36 | Enter the temperature in celsius, and convert the temperature to fahrenheit. Finally, display different fever levels of the user.
37 |
38 | `fahrenheit = 9/5*celsius + 32`
39 |
40 | Conditions:
41 | | Temperature | Remarks |
42 | | ---------------: | ------------------ |
43 | | below `96F` | Low Temperature |
44 | | `96F` to `98F` | Normal Temperature |
45 | | `99F` to `101F` | Normal Fever |
46 | | `102F` to `104F` | High Fever |
47 | | above `104F` | Critical |
48 |
49 | ## Quiz Q0404
50 |
51 | Write a program that accepts a number from the terminal and checks whether it is a multiple of both 3,4, and 5 or not
52 |
53 | ## Quiz Q0405
54 |
55 | Write a program that asks a user score in percentage and display the grade with some remarks as follows:
56 | | Temperature | Grade | Remarks |
57 | | -------------: | :---: | -------------------------------------------------- |
58 | | below `60%` | C | Work hard otherwise you're going to fail the exam. |
59 | | `61%` to `70%` | B | Your result is satisfactory. |
60 | | `71%` to `80%` | B+ | Good Job, Keep doing better. |
61 | | `81%` to `90%` | A | Amazing Your hard work paid off. |
62 | | above `90%` | A+ | Excellent work, Congratulations topper!! |
63 |
--------------------------------------------------------------------------------
/notes/c05_loops/Chapter 5.3 Pattern Generation.md:
--------------------------------------------------------------------------------
1 | # Chapter 5.3: Pattern Generation
2 |
3 | **Table of Contents**:
4 |
5 | - [Chapter 5.3: Pattern Generation](#chapter-53-pattern-generation)
6 | - [Pattern 1](#pattern-1)
7 | - [Pattern 2](#pattern-2)
8 | - [Pattern 3](#pattern-3)
9 | - [Pattern 4](#pattern-4)
10 |
11 | ## Pattern 1
12 |
13 | ```
14 | A A A A A
15 | A A A A A
16 | A A A A A
17 | A A A A A
18 | A A A A A
19 | ```
20 |
21 | **Solution:**
22 |
23 | ```python
24 | for x in range(5):
25 | for y in range(5):
26 | print('A', end=' ')
27 | print()
28 | ```
29 |
30 | ## Pattern 2
31 |
32 | ```
33 | 1
34 | 1 2
35 | 1 2 3
36 | 1 2 3 4
37 | 1 2 3 4 5
38 | ```
39 |
40 | **Solution:**
41 |
42 | ```python
43 | for row in range(1, 6):
44 | for col in range(1, row + 1):
45 | print(col, end=' ')
46 | print()
47 | ```
48 |
49 | ## Pattern 3
50 |
51 | ```
52 | 1
53 | 2 2
54 | 3 3 3
55 | 4 4 4 4
56 | 5 5 5 5 5
57 | ```
58 |
59 | **Solution:**
60 |
61 | ```python
62 | for row in range(1, 6):
63 | for col in range(row):
64 | print(row, end=' ')
65 | print()
66 | ```
67 |
68 | ## Pattern 4
69 |
70 | ```
71 | 1
72 | 2 1
73 | 3 2 1
74 | 4 3 2 1
75 | 5 4 3 2 1
76 | ```
77 |
78 | **Explanation:**
79 |
80 | - Row 1: No. of cols = 1, spaces = 4, value = `1`
81 | - Row 2: No. of cols = 2, spaces = 3, value = `Row ... 1` = `2 1`
82 | - Row 3: No. of cols = 3, spaces = 2, value = `Row ... 1` = `3 2 1`
83 | - ...
84 |
85 | **Solution:**
86 |
87 | ```python
88 | for row in range(1, 6):
89 | # printing out spaces
90 | print(' ' * (5 - row), end='')
91 | # printing out patterns
92 | for col in range(row, 0, -1):
93 | print(col, end=' ')
94 | print()
95 | ```
96 |
--------------------------------------------------------------------------------
/notes/c05_loops/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 5. Loops and Comprehension
2 |
3 | **Table Of Contents**:
4 |
5 | - [5.1. While Loop](chapter%205.1%20while%20loop.md)
6 | - [The `break` and the `continue` statement](Chapter%205.1%20while%20loop.md#the-break-and-the-continue-statement)
7 | - [`while`-`else`](Chapter%205.1%20while%20loop.md#while-else)
8 | - [Nested `while` loop](Chapter%205.1%20while%20loop.md#nested-while-loop)
9 | - [5.2. For Loop](chapter%205.chapter%205.2%20for%20loop.md)
10 | - [the `enumerate()` function](Chapter%205.2%20for%20loop.md#the-enumerate-function)
11 | - [the `zip()` function](Chapter%205.2%20for%20loop.md#the-zip-function)
12 | - [5.3. Pattern Generation](chapter%205.1%20while%20loop.md)
13 | - [5.4. List Comprehensions](chapter%205.1%20while%20loop.md)
14 |
15 | ## Introduction to loops
16 |
17 | Loop is the sequence of instructions given to the program that runs repeatedly
18 | until a certain condition is reached. There are 2 ways to
19 | perform loop or iteration in python which are:
20 |
21 | - [While Loop](chapter%205.1%20while%20loop.md)
22 | - [For Loop](chapter%205.2%20for%20loop.md)
23 |
--------------------------------------------------------------------------------
/notes/c05_loops/code/c0501_while.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 | # %% [markdown]
3 | """
4 | ## While Loop
5 | - It is used for iterating until the condition fails to satisfy
6 |
7 | Basic Syntax:
8 |
9 | while :
10 | # statements
11 |
12 |
13 | """
14 | x = 0
15 | while x < 10:
16 | print(f'current value of x is {x}')
17 | x += 1
18 | # x++ # incorrect in case of python
19 |
20 | # %% another example
21 | value = 'y'
22 | while value.lower() != 'e':
23 | value = input('Enter text[`e` to exit]: ')
24 | print(f'You Entered: {value}')
25 | print('exiting now!!')
26 |
27 | # %% another example with numbers
28 |
29 | # %% infinite loop:
30 | # while True:
31 | # print('This loop does not end')
32 |
33 | # %% break and continue statements in a loop
34 | x = 0
35 | while x < 10:
36 | x += 1
37 | if x == 5:
38 | continue
39 | if x == 8:
40 | break
41 | print(f'current value of x is {x}')
42 | print('loop complete')
43 |
44 | # %% printing out only odd numbers below 10
45 | x = 0
46 | while x < 10:
47 | x += 1
48 | if x % 2 == 0:
49 | continue
50 | print(x)
51 | print('loop complete')
52 |
53 | # %% while else statement
54 | x = 0
55 | while x < 10:
56 | x += 1
57 | # These lines do not fulfill else condition in while else
58 | # if x == 5:
59 | # continue
60 | # print(f'current value of x is {x}')
61 | # if x == 8:
62 | # break
63 | else:
64 | print(f'x is now {x}, now Exiting!!')
65 |
66 | # %% nested loops
67 | """
68 | 1
69 | 1 2
70 | 1 2 3
71 | 1 2 3 4
72 | """
73 | x = 1
74 | while x <= 4:
75 | y = 1
76 | while y <= x:
77 | print(y, end=' ')
78 | y += 1
79 | print()
80 | x += 1
81 |
82 |
83 | # While Else
84 |
--------------------------------------------------------------------------------
/notes/c05_loops/code/c0502_for.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 | # %% [markdown]
3 | """
4 | ## For Loop
5 | - it is different than for loops in other programming languages.
6 | - for loop in python is similar to for each loop in other languages.
7 | - it iterates over the iterable such as list, tuple, etc.
8 |
9 | in other programming languages we have the following syntax
10 | for(; ; )
11 | for (int x =0; x<10; x++){
12 | // sdsd
13 | }
14 | # for python
15 | for in :
16 | statement(s)
17 | """
18 |
19 | # %% basic syntax
20 | singers = ['John Lennon', 'John Mayer', 'John Legend']
21 | for singer in singers:
22 | print(f'singer is {singer}')
23 |
24 | # %% for loop in range
25 |
26 | for x in range(10):
27 | print(f'the value is {x}')
28 |
29 | # %% looping through letters in a string
30 | for char in 'Elephant':
31 | print(f'the character is: {char}')
32 |
33 | # %% we can use break, continue, and else statements in for loop too
34 | for i in range(10):
35 | if i == 5:
36 | continue
37 | if i == 8:
38 | break
39 | print(f'current value of x is {i}')
40 | print('loop complete')
41 |
42 | # %%
43 | for x in range(10):
44 | x += 1
45 | # These lines do not fulfill else condition in while else
46 | # if x == 5:
47 | # continue
48 | # print(f'current value of x is {x}')
49 | # if x == 8:
50 | # break
51 | else:
52 | print(f'x is now {x}, now Exiting!!')
53 |
--------------------------------------------------------------------------------
/notes/c05_loops/code/c0504_pattern_generation.py:
--------------------------------------------------------------------------------
1 | # Nested loop for pattern Generation
2 | """
3 | A A A A A
4 | A A A A A
5 | A A A A A
6 | A A A A A
7 | A A A A A
8 | """
9 |
10 | for i in range(5):
11 | for j in range(5):
12 | print('A', end=' ')
13 | print()
14 |
15 | """
16 | 1
17 | 1 2
18 | 1 2 3
19 | 1 2 3 4
20 | 1 2 3 4 5
21 | """
22 | """
23 | Row 1: No. of cols = 1
24 | Row 2: No. of cols = 2
25 | Row 3: No. of cols = 3
26 | ...
27 | """
28 | for row in range(1, 6):
29 | for col in range(1, row + 1):
30 | print(col, end=' ')
31 | print()
32 |
33 |
34 | for row in range(1, 6):
35 | for col in range(1, row + 1):
36 | print(col * 3, end=' ')
37 | print()
38 |
39 |
40 | """
41 | 1
42 | 2 2
43 | 3 3 3
44 | 4 4 4 4
45 | 5 5 5 5 5
46 | """
47 | """
48 | Row 1: No. of cols = 1, values = 1
49 | Row 2: No. of cols = 2, values = 2
50 | Row 3: No. of cols = 3, values = 3
51 | ...
52 | """
53 | for row in range(1, 6):
54 | for col in range(1, row + 1):
55 | print(row, end=' ')
56 | print()
57 |
58 |
59 | """
60 | 1
61 | 2 1
62 | 3 2 1
63 | 4 3 2 1
64 | 5 4 3 2 1
65 |
66 | Row 1: No. of cols = 1, spaces = 4, value = 1
67 | Row 2: No. of cols = 2, spaces = 3, value = Row -> 1
68 | Row 3: No. of cols = 3, spaces = 2
69 |
70 | """
71 | for row in range(1, 6):
72 | # printing out spaces
73 | print(' ' * (5 - row), end='')
74 | # printing out patterns
75 | for col in range(row, 0, -1):
76 | print(col, end=' ')
77 | print()
78 |
--------------------------------------------------------------------------------
/notes/c05_loops/code/c0505_list_comprehension.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 | # %% [markdown]
3 | """
4 | ## List Comprehension
5 | List Comprehension creates a new list by applying an expression to each element
6 | of an iterable.
7 |
8 |
9 | Basic Syntax:
10 | [ for in ]
11 |
12 | With if condition:
13 | [ for in if ]
14 |
15 | """
16 | # %% creating a square of numbers from a list
17 | numbers = [1, 2, 3, 4, 5, 6, 7, 8]
18 |
19 | # regular method
20 | result = []
21 | for x in numbers:
22 | result.append(x ** 2)
23 |
24 | # list comprehension
25 | result = [x ** 2 for x in range(1, 11)]
26 | print(result)
27 |
28 | # %%
29 | # multiplying each value of a list by 3
30 | numbers = [1, 45, 6, 23, 89, 2, 78, 41]
31 | result = [x * 2 for x in numbers]
32 | # %% conditions inside list comprehension
33 | x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
34 |
35 | # I want y to be odds
36 | # and z to be evens
37 |
38 | y = [y for y in x if y % 2 == 0]
39 | z = [y for y in x if y % 2 != 0]
40 | print(y)
41 | print(z)
42 |
43 | # %%
44 | """
45 | Creating a multi-dimensional list [[1,2,3],[1,2,3],[1,2,3]]
46 | """
47 | # %% Step 1, visualize with traditional loop
48 | outer = []
49 | for x in range(1, 4):
50 | # some other non-related expressions are not used
51 | inner = []
52 | for y in range(1, 4):
53 | inner.append(y)
54 | outer.append(inner)
55 | print(outer)
56 |
57 | # %% step 2 replace inner loop with list comprehension
58 | outer = []
59 | for x in range(1, 4):
60 | inner = [y for y in range(1, 4)]
61 | outer.append(inner)
62 | print(outer)
63 |
64 | # %% step 3 simplify the inner loop
65 |
66 | outer = []
67 | for x in range(1, 4):
68 | outer.append([y for y in range(1, 4)])
69 | print(outer)
70 | # %% step 4, replace the outer loop with list comprehension
71 |
72 | outer = [[y for y in range(1, 4)] for x in range(1, 4)]
73 | print(outer)
74 |
75 | # %% example 2, 3d list
76 | """
77 | Expected list
78 | [
79 | [[1,2], [1,2]],
80 | [[1,2], [1,2]]
81 | ]
82 | """
83 | outer = [[[z for z in range(1, 3)] for y in range(1, 3)] for x in range(1, 3)]
84 | print(outer)
85 |
86 | # %%
87 | lst = [1, 2, 3, 4, 5]
88 | # I want output to be [1, 4, 9, 16, 25]
89 | new_lst = []
90 | for x in lst:
91 | new_lst.append(x ** 2)
92 | lst = new_lst
93 | print(lst)
94 |
95 | # %% with list comprehension
96 | lst = [1, 2, 3, 4, 5]
97 | lst = [x ** 2 for x in lst]
98 | print(lst)
99 |
--------------------------------------------------------------------------------
/notes/c06_functions/Chapter 6.2 default arguments.md:
--------------------------------------------------------------------------------
1 | # Chapter 6.2: Default parameters in functions
2 |
3 | **Table of Contents**:
4 |
5 | - [Chapter 6.2: Default parameters in functions](#chapter-62-default-parameters-in-functions)
6 | - [Introduction](#introduction)
7 |
8 | ## Introduction
9 |
10 | While defining function, we can add default values to some or all parameters so
11 | that it takes default value when we do not pass values while calling the
12 | function. We need to add default parameters only after required parameters are
13 | added otherwise it raises an exception
14 |
15 | **Example 1:** A program that can add either 2 or 3 numbers
16 |
17 | ```python
18 | def add_numbers(a, b, c = 0):
19 | return a + b + c
20 |
21 | print(add_numbers(1, 2)) # 3
22 | print(add_numbers(1, 2, 3)) # 6
23 | ```
24 |
25 | Here, in this example, if we pass 2 arguments `(1,2)`, the default value of `c`
26 | is already `0` so when we call function, it does not give us exception.
27 |
28 | If we try to define required parameters after default parameters, then it raises
29 | an exception.
30 |
31 | ```python
32 | def add_numbers(a=0, b, c):
33 | return a + b + c
34 | # SyntaxError: non-default argument follows default argument
35 | ```
36 |
37 | **Example 2:** internationalization
38 |
39 | ```python
40 | def greet(key, lang='en'):
41 | words = {
42 | 'hello': {
43 | 'en': 'Hello',
44 | 'fr': 'Bonjour',
45 | 'np': 'नमस्कार',
46 | 'jp': 'こにちは'
47 | },
48 | 'bye': {
49 | 'en': 'Bye',
50 | 'fr': 'au revoir',
51 | 'np': 'फेरी भेटौला',
52 | 'jp': 'さよなら'
53 | }
54 | }
55 | print(words[key][lang])
56 | greet('hello') # Hello (default value of lang is 'en')
57 | greet('hello', 'fr') # Bonjour
58 | greet('hello', 'np') # नमस्कार
59 | ```
60 |
--------------------------------------------------------------------------------
/notes/c06_functions/Chapter 6.3 args kwargs.md:
--------------------------------------------------------------------------------
1 | # Chapter 6.3: Arbitrary arguments in python
2 |
3 | **Table of Contents**:
4 |
5 | - [Chapter 6.3: Arbitrary arguments in python](#chapter-63-arbitrary-arguments-in-python)
6 | - [Background](#background)
7 | - [Arbitrary number of arguments](#arbitrary-number-of-arguments)
8 | - [Arbitrary number of keyword arguments](#arbitrary-number-of-keyword-arguments)
9 |
10 | ## Background
11 |
12 | As our functions become more and more dynamic, it is not possible to define a
13 | function with maximum number of default arguments which makes our code more
14 | complicated. Example, we want to be able to add either 3, or 4, or 5 or even
15 | 10 arguments.
16 |
17 | **The Traditional Approach:**
18 |
19 | ```python
20 | def add_numbers(a, b, c=0, d=0, e=0, f=0, g=0):
21 | return a + b + c + d + e + f + g
22 |
23 | print(add_numbers(1,2)) # 3
24 | print(add_numbers(1,2, 3)) # 6
25 | print(add_numbers(1,2, 3, 4)) # 10
26 | print(add_numbers(1,2, 3, 4, 5)) # 15
27 | print(add_numbers(1,2, 3, 4, 5, 6)) # 21
28 | ```
29 |
30 | Here, the main problem is default parameters in all optional values which even
31 | decreases the code readability. If we could pass dynamic number of arguments,
32 | then we did not need to set default values to each variable.
33 |
34 | ## Arbitrary number of arguments
35 |
36 | In the above scenario, if we use arbitrary number of arguments, then we could do
37 | the following:
38 |
39 | ```python
40 | def add_numbers(a, b, *args):
41 | sum_value = a + b
42 | for item in args:
43 | sum_value += item
44 | return sum_value
45 | print(add_numbers(1, 2)) # 3
46 | print(add_numbers(1, 2, 3)) # 6
47 | print(add_numbers(1, 2, 3, 4)) # 10
48 | print(add_numbers(1, 2, 3, 4, 5)) # 15
49 | print(add_numbers(1, 2, 3, 4, 5, 6)) # 21
50 | ```
51 |
52 | Here, add_number needs at lease 2 numbers to calculate the sum but is capable of
53 | accepting any number of arguments and perform addition
54 |
55 | ## Arbitrary number of keyword arguments
56 |
57 | Arbitrary number of keyword arguments are those arguments which are used as
58 | named parameters in a function call. They are extra arguments which will be
59 | treated as a dictionary whenever we want to access keys and values from it.
60 |
61 | ```python
62 | def display_student_detail(name, age, **kwargs):
63 | print(f'[ details of {age} years old student: {name}]')
64 | for key, value in kwargs.items():
65 | print(f'The {key} is {value}')
66 |
67 | display_student_detail(
68 | 'John Doe', 20,
69 | subject='Science', class_teacher='John Lennon', address='Singapore'
70 | )
71 |
72 | ```
73 |
74 | **Output:**
75 |
76 | ```
77 | [ details of 20 years old student: John Doe]
78 | The subject is Science
79 | The class_teacher is John Lennon
80 | The address is Singapore```
81 | ```
82 |
--------------------------------------------------------------------------------
/notes/c06_functions/Chapter 6.4 recursive functions.md:
--------------------------------------------------------------------------------
1 | # Chapter 6.4: Recursive Function
2 |
3 | **Table of Contents**:
4 |
5 | - [Chapter 6.4: Recursive Function](#chapter-64-recursive-function)
6 | - [Introduction](#introduction)
7 |
8 | ## Introduction
9 |
10 | Recursive function is a function which calls itself. A recursive function is
11 | generally created to iterate over the dynamic parameter until it reaches the
12 | recursion limit. Recursive function contains 2 different elements:
13 |
14 | 1. recursion limit
15 | 2. recursive function call
16 |
17 | **Basic Syntax**
18 |
19 | ```python
20 | def function_name(params):
21 | if condition:
22 | function_name(updated_params)
23 | else: # recursion Limit
24 | return final_value
25 | ```
26 |
27 | **Example 1:** A recursive function that prints out the factorial of a number.
28 |
29 | - `5 ! = 5 * 4 * 3 * 2 * 1 = 120`
30 | - `0 ! = 1`
31 |
32 | ```python
33 | def factorial(n):
34 | if n > 0:
35 | return(n * factorial(n-1))
36 | return 1
37 |
38 | print(factorial(7)) # 5040
39 | ```
40 |
41 | **Example 2:** A recursive function that prints out the first nth item of a
42 | fibonacci series `1, 1, 2, 3, 5, 8, 13, 21, 34, ...`.
43 |
44 | ```python
45 | def fibonacci(n):
46 | if n > 1:
47 | return(fibonacci(n-1) + fibonacci(n-2))
48 | return n
49 |
50 | item = fibonacci(7)
51 |
52 | print(item) # 13
53 | ```
54 |
55 | **Example 3:** A recursive function to convert decimal to binary
56 |
57 | ```python
58 | def dec2bin(n: int):
59 | if n != 0:
60 | return dec2bin(n // 2) * 10 + n % 2
61 | else:
62 | return 0
63 |
64 | print(dec2bin(14)) # 1110
65 | ```
66 |
--------------------------------------------------------------------------------
/notes/c06_functions/Chapter 6.5 lambda.md:
--------------------------------------------------------------------------------
1 | # Chapter 6.5: Lambda Expressions
2 |
3 | **Table of Contents**:
4 |
5 | - [Chapter 6.5: Lambda Expressions](#chapter-65-lambda-expressions)
6 | - [Introduction](#introduction)
7 | - [Lambda function with Zero arguments](#lambda-function-with-zero-arguments)
8 | - [Ternary operators on lambda functions](#ternary-operators-on-lambda-functions)
9 |
10 | ## Introduction
11 |
12 | In python Lambda functions or lambda expressions are single line expressions
13 | with no name that can have any number of arguments. Lambda functions are also
14 | called as anonymous functions or one-liner functions. Lambdas are useful when
15 | we have to do small repetitive tasks.
16 |
17 | **Basic Syntax:**
18 |
19 | ```python
20 | value = lambda arg_1, arg_2, ..., arg_n : return_expression
21 | ```
22 |
23 | here, everything separated with comma before `:` are arguments and the
24 | expression after `:` is used as the return value or expression. Lambda
25 | expression do not expect `return` keyword however whatever expression is
26 | written after the colon `:`, will be treated as return statement.
27 |
28 | **Example 1:** A lambda to add 2 values
29 |
30 | ```python
31 | add_num = lambda a,b: a+b
32 |
33 | print(add_num(5, 6)) # 11
34 | ```
35 |
36 | ## Lambda function with Zero arguments
37 |
38 | We can also create a lambda expression with zero arguments. To create a lambda
39 | with zero arguments, we just use `lambda` keyword before a colon `:`.
40 |
41 | **Example 2:** Lambda to print Hello World
42 |
43 | ```python
44 | hello_world = lambda: "Hello World"
45 |
46 | print(hello_world()) # Hello World
47 | ```
48 |
49 | ## Ternary operators on lambda functions
50 |
51 | ```python
52 | odd_even = lambda num: 'Even' if num % 2 == 0 else 'Odd
53 |
54 | print(odd_even(14)) # Even
55 | ```
56 |
--------------------------------------------------------------------------------
/notes/c06_functions/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 6 Functions
2 |
3 | **Table of contents**:
4 |
5 | - [6.1. Introduction to functions](Chapter%206.1%20function.md)
6 | - Defining a function
7 | - Calling a function
8 | - The pass statement
9 | - The return statement
10 | - local, non-local, and global variables
11 | - [6.2. Function with default arguments](Chapter%206.2%20default%20arguments.md)
12 | - [6.3. Arbitrary Arguments and Keyword Arguments](Chapter%206.3%20args%20kwargs.md)
13 | - [6.4. Recursive Functions](Chapter%206.4%20recursive%20functions.md)
14 | - [6.5. Lambda Functions](Chapter%206.5%20lambda.md)
15 |
--------------------------------------------------------------------------------
/notes/c06_functions/code/c0602_default_parameters.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 | """
3 | Default Parameters in functions
4 |
5 | - default parameters are the parameters that has values assigned if it is not supplied
6 | while calling the function
7 | - default parameters always should be at last
8 | - any non default parameters after default causes error
9 | - default parameters are completely optional while calling
10 | """
11 |
12 |
13 | # %%
14 | def add(x, y, z=0):
15 | return x + y + z
16 |
17 |
18 | print(add(3, 4, 10))
19 |
20 | print(add(3, 4))
21 |
22 |
23 | # %% example use case
24 | def greet(key, lang='en'):
25 | dict_1 = {
26 | 'hello': {
27 | 'en': 'Hello',
28 | 'fr': 'Bonjour',
29 | 'np': 'नमस्कार',
30 | 'jp': 'こにちは'
31 | },
32 | 'bye': {
33 | 'en': 'Bye',
34 | 'fr': 'au revoir',
35 | 'np': 'फेरी भेटौला',
36 | 'jp': 'さよなら'
37 | }
38 | }
39 | print(dict_1[key][lang])
40 |
41 |
42 | greet('hello', 'fr')
43 | greet('hello', 'jp')
44 | greet('hello')
45 |
46 |
47 | # %% power of the number with default value 2
48 | def power(x, y=2):
49 | return x ** y
50 |
51 |
52 | print(power(10))
53 | print(power(10, 4))
54 |
55 | # %%
56 | lst = [1, 2, 3, -3, -5, 7, 6, -4, 8, -8]
57 | new_list = []
58 | for (index, value) in enumerate(lst):
59 | for x in lst[index + 1:]:
60 | tmp_sum = x + value
61 | for n in lst:
62 | if tmp_sum + n == 0:
63 | new_list = [*new_list, [value, x, n]]
64 | print(new_list)
65 |
--------------------------------------------------------------------------------
/notes/c06_functions/code/c0605_lambda.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 | # %%
3 | """
4 | Python Lambdas
5 | - lambdas are also known as anonymous functions
6 | - they are tiny functions which contains one statement
7 | - lambdas are written in a single line
8 | - they can have zero or more arguments
9 | - they are useful when we have to do small repetitive tasks
10 |
11 | structure of lambda
12 | = lambda ,, ...:
13 |
14 | - everything before `:` and after `lambda` are arguments
15 | - expression after `:` is the return value
16 | example:
17 | f1 = lambda a,b: a+b
18 | f2 = lambda a: a**2
19 | f3 = lambda : print("Hey There!")
20 | """
21 |
22 |
23 | # %% regular function to add 2 numbers and return value
24 |
25 | def add_numbers(a, b):
26 | return a + b
27 |
28 |
29 | add_numbers(4, 5)
30 |
31 | # %% same operation with lambda
32 |
33 | add_num = lambda a, b: a + b
34 | add_num(4, 5)
35 |
36 | # %%
37 | # multiply 2 numbers
38 | a = 6
39 | b = 8
40 | e = 10
41 |
42 | multiply = lambda c, d: c * d + e
43 |
44 | multiply(a, b)
45 | # %% lambda with no arguments
46 |
47 |
48 | print_hello = lambda: print("hello world")
49 | print_hello()
50 |
51 | # %% conditionals inside lambda
52 |
53 | # ternary operator "Even" if x % 2 == 0 else 'Odd'
54 |
55 | odd_even = lambda x: "Even" if x % 2 == 0 else 'Odd'
56 |
57 | print(odd_even(14))
58 |
59 | # %% double the list
60 | lst = [1, 2, 3, 4, 5]
61 |
62 | double1 = lambda l: [x * 2 for x in l]
63 |
64 | double1(lst)
65 |
66 | lst = [x * 2 for x in lst]
67 |
--------------------------------------------------------------------------------
/notes/c07_oop/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 7 Object-Oriented Programming (OOP)
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 | >
9 |
10 | **Table of contents**:
11 |
12 | - [7.1. Introduction to OOP](Chapter-7.1-oop.md)
13 | - Class
14 | - Class Attributes, Methods and the `self` parameter
15 | - The constructor method
16 | - Built-in class attributes
17 | - object
18 | - [7.2. Class Methods and Static Methods](Chapter-7.2-Class-Methods-and-Static-Methods.md)
19 | - [7.3. Operator Overloading](Chapter-7.3-Operator-Overloading.md)
20 | - [7.4. Encapsulation](Chapter-7.4-Encapsulation.md)
21 | - [7.5. Inheritance and Polymorphism](Chapter-7.5-Inheritance-and-Polymorphism.md)
22 |
--------------------------------------------------------------------------------
/notes/c07_oop/code/c0701_oop_intro.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 | """
3 | # Object Oriented Programming
4 | - it covers the limitations of functional approach of programming
5 | - functional programming is just a list of instructions but OOP approach uses block of code
6 | that contains both data and functions that operate on those data.
7 | - collection of those data and functions is known as object
8 |
9 | Features:
10 |
11 | - Inheritance
12 | - Polymorphism
13 | - encapsulation
14 | - objects
15 | - Data Abstraction
16 | """
17 |
--------------------------------------------------------------------------------
/notes/c07_oop/code/c0702_class.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 | # %%
3 | """
4 | # Class
5 |
6 | - building block of OOP
7 | - we must instantiate an object for a class to perform specific task
8 |
9 |
10 |
11 | ## basic Structure
12 |
13 | class :
14 |
15 |
16 |
17 |
18 | # Basic Convention
19 |
20 | - Class names are in `UpperCamelCase`
21 | - MyClass
22 | - ClassOne
23 | - Animal
24 | - Attributes (variables & methods) are in `snake_case`
25 | - It is better to have 2 vertical spaces before and after classes
26 | - It is better to have a vertical space before and after methods
27 |
28 |
29 | Class works just like a group or a container for variables and functions
30 |
31 | Person
32 | - first_name
33 | - last_name
34 | - address
35 |
36 | - show_fullname()
37 |
38 | detailed structure of a class:
39 |
40 | class ClassName:
41 | var1 =
42 | var2 =
43 |
44 | def function_1(self):
45 |
46 |
47 | The basics of class includes the following:
48 |
49 | - Attributes
50 | - Class Attributes
51 | - Instance Attributes
52 | - Methods
53 | - self parameter
54 |
55 | - Instance
56 |
57 | - Initializer or constructor [ __init__() method ]
58 |
59 |
60 | """
61 |
62 |
63 | class Person:
64 | first_name = ''
65 | last_name = ''
66 | address = ''
67 | city = ''
68 | age = 0
69 |
70 | def show_fullname(self):
71 | print(f"{self.first_name} {self.last_name}")
72 |
73 |
74 | # instantiating the class
75 | mickey = Person()
76 | # what happens after creating an instance of a class?
77 | # discussion in lecture
78 |
79 | # Assigning the values to attrobutes
80 | mickey.first_name = "Mickey"
81 | mickey.last_name = "Mouse"
82 | mickey.address = "123 Fantasy Way"
83 | mickey.city = "Anahiem"
84 | mickey.age = 73
85 |
86 | # Accessing the values from attributes
87 |
88 | print(mickey.first_name)
89 |
90 | # calling the methods from the instance
91 | mickey.show_fullname()
92 |
93 |
94 | # example of a class (Animal) with the __init__() method
95 | class Animal:
96 | name = "" # Class Attribute
97 | paws = 0 # Instance Attribute
98 | word = ""
99 |
100 | def __init__(self, name, word):
101 | """
102 | Constructor or initializer method
103 | """
104 | self.name = name
105 | self.word = word
106 |
107 | def __str__(self):
108 | return f"Instance of Animal named {self.name}"
109 |
110 | def speak(self):
111 | print(f'I am {self.name} and I speak with {self.word}s.')
112 |
113 | def rename(self, new_name):
114 | self.name = new_name
115 |
116 |
117 | elephant = Animal("John", "trumpet")
118 |
119 | elephant.speak()
120 | elephant.rename("Jane")
121 | elephant.speak()
122 |
123 | dog = Animal('rockey', 'bark')
124 | dog.speak()
125 |
126 | print(dog)
127 |
--------------------------------------------------------------------------------
/notes/c07_oop/code/c0703_classmethod_staticmethod.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | # Class Methods and static methods
5 |
6 | Class Methods
7 | - Class methods are special kind of methods that are bound to the class instead of the instance
8 | - If we create an instance and change the property of the object, it is not going to affect the property
9 | of the classmethod.
10 |
11 | Static Methods
12 | - Static methods are methods that do not bind to anything at all and simply return the underlying function
13 | without any transformation.
14 | - They just behave like a function. Only the difference is they are called along with the class name.
15 | """
16 |
17 |
18 | class Animal:
19 | legs = 4
20 |
21 | @classmethod # bound to the class so class is passed as the first argument instead of self
22 | def print_legs(cls):
23 | print('Total no. of legs: ', cls.legs)
24 |
25 | def print_legs_1(self): # Regular method that is bound to the instance so self is passed as the first argument
26 | print('Total no. of legs: ', self.legs)
27 |
28 |
29 | print(Animal.legs) # 4
30 |
31 | Animal.print_legs() # 4 # not instantiated
32 | Animal().print_legs() # 4 # instantiated
33 |
34 | human = Animal()
35 | human.legs = 2
36 | human.print_legs() # 4 # instantiated and attribute is changed but still prints 4 instead of 2
37 |
38 | print(human.legs) # 2
39 |
40 | print(Animal.legs) # 4
41 |
42 |
43 | # def cm_to_m(value: float):
44 | # return value/100
45 |
46 | # def kg_to_lb(value: float):
47 | # return value * 2.20462
48 |
49 | # cm_value = 3536
50 | # m_value = cm_to_m(cm_value)
51 |
52 | # print(m_value)
53 |
54 | # wt_kg = 5
55 | # print(kg_to_lb(wt_kg))
56 |
57 | # Static Methods
58 | # Just works like a regular function inside the module
59 |
60 | class Length:
61 | cm = 5438
62 |
63 | @staticmethod # unbound since it does not invoke either of class or an instance.
64 | def cm_to_m(value: float):
65 | return value / 100
66 |
67 | @staticmethod
68 | def m_to_cm(value: float):
69 | return value * 100
70 |
71 |
72 | class Weight:
73 | @staticmethod
74 | def kg_to_lb(value: float):
75 | return value * 2.20462
76 |
77 | @staticmethod
78 | def lb_to_kg(value: float):
79 | return value / 2.20462
80 |
81 |
82 | print(Length.cm_to_m(Length.cm))
83 |
--------------------------------------------------------------------------------
/notes/c07_oop/code/c0705_property.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 |
5 | @property decorator in python classes
6 |
7 | - They look like regular object variables but are capable of attaching custom behavior to the class.
8 | - They are used as alternative of getters and setters
9 | - whenever we create a property inside a class, it's behavior will be tightly controlled.
10 |
11 | for example we want to add a private variable to class and want to access and modify it.
12 | I can achieve it using getter and setter method along with private variable.
13 |
14 | or I can add getter, setter and deleter property so that I can access it
15 | similar to attributes rather than calling methods.
16 |
17 | structure:
18 | class ABC:
19 |
20 | @property
21 | def my_property(self):
22 | # property body
23 | """
24 |
25 | # %%
26 |
27 |
28 | # Using regular getter and setter
29 | class Student:
30 |
31 | def __init__(self, count: int):
32 | self.__count = count
33 |
34 | def get_count(self):
35 | return self.__count
36 |
37 | def set_count(self, value):
38 | print("I can do other things while updating my value")
39 | self.__count = value
40 |
41 |
42 | class_1 = Student(10)
43 | print(class_1.get_count()) # Getter
44 | class_1.set_count(11)
45 | print(class_1.get_count()) # Setter
46 |
47 | # %%
48 | # it can be achieved using the property decorator as follows
49 | """
50 | Getter:
51 | Whenever the getter property is added, the method can be used as the regular variable
52 | we can access this using the regular access method instead of calling the function
53 | eg: value = obj.count
54 |
55 |
56 | syntax:
57 |
58 | @property
59 | def (params):
60 | method body
61 |
62 | Whenever the setter property is added, the method can be used as the regular variable and we can assign
63 | the value using regular assignment operator "="
64 |
65 | eg: obj.count = 10
66 |
67 | syntax:
68 |
69 | @.setter
70 | def (params):
71 | method body
72 |
73 | """
74 |
75 |
76 | class Students:
77 |
78 | def __init__(self, count: int):
79 | self.__count = count
80 |
81 | @property
82 | def count(self): # Getter
83 | return self.__count
84 |
85 | @count.setter # Setter
86 | def count(self, value):
87 | print("I can do other things while updating my value")
88 | self.__count = value
89 |
90 |
91 | class_1 = Students(10)
92 | print(class_1.count)
93 | class_1.count = 11
94 | print(class_1.count)
95 |
96 | # %%
97 |
--------------------------------------------------------------------------------
/notes/c07_oop/code/c0707_inheritance.py:
--------------------------------------------------------------------------------
1 | # © https://sudipghimire.com.np
2 |
3 | # %% [markdown]
4 | """
5 | # Inheritance
6 | - This concept is exactly similar to biological inheritance where child inherits the feature of parent.
7 | - in inheritance, there exists a parent class and child classes which inherits parent's behaviors.
8 | - The base class will be the parent class and the class that is derived from the base class will
9 | be treated as a child class.
10 |
11 | Basic Structure
12 | class Parent:
13 |
14 |
15 |
16 | class Child(Parent):
17 |
18 |
19 | """
20 |
21 |
22 | # %% Example Rectangle
23 | class Rectangle: # Parent Class
24 |
25 | def __init__(self, width: float, height: float):
26 | self.width = width
27 | self.height = height
28 |
29 | def perimeter(self):
30 | return 2 * (self.width + self.height)
31 |
32 | def area(self):
33 | return self.width * self.height
34 |
35 | def diagonal_length(self):
36 | return (self.width ** 2 + self.height ** 2) ** (1 / 2)
37 |
38 |
39 | # Child Class
40 | class Square(Rectangle): # Here, the class `Square` is inheriting all the properties from the class `Rectangle`.
41 |
42 | def __init__(self, width):
43 | super().__init__(width=width, height=width) # in python3, super() does not require any arguments
44 |
45 | def diagonal_length(self):
46 | return self.width * (2 ** .5)
47 |
48 |
49 | # %%
50 | room_1 = Rectangle(5, 10)
51 | print(f'Area of room 1: {room_1.area()}')
52 | print(f'Perimeter of room 1: {room_1.perimeter()}')
53 | print(f'Diagonal length of room 1: {room_1.diagonal_length()}')
54 |
55 | # %%
56 | room_2 = Square(5)
57 |
58 | print(f'Area of room 2: {room_2.area()}')
59 | print(f'Perimeter of room 2: {room_2.perimeter()}')
60 | print(f'Diagonal length of room 2: {room_2.diagonal_length()}')
61 |
62 | # %% builtin functions that work with inheritance
63 | """
64 | isinstance()
65 | the method returns True if an object is the instance of the class ir any of the derived classes of a class
66 |
67 | """
68 |
69 |
70 | class Parent:
71 | pass
72 |
73 |
74 | class Child(Parent):
75 | pass
76 |
77 |
78 | obj1 = Parent()
79 | obj2 = Child()
80 | """
81 | here,
82 | - obj1 is an instance of a class Parent
83 | - obj2 is an instance of a class Child
84 |
85 | Note:
86 | - instance of a parent class is not an instance of a child class
87 | - instance of a child class is also an instance of a parent class since it is inherited from a parent class
88 | """
89 |
90 | print(isinstance(room_2, Square))
91 | print(isinstance(room_1, Rectangle))
92 | print(isinstance(room_1, Square))
93 | print(isinstance(room_2, Rectangle))
94 | """
95 | issubclass()
96 | the method returns True if the derived class is the subclass of the base class.
97 | """
98 |
99 | print(issubclass(Square, Rectangle))
100 | print(issubclass(Rectangle, Square))
101 | print(issubclass(Rectangle, object))
102 |
--------------------------------------------------------------------------------
/notes/c07_oop/code/c0708_multiple_inheritance.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Multiple Inheritance.
5 |
6 | Multiple inheritance means inheriting the behavior from 2 or more parent classes.
7 |
8 | The structure would be
9 |
10 | class A:
11 |
12 |
13 |
14 | class B:
15 |
16 |
17 |
18 | classs C(A, B):
19 |
20 |
21 |
22 | - In multiple inheritance, Python uses C3 Linearization algorithm to determine the order in which
23 | to resolve class attributes and methods. The process is also known as Method Resolution Order (MRO)
24 |
25 | To learn more about C3 linearization, you can check the link below:
26 | https://en.wikipedia.org/wiki/C3_linearization
27 |
28 | """
29 |
30 |
31 | class Parent1:
32 | x = 5
33 |
34 |
35 | class Parent2:
36 | x = 10
37 |
38 |
39 | class Child(Parent1, Parent2): # According to C3 linearization, Child accepts x from Parent1.
40 | pass
41 |
42 |
43 | child = Child()
44 |
45 | print(child.x)
46 |
47 |
48 | class Shop:
49 | def __init__(self) -> None:
50 | self.__reg_no=0
51 |
52 | def get_reg_no(self):
53 | return self.__reg_no
54 |
55 | def set_reg_no(self, value):
56 | self.__reg_no = value
57 |
58 |
59 |
60 | class CoffeeShop(Shop):
61 | coffee_price = 30
62 |
63 |
64 | class Bakery(Shop):
65 | dough_nut_price = 10
66 |
67 |
68 | class Restaurant(CoffeeShop, Bakery):
69 | pizza_price = 20
70 |
71 | def __init__(self, reg_no) -> None:
72 | super().__init__()
73 | self.set_reg_no(reg_no)
74 |
75 |
76 | r1 = Restaurant('A12345')
77 |
78 | r1.dough_nut_price = 15
79 |
80 | print(r1.get_reg_no())
81 | print(r1.dough_nut_price)
82 | print(r1.coffee_price)
83 | print(r1.pizza_price)
84 |
85 | print(isinstance(r1, Restaurant))
86 | print(isinstance(r1, Bakery))
87 | print(isinstance(r1, CoffeeShop))
88 | print(isinstance(r1, Shop))
89 |
--------------------------------------------------------------------------------
/notes/c07_oop/code/c0709_monkey_patching.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Monkey Patching
5 |
6 | - it is the process of adding new variable or method to a class after it's been defined
7 | - we can introduce a new instance attribute to an object even after it has been initialized
8 | - monkey patching is useful when we do not want to perform inheritance or create a child class and
9 | change the behavior of the previously defined classes or previously instantianted objects.
10 |
11 | - if we monkey patch the instance attribute, it is not going to change the class template, instead
12 | it affects only the instance we've created
13 | """
14 |
15 |
16 | class Student:
17 | def __init__(self, name):
18 | self.name = name
19 |
20 | Student.grade = 1 # Monkey Patching Class attribute
21 | Student.major = 'science'
22 | john = Student("John Doe")
23 |
24 | print(john.name)
25 | john.roll = 5 # Monkey Patching instance attribute
26 | print(john.roll)
27 |
28 |
29 | def display_name(self):
30 | print(self.name)
31 |
32 |
33 | def display_major(self):
34 | print(self.major)
35 |
36 |
37 | Student.display_name = display_name # Monkey patching class attribute
38 | Student.display_major = display_major
39 |
40 | john.display_name()
41 |
42 |
43 | jane = Student("Jane")
44 | print(jane.roll)
45 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz/q0701.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a python class with following properties:
5 |
6 | 1. private class attribute
7 | 2. public class attribute
8 | 3. instance attribute
9 | 4. initializer method
10 | 5. string representation method [__str__()]
11 | """
12 |
13 | # Your solution here
14 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz/q0702.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a class named Person and add the following attributes and methods:
5 | - name: Instance attribute
6 | - age: Instance attribute
7 | - gender: Instance attribute
8 | - weight: Class attribute
9 |
10 | - year_of_birth():
11 | Returns the year by subtracting the age from the current year.
12 |
13 | - get_pronouns():
14 | Returns list of ['he', 'his', 'him'] or ['she', 'her', 'hers'] by checking the gender
15 |
16 | the initializer method should take name, age, and gender
17 | """
18 |
19 | import datetime
20 |
21 | current_year = datetime.date.today().year
22 |
23 |
24 | # answer
25 | class Person:
26 | pass # Write your code here
27 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz/q0703.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a class Employee that contains different attributes.
5 | - id
6 | - first_name
7 | - last_name
8 | - project
9 | - department
10 | - salary
11 |
12 | Make attributes project, department, and salary as private and use getter and setter methods to get and
13 | set respective values
14 |
15 | id should be private and can only be initialized when employee instance is created
16 |
17 | first_name and last_name should be initialized with constructor and can be changed any time
18 | """
19 |
20 | # answer
21 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz/q0704.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create 3 different classes Length, Weight, and Time
5 |
6 | Add respective attributes to store values.
7 | Add static methods for conversion of different units.
8 |
9 | Example:
10 | Length: cm to inches, kilometer to miles, etc
11 | Weight: KG to pounds, gram to Ounces, etc.
12 | Time: seconds to hours, milliseconds to seconds
13 |
14 | """
15 |
16 | # answer
17 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz/q0705.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | create a classes Rectangle, Circle and Box and add respective attributes eg.: radius, length, width, height
5 |
6 | for Rectangle, create methods to find out:
7 | perimeter
8 | area
9 | length of diagonal
10 |
11 | for Circle, create methods to find out:
12 | circumference
13 | area
14 | diameter
15 |
16 | for Box, create methods to find out:
17 | surface area
18 | volume
19 | """
20 |
21 | # answer
22 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz/q0706.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create 2 different classes Major and Subject
5 |
6 | create an instance of Major and add different subjects to the 'subject' attribute as a set of subjects
7 |
8 | Create another instance for Major and add subjects to the instance
9 |
10 | add operator overloading to add different methods so that we can create another major.
11 |
12 | Example we have majors science, commerce
13 |
14 | if we call
15 |
16 | new_major = science + commerce
17 |
18 | it should be able to return the new instance of Major with all subjects inside of them
19 |
20 | Note:
21 | you can try other operator overloading options too.
22 | """
23 |
24 | # answer
25 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz/q0708.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a class Vehicle add some attributes and methods to it
5 | - name
6 | - brand
7 | - wheels_count
8 | - engine_type
9 | - braking_system
10 |
11 |
12 | Create a child class HeavyVehicle and inherit all the attributes from the parent class Vehicle
13 | - change the wheels_count from 4 to 6 in the initializer or accept the value while instantiating
14 | - add more instance attributes like max_load, mileage, etc.
15 |
16 | Create a child class Bike and inherit all the attributes from the parent class Vehicle
17 | - change the wheels_count from 4 to 2 in the initializer
18 | - add setter or getter methods or property to add bike number, and owner name
19 | - try adding property instead of setter or getter for passenger/ pillion attribute
20 |
21 | create different instances of Vehicle, HeavyVehicle, and Bike and check whether each other are subclasses
22 | and instances of different classes or not.
23 |
24 | """
25 |
26 | # answer
27 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz_solution/q0701.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a python class with following properties:
5 |
6 | 1. private class attribute
7 | 2. public class attribute
8 | 3. instance attribute
9 | 4. initializer method
10 | 5. string representation method [__str__()]
11 | """
12 |
13 | # This is the example solution you can create on your own style
14 | class MyClass:
15 | var_1 = 5 # 2
16 | __var_2 = 10 # 1
17 |
18 | def __init__(self): # 4
19 | self.var_3 = "Hello" # 3
20 |
21 | def __str__(self) -> str: # 5
22 | return f"MyClass with var3: {self.var_3}"
23 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz_solution/q0702.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a class named Person and add the following attributes and methods:
5 | - name: Instance attribute
6 | - age: Instance attribute
7 | - gender: Instance attribute
8 | - weight: Class attribute
9 |
10 | - year_of_birth():
11 | Returns the year by subtracting the age from the current year.
12 |
13 | - get_pronouns():
14 | Returns list of ['he', 'his', 'him'] or ['she', 'her', 'hers'] by checking the gender
15 |
16 | the initializer method should take name, age, and gender
17 | """
18 |
19 | import datetime
20 | from ntpath import join
21 |
22 | # answer
23 | MALE = 'male'
24 | FEMLALE = 'female'
25 |
26 | class Person:
27 |
28 | weight = 0
29 |
30 | def __init__(self, name: str, age: int, gender: str) -> None:
31 | self.name = name
32 | self.age = age
33 | self.gender = gender
34 |
35 | def year_of_birth(self):
36 | return datetime.date.today().year - self.age
37 |
38 | def get_pronouns(self):
39 | return ['he', 'his', 'him'] if self.gender == MALE else ['she', 'her', 'hers']
40 | # you can also accept other, unspecified option to gender for practice
41 |
42 |
43 |
44 | # Test
45 | john = Person("John Doe", 20, MALE)
46 |
47 | print(john.year_of_birth())
48 | print(john.get_pronouns())
49 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz_solution/q0703.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a class Employee that contains different attributes.
5 | - id
6 | - first_name
7 | - last_name
8 | - project
9 | - department
10 | - salary
11 |
12 | Make attributes project, department, and salary as private and use getter and setter methods to get and
13 | set respective values
14 |
15 | id should be private and can only be initialized when employee instance is created
16 |
17 | first_name and last_name should be initialized with constructor and can be changed any time
18 | """
19 |
20 | # answer
21 |
22 |
23 | class Employee:
24 |
25 | def __init__(self, id, first_name, last_name) -> None:
26 | self.__id = id
27 | self.first_name = first_name
28 | self.last_name = last_name
29 |
30 | self.__project = ''
31 | self.__department = ''
32 | self.__salary = ''
33 |
34 | def get_project(self):
35 | return self.__project
36 |
37 | def get_department(self):
38 | return self.__department
39 |
40 | def get_salary(self):
41 | return self.__salary
42 |
43 | def set_project(self, project: str):
44 | self.__project = project
45 |
46 | def set_department(self, department: str):
47 | self.__department = department
48 |
49 | def set_salary(self, salary: int):
50 | self.__salary = salary
51 |
52 |
53 | # Test
54 | john = Employee(1, "John", "Doe")
55 |
56 | john.set_project("ABC management system")
57 |
58 | john.set_salary(5000)
59 | john.set_department("Software")
60 |
61 |
62 | print(john.get_department())
63 | print(john.get_salary())
64 | print(john.get_project())
65 |
66 | john.last_name = "Lennon"
67 |
68 | print(john.first_name, john.last_name)
69 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz_solution/q0704.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create 3 different classes Length, Weight, and Time
5 |
6 | Add respective attributes to store values.
7 | Add static methods for conversion of different units.
8 |
9 | Example:
10 | Length: cm to inches, kilometer to miles, etc
11 | Weight: KG to pounds, gram to Ounces, etc.
12 | Time: seconds to hours, milliseconds to seconds
13 |
14 | """
15 |
16 | # answer
17 |
18 | class Length:
19 |
20 | @staticmethod
21 | def meter_to_feet(meter: float):
22 | return meter * 3.28084
23 |
24 | @staticmethod
25 | def feet_to_meter(feet: float):
26 | return feet / 3.28084
27 |
28 | class Weight:
29 |
30 | @staticmethod
31 | def kg_to_pound(kg: float):
32 | return kg * 2.20462
33 |
34 | @staticmethod
35 | def pound_to_kg(pound: float):
36 | return pound / 2.20462
37 |
38 | class Time:
39 | @staticmethod
40 | def second_to_hour(second: float):
41 | return second/3600
42 |
43 | @staticmethod
44 | def hour_to_second(hour: float):
45 | return hour *3600
46 |
47 |
48 | # tests
49 | print(Length.feet_to_meter(5.9))
50 | print(Weight.kg_to_pound(5.9))
51 | print(Time.second_to_hour(7200))
52 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz_solution/q0705.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | create a classes Rectangle, Circle and Box and add respective attributes eg.: radius, length, width, height
5 |
6 | for Rectangle, create methods to find out:
7 | perimeter
8 | area
9 | length of diagonal
10 |
11 | for Circle, create methods to find out:
12 | circumference
13 | area
14 | diameter
15 |
16 | for Box, create methods to find out:
17 | surface area
18 | volume
19 | """
20 |
21 | # answer
22 |
23 | class Rectangle:
24 | width = 0
25 | height = 0
26 |
27 | def __init__(self, width, height) -> None:
28 | self.width = width
29 | self.height = height
30 |
31 | def perimeter(self):
32 | return 2 * (self.width + self.height)
33 |
34 | def area(self):
35 | return self.width * self.height
36 |
37 | def diagonal_length(self):
38 | return (self.width**2 * self.height**2)**0.5
39 |
40 |
41 | class Circle:
42 | __PI = 3.1415
43 |
44 | def __init__(self, radius) -> None:
45 | self.radius = radius
46 |
47 | def area(self):
48 | return self.__PI * self.radius**2
49 |
50 | def diameter(self):
51 | return 2 * self.radius
52 |
53 | def circumference(self):
54 | return self.diameter() * self.__PI
55 | # return 2 * self.radius * self.__PI # This solution also works
56 |
57 |
58 | class Box:
59 | def __init__(self, length, width, height) -> None:
60 | self.length = length
61 | self.width = width
62 | self.height = height
63 |
64 | def surface_area(self):
65 | return 2 * (self.length * self.width + self.width * self.height + self.length * self.height)
66 |
67 | def volume(self):
68 | return self.length * self.width + self.height
69 |
70 |
71 | # tests
72 |
73 | rect1 = Rectangle(5, 6)
74 | print('Area of rectangle:', rect1.area())
75 | print('Perimeter of rectangle:', rect1.perimeter())
76 | print('Length of diagonal:', rect1.diagonal_length())
77 | print()
78 |
79 | circle = Circle(10)
80 | print('Area of Circle:', circle.area())
81 | print('Perimeter of Circle:', circle.circumference())
82 | print('Diameter of Circle:', circle.diameter())
83 | print()
84 |
85 | box1 = Box(5, 6, 7)
86 | print('Surface Area of Box:', box1.surface_area())
87 | print('Volume of the box:', box1.volume())
88 | print()
89 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz_solution/q0706.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create 2 different classes Major and Subject
5 |
6 | create an instance of Major and add different subjects to the 'subject' attribute as a set of subjects
7 |
8 | Create another instance for Major and add subjects to the instance
9 |
10 | add operator overloading to add different methods so that we can create another major.
11 |
12 | Example we have majors science, commerce
13 |
14 | if we call
15 |
16 | new_major = science + commerce
17 |
18 | it should be able to return the new instance of Major with all subjects inside of them
19 |
20 | Note:
21 | you can try other operator overloading options too.
22 | """
23 |
24 | # answer
25 |
26 |
27 | class Subject:
28 |
29 | def __init__(self, name) -> None:
30 | self.name = name
31 |
32 | def __str__(self) -> str:
33 | return f"subject: {self.name}"
34 |
35 | def __repr__(self) -> str:
36 | return self.__str__()
37 |
38 |
39 | class Major:
40 | name = ''
41 |
42 | def __init__(self, name) -> None:
43 | self.name = name
44 | self.subjects = []
45 |
46 | def __add__(self, other: "Major"):
47 | sub = Major(f'{self.name} + {other.name}')
48 | sub.subjects = [*self.subjects, *other.subjects]
49 | return sub
50 |
51 |
52 | # Tests
53 |
54 | sc = Major("Science")
55 | sc.subjects.append(Subject('Physics'))
56 | sc.subjects.append(Subject('Chemistry'))
57 |
58 | com = Major("Commerce")
59 | com.subjects.append(Subject('Business Mathematics'))
60 | com.subjects.append(Subject('Accounting'))
61 |
62 | new_major = sc + com
63 |
64 | print(new_major.name)
65 | print(new_major.subjects)
66 |
--------------------------------------------------------------------------------
/notes/c07_oop/quiz_solution/q0708.py:
--------------------------------------------------------------------------------
1 | """
2 | © https://sudipghimire.com.np
3 |
4 | Create a class Vehicle add some attributes and methods to it
5 | - name
6 | - brand
7 | - wheels_count
8 | - engine_type
9 | - braking_system
10 |
11 |
12 | Create a child class HeavyVehicle and inherit all the attributes from the parent class Vehicle
13 | - change the wheels_count from 4 to 6 in the initializer or accept the value while instantiating
14 | - add more instance attributes like max_load, mileage, etc.
15 |
16 | Create a child class Bike and inherit all the attributes from the parent class Vehicle
17 | - change the wheels_count from 4 to 2 in the initializer
18 | - add setter or getter methods or property to add bike number, and owner name
19 | - try adding property instead of setter or getter for passenger/ pillion attribute
20 |
21 | create different instances of Vehicle, HeavyVehicle, and Bike and check whether each other are subclasses
22 | and instances of different classes or not.
23 |
24 | """
25 |
26 | # answer
27 | class Vehicle:
28 | wheels_count = 4
29 | engine_type = 'Diesel Engine'
30 | braking_system = 'ABS'
31 | def __init__(self, name, brand):
32 | pass
33 |
34 |
35 | class HeavyVehicle(Vehicle):
36 | max_load = 4000
37 | milage = 40
38 | def __init__(self, name, brand):
39 | self.wheels_count = 6
40 | super().__init__(name, brand)
41 |
42 | class Bike(Vehicle):
43 | def __init__(self, name, brand):
44 | self.wheels_count = 2
45 | super().__init__(name, brand)
46 | self.__reg_no = ''
47 | self.__owner = ''
48 |
49 | @property
50 | def reg_no(self):
51 | return self.__reg_no
52 |
53 | @reg_no.setter
54 | def reg_no(self, value):
55 | self.__reg_no = value
56 |
57 | @property
58 | def owner(self):
59 | return self.__owner
60 |
61 | @owner.setter
62 | def owner(self, value: str):
63 | self.__owner = value
64 |
65 |
66 | b1 = Bike("Apache RTR 200", "TVS")
67 | b1.reg_no = 11223344
68 | b1.owner = "John Doe"
69 |
70 | print(b1.reg_no)
71 | print(b1.owner)
72 |
73 |
74 | print(issubclass(Bike, Vehicle))
75 |
76 | print(isinstance(b1, Vehicle))
77 | print(isinstance(b1, HeavyVehicle))
78 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/README.md:
--------------------------------------------------------------------------------
1 |
2 | # Chapter 8 Modules and Packages
3 |
4 | > **Note**:
5 | >
6 | > please refer to the repository
7 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
8 | > exercises and projects related to this chapter.
9 | >
10 | >
11 | **Table of contents**:
12 |
13 | - [8.1. Python Modules](chapter-8.1-modules.md)
14 | - [8.2. Packages](chapter-8.2-packages.md)
15 | - [8.3. `datetime` Module](chapter-8.3-datetime.md)
16 | - [8.4. `random` Module](chapter-8.4-random.md)
17 | - [8.5. `json` Module](chapter-8.5-json.md)
18 | - [8.6. `math` Module](chapter-8.6-math.md)
19 | - [8.7. `complex` and `cmath` Module](chapter-8.7-complex-and-cmath.md)
20 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/chapter-8.1-modules.md:
--------------------------------------------------------------------------------
1 | # Chapter 8.1 Python Modules
2 |
3 | - Module is a collection of different classes, functions and variables
4 | - A module is nothing but a single file which can be imported into another file.
5 |
6 | ## basic structure of a module
7 |
8 | ```shell
9 | 📁 working_dir/
10 | |-- 📄 module.py
11 | | 📦 variables
12 | | 📦 functions
13 | | 📦 classes
14 | |
15 | |-- 📄 main.py
16 | 📦 imports from module.py
17 | 📦 extra logic/code
18 | ```
19 |
20 | File 1: `school.py`
21 |
22 | ```python
23 | class Student:
24 | def __init__(self, name, roll):
25 | self.name = name
26 | self.roll = roll
27 | ```
28 |
29 | File 2: `main.py`
30 |
31 | ```python
32 | from school import Student
33 | john = Student('John Doe', 1)
34 | jane = Student('Jane Doe', 1)
35 | ```
36 |
37 | ## Importing the whole module
38 |
39 | if `animal.py` is the filename for the module, the name of the module would be `animal`. so we can import the whole module by the following command.
40 |
41 | ```python
42 | import animal
43 | ```
44 |
45 | Importing the whole module imports everything that is defined, imported, and initialized inside the module.
46 |
47 | ## Importing individual elements from the module
48 |
49 | we can use `from` keyword to import the individual component from the module. For example the module `animal` contains the class `DomesticAnimal`, we can import it using the following command.
50 |
51 | ```python
52 | from animal import DomesticAnimal
53 | # we can also import multiple elements in the single line using comma
54 | from animal import DomesticAnimal, WildAnimal
55 |
56 | # we can also use small brackets so that we can import in different lines
57 |
58 | from animal import (
59 | Animal,
60 | DomesticAnimal,
61 | WildAnimal,
62 | )
63 | ```
64 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/chapter-8.2-packages.md:
--------------------------------------------------------------------------------
1 | # Chapter 8.2 Python Packages
2 |
3 | - A python package is a directory that contains multiple python files or modules
4 | - A package contains a special file `__init__.py` as the initializer file.
5 | - it might contain subdirectories or subpackages too.
6 |
7 | ## Basic Package directory structure
8 |
9 | ```
10 | 📁 my_package/
11 | |-- 📄 module_1.py
12 | |-- 📄 module_2.py
13 | |-- 📄 module_3.py
14 | |-- 📄 __init__.py
15 | ```
16 |
17 | ## Basic Package structure with subpackage inside it
18 |
19 | ```
20 | 📁 my_package/
21 | |-- 📁 pkg_1/
22 | | |-- 📄 __init__.py
23 | | |-- 📄 pkg1_module_1.py
24 | | |-- 📄 pkg1_module_2.py
25 | |
26 | |-- 📄 __init__.py
27 | |-- 📄 module_1.py
28 | |-- 📄 module_2.py
29 | |-- 📄 module_3.py
30 | ```
31 |
32 | ## The `__init__.py` file
33 |
34 | The `__init__.py` file is the initializer for the package that is capable of importing all the components inside of the modules of the package.
35 |
36 | It is capable of creating shortcuts so that user do not need to go deeper to import required components. To create a shortcut, we can simply import everything from the modules from the package.
37 |
38 | Eg: ** `__init__.py` of package: `my_package`
39 |
40 | ```python
41 | from .module_1 import Class1, Class2
42 | from .pkg_1.pkg1_module_1 import *
43 | ```
44 |
45 | after importing all elements with the above command, we can replace the following command
46 |
47 | ```python
48 | from my_package.module_1 import Class1
49 | from my_package.pkg_1.module_1 import ABC
50 |
51 | ```
52 |
53 | with the following command:
54 |
55 | ```python
56 | from my_package import Class1, ABC
57 | ```
58 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/chapter-8.4-random.md:
--------------------------------------------------------------------------------
1 | # Chapter 8.4. The `random` Module
2 |
3 | **Table Of Contents**:
4 |
5 | - [Chapter 8.4. The `random` Module](#chapter-84-the-random-module)
6 | - [Usage](#usage)
7 | - [Printing out a random number from a range 1 to 6](#printing-out-a-random-number-from-a-range-1-to-6)
8 | - [Printing out 10 random numbers from a range 1 to 6](#printing-out-10-random-numbers-from-a-range-1-to-6)
9 | - [Printing out a random character from a string](#printing-out-a-random-character-from-a-string)
10 | - [Printing out 5 random characters as a list from a string](#printing-out-5-random-characters-as-a-list-from-a-string)
11 | - [Printing out a random password string with 8 characters](#printing-out-a-random-password-string-with-8-characters)
12 |
13 | Reference:
14 |
15 | - `random` module implements pseudo-random generators for various distributions.
16 |
17 | **Some of the functions are as follows:**
18 |
19 | - `random.seed()`
20 |
21 | It initializes the random number generator.
22 |
23 | - `random.randrange()`
24 |
25 | It accepts 3 parameters `start`, `stop`, and `step` which is similar to `range()` function. It returns the random from the eligible numbers from the range function.
26 |
27 | - `random.randint()`
28 |
29 | It is similar to `randrange()` method but the `stop` number is inclusive
30 |
31 | - `random.choice()`
32 |
33 | It chooses a character from the string provided.
34 |
35 | - `random.choices()`
36 |
37 | It chooses `k` number of characters as a list from the string provided.
38 |
39 | ## Usage
40 |
41 | ### Printing out a random number from a range 1 to 6
42 |
43 | ```python
44 | import random
45 |
46 | print(random.randint(1,6))
47 | ```
48 |
49 | ### Printing out 10 random numbers from a range 1 to 6
50 |
51 | ```python
52 | import random
53 |
54 | for _ in range(10):
55 | print(random.randint(1,6))
56 | ```
57 |
58 | ### Printing out a random character from a string
59 |
60 | ```python
61 | import random
62 |
63 | options = "12345ABCDEabcde"
64 | print(random.choice(options))
65 | ```
66 |
67 | ### Printing out 5 random characters as a list from a string
68 |
69 | ```python
70 | import random
71 |
72 | options = "12345ABCDEabcde"
73 | print(random.choices(options, k=5))
74 | ```
75 |
76 | ### Printing out a random password string with 8 characters
77 |
78 | ```python
79 | import random
80 |
81 | options = "12345ABCDEabcde"
82 | print(''.join(random.choices(options, k=8)))
83 | ```
84 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/code/animal.py:
--------------------------------------------------------------------------------
1 | """
2 | Module Animal
3 |
4 | This is a sample module animal which can be imported into different files.
5 | The module contains different constants, variables, functions and classes.
6 | """
7 | DOMESTIC = 'domestic'
8 | WILD = 'wild'
9 | age = 20
10 |
11 |
12 | def abc():
13 | print("Did Something")
14 |
15 |
16 | class Animal:
17 | name = ""
18 | paws = 0
19 | word = ""
20 |
21 | def __init__(self, name, word):
22 | self.name = name
23 | self.word = word
24 |
25 | def __str__(self):
26 | return (f"Instance of Animal named {self.name}")
27 |
28 | def speak(self):
29 | print(f'I am {self.name} and I speak with {self.word}s.')
30 |
31 | def rename(self, new_name):
32 | self.name = new_name
33 |
34 |
35 | class DomesticAnimal(Animal):
36 | purpose_of_domestication = ''
37 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/code/c0805_json_module.py:
--------------------------------------------------------------------------------
1 | """
2 | JSON
3 |
4 | - JavaScript Object Notation
5 | - Used for data exchange throughout machines and platforms.
6 | - It is used as data interchange format for REST APIs
7 |
8 | - JSON contains Key Value Pairs.
9 |
10 | - Data Types in json
11 | - number (9, 9.9)
12 | - string ("str1")
13 | - boolean (true, false)
14 | - null (Python Equivalent is None)
15 |
16 | - Array [1,2] (Python Equivalents are List, tuple, set)
17 | - Object (Python Equivalent is Dictionary)
18 | {"key": "value"}
19 |
20 | {
21 | "key": {
22 | "key": {
23 | "key1": value1
24 | "key2": value2
25 | "key3": [1,2]
26 | "key3": [
27 | {"key": "Value"},
28 | {"key": "Value"},
29 | {"key": "Value"}
30 | ]
31 | }
32 | }
33 | }
34 |
35 | """
36 |
37 |
38 | # JSON Methods in python
39 |
40 | """
41 | dump - it converts python dictionary to json file
42 | load - it retrieves all values from a json file and loads into a dictionary
43 |
44 | dumps - it is similar to dump() method, but, it just returns string instead of storing in a file
45 | loads - it is similar to load() method but, it converts json formatted string into a dictionary
46 | """
47 |
48 |
49 | # To use json we have to import the json module
50 | import json
51 | from json.decoder import JSONDecodeError
52 |
53 | # Dumping a dictionary to a file
54 | person = {
55 | "name": "John Doe",
56 | "age": 20,
57 | "married": False,
58 | "occupation": None,
59 | "father":{
60 | "name": "John Doe Sr.",
61 | "age": 50,
62 | "childrens":("Jon", "Jane",),
63 | },
64 | 5: 10.5,
65 | }
66 |
67 | with open('person.json', 'w') as f:
68 | json.dump(person, f)
69 |
70 |
71 |
72 | # loading a json object from a file
73 |
74 | with open('response.json', 'r') as f:
75 | try:
76 | response = json.load(f)
77 |
78 | print(type(response))
79 | except JSONDecodeError as e:
80 | print("The JSON file you provided is not in a correct format")
81 |
82 |
83 | # dumping a dictionary into json string
84 |
85 |
86 | value = json.dumps(person)
87 |
88 | print(value)
89 |
90 |
91 | json_string = '{"name": "Jon Doe"}'
92 |
93 |
94 | dict2 = json.loads(json_string)
95 |
96 | print(dict2["name"])
97 |
98 |
99 | # dumping a file with indented key value pairs
100 |
101 | with open('person.json', 'w') as f:
102 | json.dump(person, f, indent=4)
103 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/code/c08_01_module_intro.py:
--------------------------------------------------------------------------------
1 | """
2 | importing the whole module
3 |
4 | """
5 | # import animal
6 |
7 | # importing the individual element
8 |
9 | from animal import DomesticAnimal
10 |
11 | # we can also import multiple elements in the single line using commaa
12 | from animal import Animal, DomesticAnimal
13 |
14 | # we can also use small brackets so that we can import in different lines
15 |
16 | from animal import (
17 | Animal,
18 | DomesticAnimal,
19 | WILD,
20 | DOMESTIC,
21 | abc,
22 | )
23 |
24 | abc()
25 | elephant = Animal("John", "trumpet")
26 |
27 | # elephant.name = "sdsds"
28 | # elephant.paws = 34
29 | # elephant.word = "sds"
30 | elephant.speak()
31 | elephant.rename("Jane")
32 | elephant.speak()
33 |
34 | cow = DomesticAnimal("My Cow", "moo")
35 |
36 | print(DOMESTIC)
37 | print(WILD)
38 |
--------------------------------------------------------------------------------
/notes/c08_modules_packages/quiz/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 8 Modules and Packages Quiz
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 | >
9 |
--------------------------------------------------------------------------------
/notes/c09_file/code/.gitignore:
--------------------------------------------------------------------------------
1 | **/*.txt
2 |
--------------------------------------------------------------------------------
/notes/c09_file/code/reading_file.py:
--------------------------------------------------------------------------------
1 | # FIXME: please use this path if you run this script from current directory
2 | # file_name = "file_1.txt"
3 |
4 | # ! Please create a new file named `file1.txt` in this directory to open the file
5 |
6 | # Please use this path if you run this script fromthe project directory
7 | file_name = "c09_file/code/file1.txt"
8 |
9 |
10 | # ------------------------------------------------------------------------------
11 | # opening the file in reading mode
12 | file = open(file_name, "r")
13 |
14 | # reading the whole content
15 | content = file.read()
16 | print(content)
17 |
18 | # ? output:
19 | # This is Line 1
20 | # This is Line 2
21 | # This is Line 3
22 |
23 |
24 | # we need to close the file once we don't need anymore
25 | file.close()
26 |
27 | # ------------------------------------------------------------------------------
28 | # reading line by line
29 |
30 | file = open(file_name, "r")
31 | line = 0
32 | while content := (file.readline()).strip():
33 | print(f"{line:>3} | {content}")
34 | line += 1
35 | print(content)
36 |
37 | # ? output:
38 | # 0 | This is Line 1
39 | # 1 | This is Line 2
40 | # 2 | This is Line 3
41 |
42 | file.close()
43 |
--------------------------------------------------------------------------------
/notes/c09_file/code/reading_file_with.py:
--------------------------------------------------------------------------------
1 | # FIXME: please use this path if you run this script from current directory
2 | # file_name = "file_1.txt"
3 |
4 | # ! Please create a new file named `file1.txt` in this directory to open the file
5 |
6 | # Please use this path if you run this script fromthe project directory
7 | file_name = "c09_file/code/file1.txt"
8 |
9 | # ------------------------------------------------------------------------------
10 | # reading line by line
11 |
12 | with open(file_name, "r") as file:
13 | line = 0
14 | while content := (file.readline()).strip():
15 | print(f"{line:>3} | {content}")
16 | line += 1
17 | print(content)
18 |
19 | # ? output:
20 | # 0 | This is Line 1
21 | # 1 | This is Line 2
22 | # 2 | This is Line 3
23 |
24 | # closing the file is not necessary when using `with` statement
25 | # file.close()
26 |
--------------------------------------------------------------------------------
/notes/c09_file/code/updating_file.py:
--------------------------------------------------------------------------------
1 | # FIXME: please use this path if you run this script from current directory
2 | # file_name = "file_1.txt"
3 |
4 | """
5 | Please create a new file named `file1.txt` in this directory with content
6 | as follows:
7 |
8 | c09_file/code/file1.txt
9 |
10 | This is Line 1
11 | This is Line 2
12 | This is Line 3
13 |
14 | """
15 |
16 | # Please use this path if you run this script from the project directory
17 | file_name = "c09_file/code/file1.txt"
18 |
19 | # ------------------------------------------------------------------------------
20 | # opening the file in append mode
21 | # using with statement
22 | with open(file_name, "a") as file:
23 | file.write("This is Line 4")
24 |
25 | """
26 | After executing this script, you should be able to see the appended content in
27 | the file which will look like below:
28 |
29 | This is Line 1
30 | This is Line 2
31 | This is Line 3
32 | This is Line 4
33 |
34 | """
35 |
--------------------------------------------------------------------------------
/notes/c09_file/code/writing_file.py:
--------------------------------------------------------------------------------
1 | # FIXME: please use this path if you run this script from current directory
2 | # file_name = "file_1.txt"
3 |
4 | # ! Please create a new file named `file1.txt` in this directory to open the file
5 |
6 | # Please use this path if you run this script from the project directory
7 | file_name = "c09_file/code/file2.txt"
8 | binary_file_name = "c09_file/code/binary_file.txt"
9 |
10 | content = """This is a content to the written file.
11 | - This is a point 1
12 | - This is a point 2
13 | """
14 |
15 | binary_content = (
16 | """{
17 | "name": "Tony Stark 🙂",
18 | "affiliation": "Avengers",
19 | "age": 40,
20 | }
21 | """
22 | ).encode("utf-8")
23 |
24 | # ------------------------------------------------------------------------------
25 | # opening the file in writing mode
26 | file = open(file_name, "w")
27 |
28 | file.write(content)
29 |
30 | # we need to close the file once we don't need anymore
31 | file.close()
32 |
33 | # ------------------------------------------------------------------------------
34 | # using with statement
35 | with open(file_name, "w") as file:
36 | file.write(content)
37 |
38 | # ------------------------------------------------------------------------------
39 | # writing a binary file
40 | with open(binary_file_name, "wb") as file:
41 | file.write(binary_content)
42 |
--------------------------------------------------------------------------------
/notes/c09_file/quiz/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 9 File Quiz
2 | https://sudipghimire.com.np
3 |
4 | Please read the note carefully and try to solve the problem below:
5 |
6 | > **Note**:
7 | >
8 | > please refer to the repository
9 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
10 | > exercises and projects related to this chapter.
11 | >
12 |
--------------------------------------------------------------------------------
/notes/c10_exceptions/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 10: Exceptions and Exception Handling
2 |
3 | **Table of Contents**:
4 |
5 | - [10.1. Exceptions](chapter-10.1-exceptions.md)
6 | - [10.2. Exception Handling in Python](chapter-10.2-exception-handling.md)
7 | - [10.3. Creating Custom Exceptions](chapter-10.3-custom-exceptions.md)
8 |
--------------------------------------------------------------------------------
/notes/c10_exceptions/chapter-10.3-custom-exceptions.md:
--------------------------------------------------------------------------------
1 | # Creating custom Exceptions
2 |
3 | We can create a new `Exception` by inheriting the `BaseException` class. The
4 | following is an example of a CustomException:
5 |
6 | ```py
7 | class MyCustomException(Exception):
8 | pass
9 | ```
10 |
11 | ## Example1: AgeError
12 |
13 | We can create a custom age error that can raise the exception if a user enters
14 | the invalid range of an age of a person.
15 |
16 | ```py
17 | class AgeError(Exception):
18 | min_age = 0
19 | max_age = 100
20 |
21 | def __init__(self, age, *args):
22 | super().__init__(*args)
23 | self.age = age
24 |
25 | def __str__(self):
26 | return f'The age {self.age} is not in between {self.min_age} and {self.max_age}'
27 | ```
28 |
29 | ## Example2: `LengthError`
30 |
31 | The example below explains how a custom exception can be raised using the
32 | object-oriented oriented approach:
33 |
34 | ```py
35 | class LengthError(Exception):
36 | def __init__(self, value: int, *args: object) -> None:
37 | self.value = value
38 | super().__init__(*args)
39 |
40 | def __str__(self):
41 | return f'The length {self.value} is not possible'
42 |
43 |
44 | class Length:
45 | def __init__(self, value):
46 | if value < 0:
47 | raise LengthError(value)
48 | self.value = value
49 |
50 |
51 | l1 = Length(5)
52 | l2 = Length(-5) # LengthError: The length -5 is not possible
53 | ```
54 |
--------------------------------------------------------------------------------
/notes/c10_exceptions/code/c1001_custom_exception.py:
--------------------------------------------------------------------------------
1 | class AgeError(Exception):
2 | min_age = 0
3 | max_age = 100
4 |
5 | def __init__(self, age, *args):
6 | super().__init__(*args)
7 | self.age = age
8 |
9 | def __str__(self):
10 | return f'The age {self.age} is not in between {self.min_age} and {self.max_age}'
11 |
12 | x = 101
13 | if not x <=0 <=100:
14 | raise AgeError(x)
15 |
16 | # AgeError: The age 101 is not in between 0 and 100
17 | print(x)
--------------------------------------------------------------------------------
/notes/c10_exceptions/code/c1002_lentth_exception.py:
--------------------------------------------------------------------------------
1 | class LengthError(Exception):
2 | def __init__(self, value: int, *args: object) -> None:
3 | self.value = value
4 | super().__init__(*args)
5 |
6 | def __str__(self):
7 | return(f'The length {self.value} is not possible')
8 |
9 |
10 | class Length:
11 | def __init__(self, value):
12 | if value < 0:
13 | raise LengthError(value)
14 | self.value = value
15 |
16 |
17 | l1 = Length(5)
18 | l2 = Length(-5) # LengthError: The length -5 is not possible
19 |
--------------------------------------------------------------------------------
/notes/c10_exceptions/quiz/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 10 Exceptions Quiz
2 |
3 | Please read the note carefully and try to solve the problem below:
4 |
5 | > **Note**:
6 | >
7 | > please refer to the repository
8 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
9 | > exercises and projects related to this and following chapters.
10 | >
11 |
--------------------------------------------------------------------------------
/notes/c11_pip/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 11 Python Package Management
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 |
9 | **Table of contents**:
10 |
11 | - [Introduction to Semantic Versioning(semver)](chapter-11.1-semver.md)
12 | - [PIP package manager](chapter-11.2-pip.md)
13 |
--------------------------------------------------------------------------------
/notes/c11_pip/chapter-11.1-semver.md:
--------------------------------------------------------------------------------
1 | # 11.1. Semantic Versioning system
2 |
3 | Ref: https://semver.org/
4 |
5 | - Semantic Versioning system uses major, minor, and patch release labels to track the current release of the software.
6 |
7 | Eg: Django version `3.2.10`
8 |
9 | - The `major` release is `3`
10 | - The `minor` release is `2`
11 | - The `patch` release is `10`
12 |
13 | Sometimes, additional labels for pre-releases are also tracked using `dev`, `alpha`, `beta`, `release candidates`
14 | or `rc`, and `build_id`.
15 |
16 | Some examples of pre-release versions are as follows:
17 |
18 | - `3.2.10-a1`
19 | - `3.2.10-alpha.1`
20 | - `3.2.10-beta`
21 | - `3.2.10-rc.1`
22 | - `3.2.10-rc.1+41A39F2`, etc.
23 |
24 | ## Major Release
25 |
26 | - This release introduce new features
27 | - It is going to break your existing code with previous versions
28 | - With every major release, the first release number is going to be increased by 1 and other release numbers would be 0
29 | - `1.2.10` -> `2.0.0`
30 |
31 | ## Minor Release
32 |
33 | - They introduce some new features, but not with heavy changes.
34 | - The code might break in this case also, but can be fixed with small refactorings. (Backward Compatible)
35 | - In this release, features just gets depreciated, but do not get removed.
36 | - With this release second release number is going to be increased by 1 and the last would be 0
37 | - `11.2.10` -> `11.3.0`
38 |
39 | ## Patch Release / Bugfix Release
40 |
41 | - This release is not going to introduce any new features
42 | - The older code is not going to break in this release
43 | - Every changes is going to be internal changes or logical changes.
44 | - With this release the last release number is going to be increased by 1
45 | - `11.2.10` -> `11.2.11`
46 |
47 | The tree below shows an example of the release cycle of a software.
48 |
49 | ```
50 | v0.1.0
51 | v0.2.0
52 | ...
53 | v0.9.0
54 | v0.10.0
55 | ...
56 | v1.0.0-a1
57 | v1.0.0-a2
58 | ...
59 | v1.0.0-b1
60 | v1.0.0-b2
61 | ...
62 | v1.0.0-rc.1
63 | v1.0.0-rc.2
64 | ...
65 | v1.0.0 (Major release)
66 | v1.0.1 (patch / bugfix)
67 | v1.0.2
68 | ...
69 |
70 | v1.1.0 (Minor Release)
71 | v1.1.1 (patch / bugfix)
72 | v1.1.2
73 | ...
74 |
75 | v1.2.0 (Minor Release)
76 | ...
77 |
78 | v2.0.0 (Major Release)
79 | ```
80 |
--------------------------------------------------------------------------------
/notes/c12_virtual_environment/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 12 Virtual environments
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 |
9 | **Table of Contents**
10 |
11 | - [Introduction to Virtual environments](chapter-12.1-virtual-environment-intro.md)
12 | - [VENV and its usage](chapter-12.2-venv.md)
13 | - [PIPENV and its usage](chapter-12.3-pipenv.md)
14 | - [Poetry package manager](chapter-12.4-poetry.md)
15 |
--------------------------------------------------------------------------------
/notes/c12_virtual_environment/chapter-12.1-virtual-environment-intro.md:
--------------------------------------------------------------------------------
1 | # 12.1. Introduction to Virtual environments
2 |
3 | Let us consider that we have to work simultaneously on 2 different projects with different package dependencies as follows
4 |
5 | ### **Base Python**
6 | - `django==4.0.0`
7 |
8 | ### **Project 1**
9 | - `django==2.2`
10 | - `pillow==7.0`
11 | - `numpy==1.0.0`
12 |
13 |
14 | ### **Project 2**
15 | - `django==3.2.10`
16 | - `pillow==9.1.0`
17 | - `numpy==1.5.0`
18 |
19 | Suppose we have base python `3.9` installed and different requirements for different projects. Switching back and forth between the projects requires installing and uninstalling different packages which is impractical in daily use.
20 |
21 |
22 | To avoid frequent installation and uninstalling of packages, we can use virtual environments.
23 |
24 |
25 | A Virtual Environment is a tool that keeps dependencies required by different projects in separate places.
26 |
27 |
28 | Followings are common virtual environments:
29 |
30 | - `venv`, `virtualenv`
31 | - `pipenv`
32 | - `direnv`
33 | - `pyenv`
34 | - `conda`
35 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 13: Advanced Functions
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 |
9 | **Table of Contents**
10 |
11 | - [`groupby()` function](chapter-13.1-groupby.md)
12 | - [`sorted()` function](chapter-13.2-sorted.md)
13 | - [`filter()` function](chapter-13.3-filter.md)
14 | - [`map()` function](chapter-13.4-map.md)
15 | - [`reduce()` function](chapter-13.5-reduce.md)
16 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/chapter-13.1-groupby.md:
--------------------------------------------------------------------------------
1 | # 13.1. The `groupby()` Function
2 |
3 |
4 | The `groupby()` function is a function that returns consecutive keys and groups from the iterable.
5 |
6 | The `groupby()` generates an iterator that has key and value pair in which key is the value to be grouped by and value is the iterator again which contains grouped value.
7 | Source: https://docs.python.org/3/library/itertools.html#itertools.groupby
8 |
9 | **Example 1**
10 | ```python
11 | from itertools import groupby
12 |
13 | # example 1
14 | animals = ['Bear', 'Donkey', 'Cat', 'Dog', 'Camel', 'Elephant']
15 | animals = sorted(animals)
16 | values = groupby(animals, lambda x: x[0])
17 | grouped = {k: list(v) for k, v in values}
18 | print(grouped)
19 | ```
20 |
21 | Output
22 | ```
23 | {'B': ['Bear'], 'C': ['Camel', 'Cat'], 'D': ['Dog', 'Donkey'], 'E': ['Elephant']}
24 | ```
25 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/chapter-13.2-sorted.md:
--------------------------------------------------------------------------------
1 | **Table of Contents**
2 | - [13.2. The `sorted()` Function](#132-the-sorted-function)
3 | - [Sorting the list in an ascending order](#sorting-the-list-in-an-ascending-order)
4 | - [Sorting the list in a descending order](#sorting-the-list-in-a-descending-order)
5 | - [Complex Sorting with the `sorted()` function](#complex-sorting-with-the-sorted-function)
6 | - [a `key` parameter in `sorted()` function](#a-key-parameter-in-sorted-function)
7 | # 13.2. The `sorted()` Function
8 |
9 | https://docs.python.org/3.9/howto/sorting.html
10 |
11 | Although there's a `list.sort()` method that modifies the list in place, we need to return the sorted list without modifying the original list. for that purpose, we can use `sorted()` function that builds a new sorted list from an iterable
12 |
13 |
14 | ## Sorting the list in an ascending order
15 | Sorting the list in an ascending order is done by just passing the list as an argument in the `sorted()` function.
16 |
17 | ```py
18 | print(sorted([1, 4, 7, 2, 9, 5, 3]))
19 | # [1, 2, 3, 4, 5, 7, 9]
20 | ```
21 |
22 | **Note**: _We can sort the list using `list.sort()` method which is more efficient than `sorted()` method, but remember, `list.sort()` method modifies the list in place so we cannot recover the original list._
23 |
24 | ## Sorting the list in a descending order
25 | Sorting in a descending order is as easy as sorting in an ascending order; we just pass the second argument `reverse=True`.
26 |
27 | ```py
28 | print(sorted([1, 4, 7, 2, 9, 5, 3], reverse=True))
29 | # [9, 7, 5, 4, 3, 2, 1]
30 | ```
31 |
32 |
33 | ## Complex Sorting with the `sorted()` function
34 | Unlike `list.sort()` which is available in the list only, we can sort any iterable with the sorted() method. For example we can sort the dictionary, a multidimensional iterable, etc.
35 |
36 | The following is an example of sorting the dictionary
37 |
38 | ```py
39 | person = {
40 | 'name': 'John Doe',
41 | 'age': 20,
42 | 'occupation': 'student'
43 | }
44 | print(sorted(person))
45 |
46 | # ['age', 'name', 'occupation']
47 | ```
48 | **Note**: _When we sort the dictionary, it returns the list of keys of the dictionary._
49 |
50 | If we want to sort the dictionary and get the final result as the dictionary, then we need to sort using `dict.items()` and then regenerate the dictionary again.
51 |
52 | ```py
53 | person = {
54 | 'name': 'John Doe',
55 | 'age': 20,
56 | 'occupation': 'student'
57 | }
58 |
59 | print(sorted(person.items()))
60 | # output (dict_items)
61 | # [('age', 20), ('name', 'John Doe'), ('occupation', 'student')]
62 |
63 |
64 | print({k:v for k,v in sorted(person.items())})
65 | # output (dict)
66 | # {'age': 20, 'name': 'John Doe', 'occupation': 'student'}
67 | ```
68 |
69 | ### a `key` parameter in `sorted()` function
70 |
71 | Additionally, we can pass the `key` parameter to the `sorted()` function to do advanced sorting. for example:
72 |
73 | ```py
74 | students = [
75 | ('John', 2),
76 | ('Eve', 4),
77 | ('Jennifer', 3),
78 | ('Adam', 1)
79 | ]
80 | print(sorted(students, key=lambda x: x[1]))
81 |
82 | # Output
83 | # [('Adam', 1), ('John', 2), ('Jennifer', 3), ('Eve', 4)]
84 | ```
85 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/chapter-13.3-filter.md:
--------------------------------------------------------------------------------
1 | **Table of Contents**
2 |
3 |
4 | # 13.3. The `filter()` function
5 |
6 | https://docs.python.org/3/library/functions.html#filter
7 |
8 | A `filter()` function is a generator function that filters an iterable by specified condition. A filter function takes
9 |
10 | ```py
11 | def is_even(num):
12 | return num%2==0
13 |
14 | list_1 = [1,2,3,4,5,6,7,8,9,10]
15 |
16 | list_2 = filter(is_even, list_1)
17 |
18 | print(list(list_2))
19 | ```
20 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/chapter-13.4-map.md:
--------------------------------------------------------------------------------
1 | # Chapter 13.4: The `map()` Function
2 |
3 | **Table of Contents**
4 |
5 | - [Chapter 13.4: The `map()` Function](#chapter-134-the-map-function)
6 | - [Introduction to `map()` function](#introduction-to-map-function)
7 | - [Using `map()` function with other functions to transform data](#using-map-function-with-other-functions-to-transform-data)
8 | - [Using `map()` function with lambda to transform data](#using-map-function-with-lambda-to-transform-data)
9 | - [Using `map()` function to manipulate 2 lists](#using-map-function-to-manipulate-2-lists)
10 |
11 | ## Introduction to `map()` function
12 |
13 | Map function is a generator function that applies another function to elements
14 | of the iterable that is passed as an argument. The basic structure of a `map()`
15 | function is as follows:
16 |
17 | ```python
18 | map(function, iterable)
19 | map(function, iterable, *iterables)
20 | ```
21 |
22 | The first parameter `function` is function itself that transforms the element to
23 | the iterable. The `function` can either be a regular function or a lambda.
24 |
25 | Reference:
26 |
27 | ## Using `map()` function with other functions to transform data
28 |
29 | ```python
30 | def capitalize_and_ascii_sum(word: str):
31 | """
32 | This method capitalizes the word and finds out the sum of ASCII value of
33 | all characters of a word
34 | """
35 | return sum(ord(x) for x in word.capitalize())
36 |
37 |
38 | animals = ['cat', 'dog', 'cow']
39 |
40 | transformed_data = map(capitalize_and_ascii_sum, animals)
41 | print(list(transformed_data)) # [280, 282, 297]
42 |
43 | ```
44 |
45 | Here the first parameter `capitalize_and_ascii_sum` is a callable that is mapped
46 | to each data of the list `animals`.
47 |
48 | ## Using `map()` function with lambda to transform data
49 |
50 | We can also use `lambda` instead of a function for transforming the data.
51 |
52 | ```python
53 | numbers = [1, 2, 3, 4, 5]
54 |
55 | squares = map(lambda x: x ** 2, numbers)
56 | print(list(squares)) # [1, 4, 9, 16, 25]
57 | ```
58 |
59 | Here, the `lambda` squares the number that is passed and maps the square values
60 | of all the elements of the list `numbers`.
61 |
62 |
63 | ## Using `map()` function to manipulate 2 lists
64 |
65 | We can also map 2 or more iterables to find out the resulting iterable. This
66 | method is useful when we have to perform operations between different data such
67 | as sum of each element of list.
68 |
69 | If we pass 2 iterables of different length, it will map the value until the
70 | shortest iterable gets exhausted.
71 |
72 | ```python
73 | food = ['apple', 'potato', 'chicken', 'banana']
74 | product = ['juice', 'chips', 'chilly', 'shake']
75 |
76 | dishes = map(lambda a, b: f'{a} {b}', food, product)
77 | print(list(dishes)) # ['apple juice', 'potato chips', 'chicken chilly', 'banana shake']
78 | ```
79 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/chapter-13.5-reduce.md:
--------------------------------------------------------------------------------
1 | # Chapter 13.5: The `reduce()` Function
2 |
3 | **Table of Contents**
4 |
5 | - [Chapter 13.5: The `reduce()` Function](#chapter-135-the-reduce-function)
6 | - [Introduction to `reduce` function](#introduction-to-reduce-function)
7 |
8 | ## Introduction to `reduce` function
9 |
10 | The `reduce` function performs calculation on all elements by iterating on items
11 | of an iterable. For example, we can find out the product of all numbers of a
12 | list. The function `reduce` can be imported from `functools` builtin library.
13 |
14 | The first parameter of the `reduce` function takes a function as a parameter
15 | that takes exactly 2 arguments. To reduce the iterable it first takes first 2
16 | elements and performs operation. The result of them is then fed to the function
17 | again with another element until the iterable gets exhausted.
18 |
19 | ```python
20 | from functools import reduce
21 |
22 | numbers = [1, 12, 30, 24, 8, 11, 15, ]
23 | result = reduce(lambda x, y: x * y, numbers)
24 | print(result) # 11404800
25 | ```
26 |
27 | Explanation of the above function:
28 |
29 | function: `lambda x, y: x * y`
30 |
31 | - step1: `lambda` => `1 * 12` = `12`
32 | - step2: `lambda` => `12 * 30` = `360`
33 | - step3: `lambda` => `360 * 24` = `8640`
34 | - step4: `lambda` => `8640 * 8` = `69120`
35 | - step5: `lambda` => `69120 * 11` = `760320`
36 | - step6: `lambda` => `760320 * 15` = `11404800`
37 |
38 | > **Note**: We might mistakenly think that we could find out sum of squares with
39 | > the `reduce` function by passing lambda as `lambda x,y: x**2 + y**2` but it
40 | > end up giving very large number since it finds out square of first 2 numbers
41 | > and in the next iteration, it again finds out the square of the past result
42 | > and add it with the square of the next occurrence.
43 |
44 | Explanation:
45 |
46 | ```python
47 | from functools import reduce
48 |
49 | numbers = [1, 2, 3, 4, 5]
50 | result = reduce(lambda x, y: x ** 2 + y ** 2, numbers)
51 | print(result) # 1373609
52 |
53 | ```
54 |
55 | here we might think that the reduce function does `1 + 4 + 9 + 16 + 25` but it
56 | does something like below:
57 |
58 | `[{(1 + 4) ** 2 + 9} ** 2 + 16] ** 2 + 25` which will be equal to `1373609`
59 |
60 | - step 1: `lambda` => `1 ** 2 + 2 ** 2` = `1 + 4` = `5`
61 | - step 2: `lambda` => `5 ** 2 + 3 ** 2` = `25 + 9` = `34`
62 | - step 3: `lambda` => `34 ** 2 + 4 ** 2` = `1156 + 16` = `1172`
63 | - step 4: `lambda` => `1172 ** 2 + 5 ** 2` = `1,373,584 + 25` = `1373609`
64 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/code/c0501_groupby.py:
--------------------------------------------------------------------------------
1 | from itertools import groupby
2 |
3 | """
4 | `groupby()` function
5 | """
6 | # example 1
7 | animals = ['Bear', 'Donkey', 'Cat', 'Dog', 'Camel', 'Elephant']
8 | animals = sorted(animals)
9 | values = groupby(animals, lambda x: x[0])
10 | grouped = {k: list(v) for k, v in values}
11 | print(grouped)
12 |
13 | # {'B': ['Bear'], 'C': ['Camel', 'Cat'], 'D': ['Dog', 'Donkey'], 'E': ['Elephant']}
14 |
15 | # Example 2
16 | coordinates = ((1, 2), (2, 3), (3, 4), (1, 3), (2, 4), (1, 4))
17 | f = lambda f: f[0]
18 |
19 | grouped = groupby(sorted(coordinates, key=f), f)
20 |
21 | print({k: list(v) for k,v in grouped})
22 | # {1: [(1, 2), (1, 3), (1, 4)], 2: [(2, 3), (2, 4)], 3: [(3, 4)]}
23 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/code/c0502_sorted.py:
--------------------------------------------------------------------------------
1 | # sorting in an ascending order
2 | print(sorted([1, 4, 7, 2, 9, 5, 3]))
3 |
4 | # sorting in an ascending order
5 | print(sorted([1, 4, 7, 2, 9, 5, 3], reverse=True))
6 |
7 |
8 | ## Sorting the dictionary
9 | person = {
10 | 'name': 'John Doe',
11 | 'age': 20,
12 | 'occupation': 'student'
13 | }
14 | print(sorted(person))
15 |
16 | # Sorting the dictionary to get the dictionary
17 | print(sorted(person.items()))
18 |
19 | print({k:v for k,v in sorted(person.items())})
20 |
21 |
22 | # sorting with the key
23 |
24 | students = [
25 | ('John', 2),
26 | ('Eve', 4),
27 | ('Jennifer', 3),
28 | ('Adam', 1)
29 | ]
30 |
31 | print(sorted(students, key=lambda x: x[1]))
32 |
--------------------------------------------------------------------------------
/notes/c13_advanced_functions/code/c0503_filter.py:
--------------------------------------------------------------------------------
1 | # https://docs.python.org/3/library/functions.html#filter
2 |
3 | def is_even(num):
4 | return num%2==0
5 |
6 | list_1 = [1,2,3,4,5,6,7,8,9,10]
7 |
8 | list_2 = filter(is_even, list_1)
9 |
10 | print(list(list_2))
11 |
--------------------------------------------------------------------------------
/notes/c14_regex/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 14: Regular Expressions (REGEX)
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 | >
9 |
10 | **Table of contents**:
11 |
12 | - [Regular Expressions](chapter-14.1-regular-expressions.md)
13 | - [REGEX Special Characters](chapter-14.2-regex-special-characters.md)
14 | - [The `re` module](chapter-14.3-the-re-module.md)
15 |
--------------------------------------------------------------------------------
/notes/c14_regex/chapter-14.1-regular-expressions.md:
--------------------------------------------------------------------------------
1 | **Table of Contents**
2 |
3 | # Chapter 14 Introduction to Regular Expressions
4 |
5 | **res**: https://docs.python.org/3/library/re.html
6 |
7 | Python has a standard library for regular expressions called `re` module. The `re` module provides us regular expression matching operations.
8 |
9 | Both _Unicode Strings_ and _Byte Strings_ can be searched using regular expressions in python however both cannot be mixed.
10 |
11 | Regular Expressions contain even wider range of escape characters to represent different patterns inside strings.
12 |
13 |
14 | Let us suppose we have the sentence:
15 |
16 | ```python
17 | str_1 = 'Regular expressions are better pattern matchers.'
18 | ```
19 |
20 | **Example 1:**
21 | If we want to match all `er` from the above string like shown below:
22 |
23 | > Regular expressions are bett`er` patt`er`n match`er`s.
24 |
25 | We can match the regular expression by specyfing regex string as: `r'er'`
26 |
27 |
28 | **Example 2:**
29 | If we want to match all `er` and `ar` from the above string like shown below:
30 |
31 | > Regul`ar` expressions `ar`e bett`er` patt`er`n match`er`s.
32 |
33 | We can match the regular expression by specifying regex string as: `r'[ae]r'`
34 |
35 |
36 | **Example 3:**
37 | If we want to match all the occurence that start with `J` and end with `n` from the following expression:
38 |
39 | ```python
40 | str_1 = 'John, Jane, Jennifer, Joan, Jon, Adam, Eve'
41 | ```
42 |
43 | We can match the regular expression by specifying regex string as: `r'J\w*n'` which results in matching the following:
44 |
45 | > `John`, `Jan`e, `Jenn`ifer, `Joan`, `Jon`, Adam, Eve'
46 |
47 | The example code snippet would be:
48 |
49 | ```py
50 | import re
51 | str_1 = 'John, Jane, Jennifer, Joan, Jon, Adam, Eve'
52 | print(re.findall(r'J\w*n', str_1))
53 |
54 | # ['John', 'Jan', 'Jenn', 'Joan', 'Jon']
55 | ```
56 |
57 |
58 | In above examples, we can see ordinary characters like `J`, `n`, etc. and special characters like `.`, `\`, `[ ]`, etc. A combination of those characters creates a regular expression.
59 |
60 | For Example
61 |
62 | - `"Ap+le"` matches words `Aple`, `Apple`, `Appple`, etc.
63 | - `"b..k"` matches words `book`, `beak`, `b ok`, etc.
64 |
--------------------------------------------------------------------------------
/notes/c14_regex/code/ch1400_regex.py:
--------------------------------------------------------------------------------
1 | import re
2 |
3 | # compile
4 | countries = ['USA', 'Japan', 'Angola', 'China', 'Algeria', 'Nepal', 'Argentina', 'Albania']
5 |
6 | # a word that starts with A and ends with a
7 | pattern = re.compile(r'^A\w*a$')
8 |
9 | for country in countries:
10 | print(pattern.match(country))
11 |
12 | """
13 | # Output
14 |
15 | None
16 | None
17 |
18 | None
19 |
20 | None
21 |
22 |
23 | """
24 |
25 | # compile example 2
26 |
27 | strings = ["0xaa", 'FA04', 'Ak45', 'As40', '0x5h', '0x56']
28 |
29 | pat = re.compile(r'^(0x)?[0-9A-Fa-f]+$')
30 |
31 | for st in strings:
32 | print(pat.match(st))
33 |
34 | """
35 | # output
36 |
37 |
38 |
39 | None
40 | None
41 | None
42 |
43 | """
44 |
45 | # search()
46 | text = "Dear sir, I've emailed you last week regarding the assignment. I've also sent you another mail specifying the next assignment."
47 |
48 | results = re.search(r'e?mail(ed)?', text)
49 | print(results)
50 |
51 | """
52 | # Output
53 |
54 |
55 | """
56 |
57 | # match()
58 | text = 'Dear sir'
59 | print(re.match(r'[a-z]e\w+', text, re.I))
60 | """
61 | # output
62 |
63 |
64 | """
65 |
66 | # # findall
67 | countries = 'USA, Japan, Angola, China, Algeria, Nepal, Argentina, Albania'
68 |
69 | print(re.findall(r'A[a-z]+a', countries))
70 |
71 | """
72 | # Output
73 |
74 | ['Angola', 'Algeria', 'Argentina', 'Albania']
75 | """
76 |
77 |
78 | # split
79 | countries = 'USA, Japan; Angola! China, Algeria% Nepal; Argentina/ Albania'
80 | print(re.split(r'\W+', countries))
81 | """
82 | # Output:
83 |
84 | ['USA', ' Japan', ' Angola', ' China', ' Algeria', ' Nepal', ' Argentina', ' Albania']
85 | """
86 |
87 |
88 | print(re.sub(r'\W+', ', ', countries))
89 | """
90 | # Output
91 |
92 | USA, Japan, Angola, China, Algeria, Nepal, Argentina, Albania
93 | """
94 |
--------------------------------------------------------------------------------
/notes/c15_type_hinting/example/ch1501_basic_type_hinting.py:
--------------------------------------------------------------------------------
1 | id: int = 1
2 | name: str = "John Doe"
3 | subjects: list[str] = ["Physics", "Chemistry", "Biology"]
4 | data: dict[str, int] = {
5 | "roll": 1,
6 | "age": 10,
7 | "grade": 5,
8 | }
9 |
--------------------------------------------------------------------------------
/notes/c15_type_hinting/example/ch1502_function_hinting.py:
--------------------------------------------------------------------------------
1 | # type hinting in functions
2 |
3 |
4 | def distance(x1: int, y1: int, x2: int, y2: int) -> float:
5 | return ((x2 - x1) ** 2 + (y2 - y1) ** 2) ** 0.5
6 |
7 |
8 | if __name__ == "__main__":
9 | dist = distance(0, 0, 5, 5)
10 | print(f"The distance is: {dist:10.3}.")
11 | # The distance is: 7.07.
12 |
--------------------------------------------------------------------------------
/notes/c15_type_hinting/example/ch1503_typing_module.py:
--------------------------------------------------------------------------------
1 | # Advanced type hinting using typing module
2 | # This is useful for python version below 3.10
3 |
4 | from typing import List, Literal, TypedDict, Union
5 |
6 | my_list: List[Union[int, str]] = ["John", "Jane", 2]
7 |
8 |
9 | class User(TypedDict):
10 | id: int
11 | name: str
12 | type: Literal["admin", "staff"]
13 |
14 |
15 | if __name__ == "__main__":
16 | print(f"My List: {my_list }")
17 |
18 | user: User = {
19 | "id": 1,
20 | "name": "John Doe",
21 | "type": "admin",
22 | }
23 | print(user)
24 |
--------------------------------------------------------------------------------
/notes/c15_type_hinting/example/ch1504_advanced_hinting.py:
--------------------------------------------------------------------------------
1 | # Advanced type hinting with less typing module
2 | # This is useful for python version greater than or equal to 3.10
3 |
4 | from typing import Literal, TypedDict
5 |
6 | my_list: list[int | str] = ["John", "Jane", 2]
7 |
8 |
9 | class User(TypedDict):
10 | id: int
11 | name: str
12 | type: Literal["admin", "staff"]
13 |
14 |
15 | if __name__ == "__main__":
16 | print(f"My List: {my_list }")
17 |
18 | user: User = {
19 | "id": 1,
20 | "name": "John Doe",
21 | "type": "admin",
22 | }
23 | print(user)
24 |
--------------------------------------------------------------------------------
/notes/c16_decorators/code/ch1401_decorator.py:
--------------------------------------------------------------------------------
1 | # ================================[ Example 1 ]================================
2 | # raw behavior of decorator
3 |
4 |
5 | def decorate_me(func):
6 | def inner(*args, **kwargs):
7 | print("This is first called before executing the original method")
8 | return func(*args, **kwargs)
9 |
10 | return inner
11 |
12 |
13 | def original_method():
14 | print("This is an original method")
15 |
16 |
17 | decorated = decorate_me(original_method)
18 | print("Calling Decorated method: ")
19 | decorated()
20 |
21 | # ================================[ Example 2 ]================================
22 |
23 |
24 | def auto_typecast(fn):
25 | def inner(x, y):
26 | if isinstance(x, str):
27 | x = int(x)
28 | if isinstance(y, str):
29 | y = int(y)
30 | return fn(x, y)
31 |
32 | return inner
33 |
34 |
35 | @auto_typecast
36 | def add(x, y):
37 | return x + y
38 |
39 |
40 | @auto_typecast
41 | def subtract(x, y):
42 | return x - y
43 |
44 |
45 | if __name__ == "__main__":
46 | print("adding number 5 and string '6': ", add(5, "6"))
47 | print("subtracting number 5 from string '10': ", subtract("10", 5))
48 |
--------------------------------------------------------------------------------
/notes/c16_decorators/code/ch1402_decorator_factory.py:
--------------------------------------------------------------------------------
1 | """
2 | # Decorator Factory
3 |
4 | Decorator factory is a higher order function, that accepts arguments
5 | """
6 | import time
7 |
8 |
9 | # ================================[ Example 1 ]================================
10 |
11 |
12 | def log(before: bool, after: bool):
13 | def factory(func):
14 | def _inner(*args, **kwargs):
15 | if before:
16 | print(f"function {func} execution started at: {time.time()}")
17 | result = func(*args, **kwargs)
18 | if after:
19 | print(f"function {func} execution ended at: {time.time()}")
20 | return result
21 |
22 | return _inner
23 |
24 | return factory
25 |
26 |
27 | @log(True, True)
28 | def delay_execution(seconds):
29 | time.sleep(seconds)
30 |
31 | @log(before=True, after=False)
32 | def print_hello():
33 | print("Hello")
34 | # ================================[ Example 2 ]================================
35 | def moves(x: int, y: int):
36 | """
37 | This Decorator factory accepts 2 arguments which will then be used later by
38 | the inner function.
39 | """
40 |
41 | def factory(function):
42 | def inner(current_x, current_y):
43 | return function(current_x + x, current_y + y)
44 |
45 | return inner
46 |
47 | return factory
48 |
49 |
50 | @moves(x=5, y=5)
51 | def move_king(x, y):
52 | print(f"moved king to: ({x}, {y})")
53 |
54 |
55 | if __name__ == "__main__":
56 | delay_execution(2)
57 | print_hello()
58 | move_king(1, 1)
59 |
--------------------------------------------------------------------------------
/notes/c16_decorators/code/ch1403_class_based_decorator.py:
--------------------------------------------------------------------------------
1 | from typing import Any, Callable
2 |
3 |
4 | class LoggerDecorator:
5 | def __init__(self, func: Callable) -> None:
6 | self.func = func
7 |
8 | def __call__(self, *args: Any, **kwds: Any) -> Any:
9 | print(f"the function {self.func.__name__} is being called")
10 | result = self.func(*args, **kwds)
11 | print(f"the function {self.func.__name__} has been successfully called")
12 | return result
13 |
14 |
15 | @LoggerDecorator
16 | def regular_function():
17 | print("this is a function body")
18 |
19 |
20 | if __name__ == "__main__":
21 | regular_function()
22 |
--------------------------------------------------------------------------------
/notes/c16_decorators/code/ch1404_decorating_class.py:
--------------------------------------------------------------------------------
1 | def personify(cls):
2 | class Wrapped(cls):
3 | def set_full_name(self, full_name: str):
4 | self.full_name = full_name
5 |
6 | return Wrapped
7 |
8 |
9 | @personify
10 | class Animal:
11 | pass
12 |
13 |
14 | animal = Animal()
15 | animal.set_full_name("John")
16 | print(f"The full name is: {animal.full_name}")
17 |
--------------------------------------------------------------------------------
/notes/c17_mixins/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 17: Mixins
2 |
3 | **Table of contents**:
4 | - [Chapter 17: Mixins](#chapter-17-mixins)
5 | - [Introduction to mixin](#introduction-to-mixin)
6 | - [Example of a mixin](#example-of-a-mixin)
7 |
8 |
9 | ## Introduction to mixin
10 |
11 | Mixin is a class that adds method implementation. It is intended to be used to
12 | add method implementation but not instantiation so we generally add methods to
13 | the mixin class. Mixin uses a concept of multiple inheritance to combine all
14 | parent methods to create a new type by a child class.
15 |
16 | We can implement multiple mixins in a child class to expand its usability and
17 | reduce code duplication. Python uses `C3 Linearization` or
18 | `Method Resolution order` to deal with collisions if there are same methods
19 | defined in multiple mixins. To know more about it, you can review the topic
20 | `Multiple Inheritance`.
21 |
22 | Reference:
23 |
24 | ### Example of a mixin
25 | ```python
26 | # resource/c17_mixins/example/ch1501_mixin.py
27 | class Vehicle:
28 | """
29 | Base class
30 | """
31 |
32 | name: str
33 |
34 | def __init__(self, name: str) -> None:
35 | self.name = name
36 |
37 |
38 | class EvMixin:
39 | """
40 | A mixin that adds more method implementation without affecting the parent's
41 | instantiation
42 | """
43 |
44 | name: str # this mixin expects attribute `name` to be in the parent class
45 |
46 | def recharge_with_ac(self):
47 | assert self.name
48 | print(f"⚡: Recharging {self.name} with alternating current")
49 |
50 | def recharge_with_dc(self):
51 | print(f"⚡: Recharging {self.name} with direct current")
52 |
53 |
54 | class ElectricCar(Vehicle, EvMixin):
55 | """
56 | A child class that inherits `Vehicle` and implements `EvMixin`.
57 | """
58 |
59 | def __init__(self, name: str) -> None:
60 | super().__init__(name)
61 |
62 |
63 | if __name__ == "__main__":
64 | car = ElectricCar("my electric car")
65 | car.recharge_with_ac()
66 | car.recharge_with_dc()
67 |
68 | ```
69 |
70 | **Output**
71 | ```
72 | ⚡: Recharging my electric car with alternating current
73 | ⚡: Recharging my electric car with direct current
74 | ```
75 |
--------------------------------------------------------------------------------
/notes/c17_mixins/example/ch1701_mixin.py:
--------------------------------------------------------------------------------
1 | """
2 | ! To run, please run the following command in your preferred shell
3 |
4 | python resource/c17_mixins/example/ch1701_mixin.py
5 |
6 | """
7 |
8 |
9 | class Vehicle:
10 | """
11 | Base class
12 | """
13 |
14 | name: str
15 |
16 | def __init__(self, name: str) -> None:
17 | self.name = name
18 |
19 |
20 | class EvMixin:
21 | """
22 | A mixin that adds more method implementation without affecting the parent's
23 | instantiation
24 | """
25 |
26 | name: str # this mixin expects attribute `name` to be in the parent class
27 |
28 | def recharge_with_ac(self):
29 | assert self.name
30 | print(f"⚡: Recharging {self.name} with alternating current")
31 |
32 | def recharge_with_dc(self):
33 | print(f"⚡: Recharging {self.name} with direct current")
34 |
35 |
36 | class ElectricCar(Vehicle, EvMixin):
37 | """
38 | A child class that inherits `Vehicle` and implements `EvMixin`.
39 | """
40 |
41 | def __init__(self, name: str) -> None:
42 | super().__init__(name)
43 |
44 |
45 | if __name__ == "__main__":
46 | car = ElectricCar("my electric car")
47 | car.recharge_with_ac()
48 | car.recharge_with_dc()
49 |
--------------------------------------------------------------------------------
/notes/c17_mixins/example/ch1702_multiple_mixins.py:
--------------------------------------------------------------------------------
1 | """
2 | ! To run, please run the following command in your preferred shell
3 |
4 | python resource/c17_mixins/example/ch1702_multiple_mixins.py
5 |
6 | """
7 |
8 |
9 | class Vehicle:
10 | """
11 | Base class
12 | """
13 |
14 | name: str
15 |
16 | def __init__(self, name: str) -> None:
17 | self.name = name
18 |
19 |
20 | class EvMixin:
21 | """
22 | A mixin that adds more method implementation without affecting the parent's
23 | instantiation
24 | """
25 |
26 | name: str # this mixin expects attribute `name` to be in the parent class
27 |
28 | def recharge_with_ac(self):
29 | assert self.name
30 | print(f"⚡: Recharging {self.name} with alternating current")
31 |
32 | def recharge_with_dc(self):
33 | print(f"⚡: Recharging {self.name} with direct current")
34 |
35 |
36 | class GasolineMixin:
37 | """
38 | Something similar to EvMixin, but with other method implementation
39 | """
40 |
41 | MAX_VOLUME: int
42 | fuel_available: int
43 |
44 | def refuel(self, volume: int):
45 | new_volume = self.fuel_available + volume
46 | if new_volume > self.MAX_VOLUME:
47 | print(f"Overflow: {new_volume - self.MAX_VOLUME}")
48 | self.fuel_available = self.MAX_VOLUME
49 | else:
50 | self.fuel_available = new_volume
51 |
52 |
53 | class HybridCar(Vehicle, EvMixin, GasolineMixin):
54 | """
55 | A child class that inherits `Vehicle` and implements `EvMixin`.
56 | """
57 |
58 | MAX_VOLUME = 50
59 |
60 | def __init__(self, name: str) -> None:
61 | super().__init__(name)
62 | self.fuel_available = 0
63 |
64 |
65 | if __name__ == "__main__":
66 | car = HybridCar("my electric car")
67 | car.refuel(60) # Overflow: 10
68 | car.recharge_with_ac() # ⚡: Recharging my electric car with alternating current
69 |
--------------------------------------------------------------------------------
/notes/c18_python_http/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 18: the `http` module
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 |
9 | **Table of contents**:
10 |
11 | ### 🚧 Under Construction 🚧
12 | This section is currently incomplete and will be updated in the future. Stay tuned for more content!
13 |
14 | 💡 **Want to contribute?**
15 | Fork the repository, make your changes, and submit a Pull Request. Your contributions are greatly appreciated! 🙌
16 |
--------------------------------------------------------------------------------
/notes/c18_python_http/example/ch1801_http_client.py:
--------------------------------------------------------------------------------
1 | from http import client
2 |
3 |
4 | connection = client.HTTPSConnection("www.python.org")
5 |
6 | # connection.connect()
7 | connection.request("GET", "/")
8 |
9 | resp = connection.getresponse()
10 | print("status, reason: {}, {}".format(resp.status, resp.reason))
11 |
--------------------------------------------------------------------------------
/notes/c19_requests/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 19: the `requests` module
2 |
3 | > **Note**:
4 | >
5 | > please refer to the repository
6 | > **[python projects](https://github.com/ghimiresdp/python-projects)** for more
7 | > exercises and projects related to this chapter.
8 |
9 | **Table of contents**:
10 |
11 | ### 🚧 Under Construction 🚧
12 | This section is currently incomplete and will be updated in the future. Stay tuned for more content!
13 |
14 | 💡 **Want to contribute?**
15 | Fork the repository, make your changes, and submit a Pull Request. Your contributions are greatly appreciated! 🙌
16 |
--------------------------------------------------------------------------------
/notes/c21_testing/README.md:
--------------------------------------------------------------------------------
1 | # Chapter 21: Software Testing in Python
2 |
3 | **Table of contents**:
4 |
5 | - [21.1: Introduction to Software Testing](./chapter-21.1-intro.md)
6 | - [21.2: Testing using `unittest` module](./chapter-21.2-unittest.md)
7 | - [21.3: The `pytest` Package](./chapter-21.3-pytest.md)
8 |
--------------------------------------------------------------------------------
/notes/c21_testing/chapter-21.2-unittest.md:
--------------------------------------------------------------------------------
1 | # Chapter 21.2: Testing using `unittest` module
2 |
3 | **Table of contents**:
4 | - [Chapter 21.2: Testing using `unittest` module](#chapter-212-testing-using-unittest-module)
5 | - [Introduction to `unittest`](#introduction-to-unittest)
6 | - [Example of unittest](#example-of-unittest)
7 | - [`setUp` and `tearDown` methods](#setup-and-teardown-methods)
8 |
9 |
10 |
11 | ## Introduction to `unittest`
12 |
13 | The `unittest` module is a python's builtin test module. It contains all basics
14 | of software testing, example: test discovery,assertion, setup/teardown, etc.
15 |
16 | As python is an Object-oriented programming language, it's `unittest` module
17 | also implements Class-based interface to setup the test module. We can easily
18 | import `unittest` module to start testing our software.
19 |
20 | ### Example of unittest
21 |
22 | The following is a basic example of a testcase which will test for the
23 | correctness of the given function, "reverse_word".
24 |
25 | ```python
26 | import unittest
27 |
28 | def reverse_word(word: str):
29 | return word[::-1]
30 |
31 | class TestReverseWord(unittest.TestCase):
32 | def test_reverse():
33 | self.assertEqual(reverse_word("abcde"), "edcba")
34 |
35 |
36 | if __name__ == "__main__":
37 | main()
38 | ```
39 |
40 | ## `setUp` and `tearDown` methods
41 |
42 | A setup method is a method that runs before each test. The `setUp` method
43 | generally initializes values before testcases are run. For example, If we need
44 | an authorization token to test an API, we can get them in the `setUp` method.
45 |
46 | A `tearDown` method is a method that runs after each test. This method is used
47 | as a cleanup method after test gets executed.
48 |
49 | An example of setup and teardowm methods are as follows:
50 | ```python
51 | def celsius_to_fahrenheit(celsius: float):
52 | return (9 / 5) * celsius + 32
53 |
54 |
55 | class TestTemperatureChange(TestCase):
56 | def setUp(self) -> None:
57 | # This method runs before each test case inside of this test class
58 | self.celsius = 37
59 |
60 | def tearDown(self) -> None:
61 | # This method runs after each test case inside of this test class
62 | del self.celsius
63 |
64 | def test_conversion_from_setup(self):
65 | self.assertAlmostEqual(celsius_to_fahrenheit(self.celsius), 98.6)
66 |
67 | ```
68 |
--------------------------------------------------------------------------------
/notes/c21_testing/chapter-21.3-pytest.md:
--------------------------------------------------------------------------------
1 | # Chapter 21.3: The `pytest` Package
2 |
3 | **Table of contents**:
4 |
5 |
6 | ## The `pytest` package.
7 |
8 | Pytest is a testing framework which is made to simplify tests. It allows us to
9 | test our programs with the help of functional programming technique.
10 |
11 | Some of the interesting features of `pytest` are as follows:
12 |
13 | - Auto discovery of test modules and functions
14 | - Fixtures to reduce repetitions in test codes
15 | - Plugins can be introduced to enhance testing experience
16 |
17 |
18 | ## Installing `pytest`
19 |
20 | To install `pytest`, we need to install it using package manager of our choice.
21 | For example, we could use `pip`, `poetry`, etc. to install `pytest` package.
22 | ```shell
23 | # using pip
24 | pip install pytest
25 |
26 | # using poetry
27 | poetry add pytest
28 | ```
29 | To know more about `pytest`, you can read the detailed `pytest` documentation
30 | [here](https://docs.pytest.org/en/stable/).
31 |
--------------------------------------------------------------------------------
/notes/c21_testing/code/c21_001_intro.py:
--------------------------------------------------------------------------------
1 | """
2 | ! Simple unit testing with `assert` keyword
3 |
4 | * The `assert` keyword is used to check for the truthfulness or equality of the
5 | given expression
6 | * It is a python's reserved keyword.
7 | """
8 |
9 |
10 | def multiply_divide(number):
11 | if number % 2 == 0:
12 | return int(number / 2)
13 | return number * 2
14 |
15 |
16 | def test():
17 | assert multiply_divide(4) == 2
18 |
19 | assert multiply_divide(4) == 8 # AssertionError
20 | """NOTE: if we uncomment the above line, we get an `AssertionError`.
21 | Assertion error occurs when we do not get expected result.
22 |
23 | The test below was intentionally checked for the equality of 1 + 2 == 4
24 | so that we would get `AssertionError`
25 | """
26 |
27 |
28 | if __name__ == "__main__":
29 | test()
30 |
--------------------------------------------------------------------------------
/notes/c21_testing/code/c21_002_unittest.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase, main
2 |
3 |
4 | def reverse_word(word: str):
5 | return word[::-1]
6 |
7 |
8 | def divide(a, b):
9 | """
10 |
11 | Args:
12 | a (int | float): _description_
13 | b (int | float): _description_
14 |
15 | Returns:
16 | _type_: float
17 | """
18 | return a / b
19 |
20 |
21 | class TestReverseWord(TestCase):
22 | def test_correct_reverse(self):
23 | self.assertEqual(reverse_word("abcde"), "edcba")
24 |
25 | def test_almost_equal_value(self):
26 | self.assertAlmostEqual(divide(22, 7), 3.14, places=2)
27 |
28 | def test_raises_error(self):
29 | with self.assertRaises(ZeroDivisionError) as err:
30 | divide(2, 0)
31 | self.assertEqual(str(err.exception), "division by zero")
32 |
33 |
34 | if __name__ == "__main__":
35 | main()
36 |
--------------------------------------------------------------------------------
/notes/c21_testing/code/c21_003_setup_teardown.py:
--------------------------------------------------------------------------------
1 | from unittest import TestCase, main
2 |
3 |
4 | def celsius_to_fahrenheit(celsius: float):
5 | return (9 / 5) * celsius + 32
6 |
7 |
8 | class TestTemperatureChange(TestCase):
9 | def setUp(self) -> None:
10 | # This method runs before each test case inside of this test class
11 | self.celsius = 37
12 |
13 | def tearDown(self) -> None:
14 | # This method runs after each test case inside of this test class
15 | del self.celsius
16 |
17 | @classmethod
18 | def setUpClass(cls) -> None:
19 | # This method runs once before test case inside of this test class start
20 | cls.initial_value = 0
21 |
22 | def test_conversion_from_setup_class(self):
23 | self.assertEqual(self.initial_value, 0)
24 |
25 | def test_conversion_from_setup(self):
26 | self.assertAlmostEqual(celsius_to_fahrenheit(self.celsius), 98.6)
27 |
28 |
29 | if __name__ == "__main__":
30 | main()
31 |
--------------------------------------------------------------------------------
/problem_solving/README.md:
--------------------------------------------------------------------------------
1 | # Problem Solving
2 |
3 | This section will help us become more proficient with problem solving skills
4 | with Python Programming Language.
5 |
6 | This section primarily contains 2 different sections which are as follows:
7 |
8 | ## Basic Problem Solving
9 |
10 | This section helps us warming up with basic problem solving skills. It's goal is
11 | to review our previously learned python notes section.
12 |
13 | Some of the Basic Problem Solving solutions are as follows:
14 |
15 | 1. [Practical Number Solution](basic/practical_number.py)
16 | 2. [Greatest Common Divisor](basic/gcd.py)
17 | 3. [Matrix Multiplication](basic/matrix_multiplication.py)
18 | 4. [Median Calculation](basic/median.py)
19 | 5. [Reverse digits of an integer](basic/reverse_digits.py)
20 |
21 |
22 | ## Dynamic Programming
23 |
24 | This section helps us solving advanced problems more efficiently and using more
25 | optimum solutions. Solving this type of problem helps us efficiently solve
26 | problems while managing time and space complexity efficiently.
27 |
28 | Some of the Dynamic Programming solutions are as follows:
29 |
30 | 1. [Coin Change Problem](dp/coin_change.py)
31 | 2. [Fibonacci Series Problem](dp/fibonacci.py)
32 | 3. Palindrome Partition Problem
33 | 4. Minimizing the sum of list of integers
34 | 5. Longest Common Subsequence Problem
35 |
--------------------------------------------------------------------------------
/problem_solving/basic/gcd.py:
--------------------------------------------------------------------------------
1 | """
2 | Greatest Common Divisor (GCD)
3 | -----------------------------
4 |
5 | Greatest Common Divisor or the highest common factor is a common divisor of the
6 | given numbers.
7 |
8 | It is the largest positive integer that divides both numbers without leaving
9 | remainders.
10 |
11 | For example numbers 12 and 18 has GCD 6
12 |
13 | """
14 |
15 | from functools import reduce
16 |
17 |
18 | def _gcd(a: int, b: int):
19 | """
20 | Finding out GCD between 2 numbers is so easy in python. we try to perform
21 | modulo operator between 2 numbers until we find remainder to be 0.
22 |
23 | For example we have 2 numbers 48 and 64
24 |
25 | a = 48, b = 64
26 | Step 1: (a, b) = (b, a%b) = (64, 48 % 64) = (64, 48) b != 0
27 | Step 2: (a, b) = (48, 64 % 48) = (48, 16) b != 0
28 | Step 3: (a, b) = (16, 48 % 16) = (16, 0) b == 0
29 |
30 | Setp 4: b == 0, so GCD = a = 16
31 |
32 | """
33 | # find out gcd between 2 numbers
34 | while b:
35 | a, b = b, a % b
36 | return a
37 |
38 |
39 | def gcd(numbers: list[int]) -> int:
40 | """
41 | To find out gcd between multiple numbers, we simply reduce the above _gcd
42 | function to each numbers in the list.
43 |
44 | """
45 | return reduce(_gcd, numbers)
46 |
47 |
48 | if __name__ == "__main__":
49 | print(_gcd(1, 2)) # 1
50 | print(_gcd(12, 18)) # 6
51 | print(_gcd(9, 18)) # 9
52 | print(_gcd(32, 40)) # 8
53 |
54 | print(gcd([1, 2])) # 1
55 | print(gcd([32, 40, 80])) # 8
56 | print(gcd([32, 64, 96])) # 32
57 | print(gcd([64, 96, 160, 320])) # 32
58 | print(gcd([64, 96, 160, 320, 48])) # 16
59 |
--------------------------------------------------------------------------------
/problem_solving/basic/median.py:
--------------------------------------------------------------------------------
1 | """
2 | # Median
3 | --------
4 |
5 | Median is a statistical calculation procedure that finds out the number that
6 | separates the higher half of the numbers from a list with lower half.
7 |
8 | Generally, when we have odd number of elements, we have the number in the list,
9 | however, if there are even number of elements, we have to find the mean of two
10 | median values.
11 |
12 | if x(n) is nth element of the list, then
13 |
14 | | x((n+1)/2) if n is odd
15 | median = |
16 | | (x(n/2) + x((n/2)+1)/2 if n is even
17 |
18 | NOTE: The index starts at 0 in python so while of finding out nth element, we
19 | have to find out element at (n-1)th index hence,
20 | x((n+1)/2) becomes x((n+1)/2 -1) and
21 | x((n/2) becomes x((n/2 -1))
22 | """
23 |
24 |
25 | def median(items: list[int]):
26 | """
27 | Median needs sorted list since we will find out the middle number that is in
28 | ascending order.
29 | """
30 | items.sort()
31 | n = items.__len__()
32 |
33 | if n % 2 == 1:
34 | return items[(n + 1) // 2 - 1]
35 | else:
36 | return (items[n // 2 - 1] + items[n // 2]) / 2
37 |
38 |
39 | if __name__ == "__main__":
40 | list = [1, 2, 3, 4, 5]
41 | print(f"Median of the list is : {median(list)}") # 3
42 |
43 | list = [1, 2, 3, 4, 5, 6]
44 | print(f"Median of the list is : {median(list)}") # 3.5
45 |
--------------------------------------------------------------------------------
/problem_solving/basic/practical_number.py:
--------------------------------------------------------------------------------
1 | """
2 | # Practical Number Solution
3 | ---------------------------
4 |
5 | A practical number is a positive integer where every smaller can be expressed as
6 | a sum of distinct divisors of that number.
7 |
8 | In other words, a number is a practical number whose divisors can be combined to
9 | create a number below it.
10 |
11 | For example 12 is a practical number.
12 | It's divisors are: 1, 2, 3, 4, and 6.
13 |
14 | 1 -> 1
15 | 2 -> 2
16 | 3 -> 3
17 | 4 -> 4
18 | 5 -> 4 + 1
19 | 6 -> 6
20 | 7 -> 7 + 1
21 | 8 -> 6 + 2
22 | 9 -> 6 + 3
23 | 10 -> 6 + 4
24 | 11 -> 6 + 4 + 1
25 |
26 | """
27 |
28 | import math
29 |
30 |
31 | def find_divisors(num: int) -> list[int]:
32 | divisors = {1}
33 | for n in range(2, int(math.sqrt(num)) + 1):
34 | if num % n == 0:
35 | divisors.add(n)
36 | divisors.add(num // n)
37 | return list(divisors)
38 |
39 |
40 | def has_sum(target: int, _set: list[int]):
41 | # if the number itself exists, or number return True
42 | if target == 0 or target in _set:
43 | return True
44 |
45 | sums = {0}
46 |
47 | for num in _set:
48 | new_sums = {x + num for x in sums}
49 | sums.update(new_sums)
50 | if target in sums:
51 | return True
52 | return False
53 |
54 |
55 | def is_practical_number(num: int) -> bool:
56 | if num < 1:
57 | return False
58 | divisors = find_divisors(num)
59 | sum_previous = 1
60 |
61 | for i in range(1, len(divisors)):
62 | if divisors[i] > sum_previous + 1:
63 | return False
64 | sum_previous += divisors[i]
65 |
66 | for n in range(1, num):
67 | if not has_sum(n, divisors):
68 | return False
69 | return True
70 |
71 |
72 | if __name__ == "__main__":
73 | for number in [8, 10, 12, 15]:
74 | print(
75 | f"{number:<5d}: {'✅' if is_practical_number(number) else '⛔ Not'} Practical"
76 | )
77 |
78 | """
79 |
80 | OUTPUT
81 | ---------
82 | 8 : ✅ Practical
83 | 10 : ⛔ Not Practical
84 | 12 : ✅ Practical
85 | 15 : ⛔ Not Practical
86 |
87 | """
88 |
--------------------------------------------------------------------------------
/problem_solving/basic/reverse_digits.py:
--------------------------------------------------------------------------------
1 | """
2 | Reverse Digits of an integer
3 | ----------------------------
4 |
5 | Given an integer, reverse all the digits of an integer mathematically. Example,
6 | if an integer 12345 is given, the reversed integer should be 54321.
7 |
8 | Mathematically reversing an integer requires a loop that divides the integer and
9 | find outs the remainder to know the digit of one place and multiplying back by
10 | 10 and adding the remainder of the previous iteration until the number becomes 0.
11 |
12 | Steps:
13 | 1. result = 0
14 |
15 | 2. divide the integer by 10 and find out remainder and result
16 | - number = num // 10 -> 12345 // 10 = 1234
17 | - r = num % 10 -> 12345 %10 = 5
18 | - num = number -> 1234
19 | 3. now multiply the previous remainder by 10 and add with the current remainder
20 | - result = result * 10 + r
21 | 4. Repeat steps 2 and 3 until number becomes 0
22 | """
23 |
24 |
25 | def reverse_digits(number: int) -> int:
26 | result = 0
27 | if number < 0:
28 | raise ValueError("Negative numbers not allowed")
29 | if number < 10:
30 | return number
31 | while number > 0:
32 | number, result = (number // 10, result * 10 + number % 10)
33 | return result
34 |
35 |
36 | if __name__ == "__main__":
37 | print(reverse_digits(123)) # 321
38 | print(reverse_digits(12345)) # 54321
39 | print(reverse_digits(87214)) # 41278
40 |
--------------------------------------------------------------------------------
/problem_solving/dp/fibonacci.py:
--------------------------------------------------------------------------------
1 | """
2 | Find the nth item of a fibonacci series
3 | ---------------------------------------
4 |
5 | Fibonacci Series is a series in which a number in a series is equal to the sum
6 | of last 2 numbers in the series.
7 |
8 | An example of a fibonacci series is 1, 1, 2, 3, 5, 8, 13, 21, 34...
9 |
10 | Nth item of fibonacci series can be solved both using dynamic programming and
11 | recursion however recursive functions will be much slower for higher value of n
12 | so it is recommended to use dynamic programming to find it out.
13 | """
14 |
15 |
16 | def fibonacci_recursion(n: int) -> int:
17 | if n < 1:
18 | raise ValueError("Negative number is not acceptable")
19 | if n < 3:
20 | return 1
21 | return fibonacci_recursion(n - 1) + fibonacci_recursion(n - 2)
22 |
23 |
24 | def fibonacci(n: int):
25 | if n < 1:
26 | raise ValueError("Negative number is not acceptable")
27 | if n < 3:
28 | return 1
29 | a, b = 1, 1
30 | for n in range(3, n + 1):
31 | a, b = b, a + b
32 |
33 | return b
34 |
35 |
36 | if __name__ == "__main__":
37 | print(fibonacci_recursion(8)) # 21
38 | print(fibonacci_recursion(9)) # 34
39 | print(fibonacci_recursion(10)) # 55
40 | # It takes too long to find out the number for higher value of N
41 | # and eventually maximum depth of recursion will be exceeded in such case
42 | # print(fibonacci_recursion(40)) # 102334155 (very slow)
43 |
44 | print(fibonacci(8)) # 21
45 | print(fibonacci(9)) # 34
46 | print(fibonacci(10)) # 55
47 | print(fibonacci(40)) # 102334155
48 | print(fibonacci(100)) # 354224848179261915075
49 |
--------------------------------------------------------------------------------
/projects/solutions/hangman/README.md:
--------------------------------------------------------------------------------
1 | # Hangman Game
2 |
--------------------------------------------------------------------------------
/projects/solutions/hangman/game_data.py:
--------------------------------------------------------------------------------
1 | HANGMAN = """\
2 | ██ ██ ██████ ███ ██ ██████ ██ ██ ██████ ███ ██
3 | ██ ██ ██ ██ ████ ██ ██ ███ ███ ██ ██ ████ ██
4 | ████████ ████████ ██ ██ ██ ██ ████ ██ ██ ██ ████████ ██ ██ ██
5 | ██ ██ ██ ██ ██ ████ ██ ██ ██ ██ ██ ██ ██ ████
6 | ██ ██ ██ ██ ██ ███ ███████ ██ ██ ██ ██ ██ ███
7 | """
8 |
9 | WINNER = """\
10 | ██ ██ ████████ ███ ██ ███ ██ ████████ ███████
11 | ██ ██ ██ ████ ██ ████ ██ ██ ██ ██
12 | ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████████ ███████
13 | ███ ███ ██ ██ ████ ██ ████ ██ ██ ██
14 | ██ ██ ████████ ██ ███ ██ ███ ████████ ██ ███
15 | """
16 |
17 | LOOSER = """\
18 | ██ ██████ ██████ ███████ ████████ ███████
19 | ██ ██ ██ ██ ██ ██ ██ ██ ██
20 | ██ ██ ██ ██ ██ ██████ ████████ ████████
21 | ██ ██ ██ ██ ██ ██ ██ ██ ██
22 | ████████ ██████ ██████ ███████ ████████ ██ ███
23 | """
24 |
--------------------------------------------------------------------------------
/projects/solutions/rock_paper_scissor/README.md:
--------------------------------------------------------------------------------
1 | # Project Rock Paper Scissor
2 |
3 | The project uses `random` library.
4 | the file `game.py` consists of a class `Game` that does all the gaming logic.
5 |
6 | all of the states of the game are defined by instance attributes which are as follows:
7 |
8 | 1. `username`: Used to save the name of the player.
9 | 2. `_consecutive_wins`: Used to track consecutive wins.
10 | 3. `last_win`: Used to track who won the last match.
11 | 4. `_matches`: Used to count the total number of matches.
12 | 5. `_draws`: Used to count total number of draws.
13 | 6. `_bot_score`: Used to count the total number of matches won by the bot
14 | 7. `_user_score`: Used to count the total number of matches won by the user
15 | 8. `user_input`: Used to store the last input of the user
16 | 9. `bot_input`: Used to store the last input of the bot
17 |
18 | ## The `__init__()` method
19 | The `__init__()` function sets the default state whenever initialized.
20 |
21 | ## The `__get_bot_choice()` method:
22 | This method gives the guess of the bot.
23 |
24 | ## The `__display_menu()` method:
25 | This method displays the main menu
26 |
27 | ## Running the game
28 | to run the project, move the terminal to the project folder.
29 |
30 | ```shell
31 | $ cd projects/project_01/rock_paper_scissor
32 | ```
33 | and then run the `game.py` file using the command below:
34 | ```shell
35 | $ python game.py
36 | ```
37 |
--------------------------------------------------------------------------------
/projects/solutions/school_manager/README.md:
--------------------------------------------------------------------------------
1 | # School Manager
2 |
--------------------------------------------------------------------------------
/projects/solutions/school_manager/utils.py:
--------------------------------------------------------------------------------
1 | import os
2 |
3 | RECORD_PATH = 'records'
4 |
5 | def clear_screen():
6 | os.system('cls' if os.name == 'nt' else 'clear')
7 |
8 | def display_message(msg: str):
9 | print("+", '-'*78, '+' , sep='')
10 | print("|", msg.center(78), '|' , sep='')
11 | print("+", '-'*78, '+' , sep='')
12 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name="python-perks"
3 | version = "4.0.1"
4 | description="A repository for Python course notes, examples, and lab exercises targeted to students, professionals, and enthusiasts."
5 | authors=[
6 | { name="Sudip Ghimire", email="ghimiresdp@gmail.com" },
7 | ]
8 | license="MIT"
9 | readme="README.md"
10 | requires-python=">=3.10"
11 |
12 | dependencies=[
13 | "requests>=2.32.3",
14 | ]
15 |
16 | [dependency-groups]
17 | dev=[
18 | "ipython>=8.31.0",
19 | "ruff>=0.8.4",
20 | ]
21 | test=[
22 | "pytest>=8.3.4",
23 | ]
24 |
25 | [tool.ruff]
26 | line-length=88
27 | indent-width=4
28 | target-version="py312"
29 | # Exclude a variety of commonly ignored directories.
30 | exclude=[
31 | ".bzr",
32 | ".direnv",
33 | ".eggs",
34 | ".git",
35 | ".git-rewrite",
36 | ".hg",
37 | ".ipynb_checkpoints",
38 | ".mypy_cache",
39 | ".nox",
40 | ".pants.d",
41 | ".pyenv",
42 | ".pytest_cache",
43 | ".pytype",
44 | ".ruff_cache",
45 | ".svn",
46 | ".tox",
47 | ".venv",
48 | ".vscode",
49 | "__pypackages__",
50 | "_build",
51 | "buck-out",
52 | "build",
53 | "dist",
54 | "node_modules",
55 | "site-packages",
56 | "venv",
57 | ]
58 |
59 | [tool.ruff.format]
60 | quote-style="double"
61 | indent-style="space"
62 | skip-magic-trailing-comma=false
63 |
--------------------------------------------------------------------------------
/requirements.txt:
--------------------------------------------------------------------------------
1 | # This file was autogenerated by uv via the following command:
2 | # uv pip compile pyproject.toml -o requirements.txt
3 | certifi==2024.12.14
4 | # via requests
5 | charset-normalizer==3.4.1
6 | # via requests
7 | idna==3.10
8 | # via requests
9 | requests==2.32.3
10 | # via python-perks (pyproject.toml)
11 | urllib3==2.3.0
12 | # via requests
13 |
--------------------------------------------------------------------------------