├── .gitignore ├── 01_python_fundamentals ├── 01_introduction_to_python │ ├── 01_intro_to_python │ │ └── readme.md │ └── readme.md ├── 02_lines_and_indentation │ ├── main.py │ └── readme.md ├── 03_character_sets │ ├── main.py │ └── readme.md ├── 04_tokens │ ├── main.py │ └── readme.md ├── 05_identifiers │ ├── main.py │ └── readme.md ├── 06_keywords │ └── readme.md ├── 07_operators │ ├── bitwise-operators.md │ ├── complete.md │ ├── main.py │ ├── practice.md │ ├── readme.md │ └── solution.md ├── 08_delimiters │ ├── main.py │ └── readme.md ├── 09_literals │ ├── main.py │ └── readme.md ├── 10_data_types │ ├── main.py │ └── readme.md ├── 11_variables_as_references │ ├── main.py │ └── readme.md ├── 12_decisions_statements │ ├── exercise.md │ ├── main.py │ ├── readme.md │ └── solutions.md ├── 13_iterations │ ├── exercise.md │ ├── main.py │ ├── readme.md │ └── solutions.md ├── 14_strings │ ├── function.md │ ├── methods.md │ ├── practice.md │ └── readme.md ├── 15_lists │ ├── if-with-lists.md │ ├── list-comprehension.md │ ├── practice.md │ ├── readme.md │ ├── solutions.md │ └── unpacking-sequences-and-slicing.md ├── 16_tuple │ ├── exercise.md │ ├── readme.md │ └── solutions.md ├── 17_set │ ├── exercise.md │ ├── main.py │ ├── readme.md │ └── solutions.md ├── 18_dict │ ├── exercise.md │ ├── main.py │ ├── readme.md │ └── solutions.md ├── 19_functions │ ├── functions.md │ ├── functions_in_modules.md │ ├── quiz.md │ ├── readme.md │ └── solutions.md ├── 20_regular_expressions │ ├── 01_basic_regular_expressions │ │ ├── problem1.1.py │ │ ├── problem1.2.py │ │ ├── problem1.3.py │ │ ├── problem1.4.py │ │ ├── problem1.5.py │ │ ├── problem1.6.py │ │ ├── problem1.7.py │ │ ├── problem1.8.py │ │ ├── problem1.9.py │ │ ├── problem2.0.py │ │ ├── problem2.1.py │ │ ├── problem2.2.py │ │ ├── problem2.3.py │ │ ├── problem2.4.py │ │ ├── problem2.5.py │ │ ├── problem2.6.py │ │ ├── problem2.7.py │ │ ├── problem2.8.py │ │ ├── problem2.9.py │ │ ├── problem3.0.py │ │ ├── problem3.1.py │ │ └── problem3.2.py │ ├── basic_regular_expressions.md │ └── readme.md ├── 21_working_with_files │ ├── example.md │ ├── file.md │ ├── logs.txt │ ├── logs_backup.txt │ ├── main.py │ ├── quiz.md │ ├── readme.md │ └── solutions │ │ ├── problem1.md │ │ ├── problem10.md │ │ ├── problem11.md │ │ ├── problem12.md │ │ ├── problem13.md │ │ ├── problem14.md │ │ ├── problem15.md │ │ ├── problem16.md │ │ ├── problem17.md │ │ ├── problem18.md │ │ ├── problem19.md │ │ ├── problem2.md │ │ ├── problem3.md │ │ ├── problem4.md │ │ ├── problem5.md │ │ ├── problem6.md │ │ ├── problem7.md │ │ ├── problem8.md │ │ └── problem9.md ├── 22_scope │ ├── example.txt │ ├── main.py │ └── readme.md ├── 23_modules │ ├── 01_import_and_attributes │ │ ├── b.py │ │ ├── c.py │ │ ├── main.py │ │ └── readme.md │ ├── 02_standard_library_modules │ │ └── readme.md │ ├── 03_byte_code │ │ ├── my_module.py │ │ └── readme.md │ ├── 04_module_search_path │ │ ├── 01_extend_module │ │ │ ├── common_utils │ │ │ │ ├── __init__.py │ │ │ │ └── helper.py │ │ │ ├── modules │ │ │ │ ├── __init__.py │ │ │ │ ├── main.py │ │ │ │ └── user.py │ │ │ └── readme.md │ │ ├── main.py │ │ ├── mypackage │ │ │ ├── __init__.py │ │ │ └── my_module.py │ │ └── readme.md │ ├── 05_configuring_the_search_path │ │ ├── main.py │ │ └── readme.md │ ├── 06_sys_path │ │ ├── main.py │ │ └── readme.md │ ├── 07_module_coding_basic │ │ ├── main.py │ │ ├── module1.py │ │ └── readme.md │ ├── 08_module_namespace │ │ ├── main.py │ │ ├── module2.py │ │ ├── module_example.py │ │ └── readme.md │ ├── 09_reloading_modules │ │ ├── changer.py │ │ └── readme.md │ └── readme.md ├── 24_modules_packages │ ├── 01_package_import_basic │ │ ├── main.py │ │ ├── readme.md │ │ └── web_scraper │ │ │ ├── __init__.py │ │ │ ├── crawler │ │ │ ├── __init__.py │ │ │ └── page_parser.py │ │ │ └── utils │ │ │ ├── __init__.py │ │ │ └── helper.py │ ├── 02_package_import_example │ │ └── readme.md │ └── readme.md ├── 25_advance_module │ ├── 01_module_design_concept │ │ └── readme.md │ ├── 02_data_hiding_in_modules │ │ ├── main.py │ │ ├── module.py │ │ ├── readme.md │ │ └── underscore_example.py │ ├── 03_enable_future_feature │ │ ├── main.py │ │ └── readme.md │ ├── 04_mixed_usage_modes │ │ ├── calculator.py │ │ ├── greetings.py │ │ ├── main.py │ │ └── readme.md │ ├── 05_changig_the_module_search │ │ ├── main.py │ │ └── readme.md │ ├── 06_alias_in_modules │ │ └── readme.md │ └── readme.md ├── 26_testing_your_code │ ├── name.py │ ├── name_function.py │ ├── readme.md │ ├── survey.py │ ├── survey_program.py │ ├── test_name_function.py │ └── test_survey.py ├── 27_virtual_environment │ ├── Pipfile │ ├── Pipfile.lock │ ├── poetry.lock │ ├── pyproject.toml │ └── readme.md ├── common_utils │ ├── __init__.py │ └── helper.py └── readme.md ├── 02_object_oriented_programming ├── 01_Classes │ ├── 01_what_is_classes │ │ ├── main.py │ │ └── readme.md │ ├── 02_creating_a_class │ │ ├── main.py │ │ └── readme.md │ ├── 03_memory_allocation │ │ └── readme.md │ ├── 04_procedural_programming │ │ ├── main.py │ │ └── readme.md │ ├── 05_encapsulation │ │ ├── main.py │ │ └── readme.md │ ├── 06_getter_and_setter │ │ ├── main.py │ │ └── readme.md │ ├── 07_abstraction │ │ ├── 01_coupling │ │ │ ├── main.py │ │ │ └── readme.md │ │ ├── 02_coupling │ │ │ ├── main.py │ │ │ └── readme.md │ │ ├── 03_abstraction │ │ │ ├── main.py │ │ │ └── readme.md │ │ ├── couple.md │ │ └── readme.md │ ├── 08_constructor │ │ ├── main.py │ │ └── readme.md │ ├── 09_default_arguments │ │ ├── main.py │ │ └── readme.md │ ├── 10_constructor_overloading │ │ ├── main.py │ │ └── readme.md │ ├── 11_static_method_and_class_variable │ │ ├── main.py │ │ └── readme.md │ └── readme.md ├── 02_inheritance │ ├── 01_inheritance │ │ ├── main.py │ │ └── readme.md │ ├── 02_the_object_class │ │ ├── main.py │ │ └── readme.md │ ├── 03_constructors_and_inheritance │ │ ├── main.py │ │ └── readme.md │ ├── 04_overriding_methods │ │ ├── main.py │ │ └── readme.md │ ├── 05_super_function │ │ └── readme.md │ ├── 06_MRO_and_multiple_inheritance │ │ └── readme.md │ ├── 07_mixins │ │ └── readme.md │ ├── 08_polymorphism │ │ ├── data.csv │ │ ├── data.json │ │ ├── data.xml │ │ ├── main.py │ │ └── readme.md │ ├── 09_abstract_classes_and_methods │ │ ├── example.py │ │ ├── main.py │ │ ├── readme.md │ │ └── uicontrol.py │ ├── 10_callable_object │ │ ├── main.py │ │ └── readme.md │ └── readme.md ├── 04_interfaces │ ├── 01_tightly_coupled_code │ │ ├── main.py │ │ └── readme.md │ ├── 02_creating_an_interface │ │ ├── main.py │ │ └── readme.md │ ├── 04_what_is_dependency_injection │ │ ├── 01_constructor_injection │ │ │ ├── main.py │ │ │ └── readme.md │ │ ├── 02_setter_injection │ │ │ ├── main.py │ │ │ └── readme.md │ │ ├── 03_method_injection │ │ │ ├── main.py │ │ │ └── readme.md │ │ └── readme.md │ ├── 05_interface_segregation_principle │ │ └── readme.md │ ├── 06_Project_Video │ │ ├── 01_mytube_video_platform │ │ │ ├── main.py │ │ │ └── readme.md │ │ ├── 02_solution_of_project_video │ │ │ ├── main.py │ │ │ └── readme.md │ │ └── readme.md │ └── readme.md ├── 05_exceptions │ ├── exceptions.md │ ├── main.py │ └── readme.md ├── 08_lambda_expression │ ├── cart │ │ ├── __init__.py │ │ ├── admin.py │ │ ├── apps.py │ │ ├── models.py │ │ ├── static │ │ │ ├── script.js │ │ │ └── styles.css │ │ ├── templates │ │ │ └── product.html │ │ ├── tests.py │ │ ├── urls.py │ │ └── views.py │ ├── ecommerce │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ └── readme.md ├── 09_generators │ ├── practice.md │ └── readme.md ├── 10_python_types │ ├── 01_typing_system_in_python │ │ ├── main.py │ │ ├── readme.md │ │ └── script.js │ ├── 02_understanding_ducky_typing │ │ ├── main.py │ │ └── readme.md │ ├── main.py │ └── readme.md ├── 11_type_annotations │ ├── 01_type_annotations │ │ ├── main.py │ │ └── readme.md │ ├── 02_benefits_of_type_annotations │ │ ├── main.py │ │ └── readme.md │ ├── 03_constraining_type │ │ └── readme.md │ ├── 04_optional_type │ │ ├── failure.py │ │ ├── main.py │ │ ├── pizza_making_with_return.py │ │ ├── readme.md │ │ └── user.py │ ├── 05_union_type │ │ ├── main.py │ │ └── readme.md │ ├── 06_literals │ │ ├── main.py │ │ └── readme.md │ ├── 07_annotated │ │ ├── main.py │ │ └── readme.md │ ├── 08_newtype │ │ ├── main.py │ │ └── readme.md │ ├── 09_final │ │ ├── main.py │ │ └── readme.md │ └── readme.md ├── 12_collection_types │ ├── 01_annotated_collections │ │ ├── main.py │ │ └── readme.md │ ├── 02_homogenous_and_hetrogenous_collections │ │ ├── main.py │ │ └── readme.md │ ├── 03_typed_dict │ │ ├── main.py │ │ ├── readme.md │ │ └── typedict.py │ ├── 04_generic_types │ │ └── readme.md │ ├── 05_collection_abc_modules │ │ └── readme.md │ └── readme.md ├── 13_customizing_typechecker │ ├── 01_configuring_type_checker │ │ ├── main.py │ │ └── readme.md │ ├── 02_adopting_type_checker_practically │ │ └── readme.md │ ├── mypy.ini │ └── readme.md ├── 14_defining_our_own_type │ ├── 01_enums │ │ └── readme.md │ ├── 02_advance_anums │ │ ├── main.py │ │ └── readme.md │ ├── 03_user_defined_classes │ │ └── readme.md │ ├── 04_comparison_to_other_types │ │ └── readme.md │ ├── 05_user_defined_class_anatomy │ │ └── readme.md │ ├── 06_defining_your_interfaces │ │ └── readme.md │ ├── 07_natural_interface_design │ │ └── readme.md │ ├── 08_subtyping │ │ └── readme.md │ ├── 09_protocols │ │ └── readme.md │ ├── 10_decorator │ │ ├── main.py │ │ └── readme.md │ ├── 11_pydantic │ │ ├── main.py │ │ └── readme.md │ └── readme.md ├── 15_async_programming │ ├── 01_truth_about_threads │ │ ├── file1.txt │ │ ├── file2.txt │ │ ├── file3.txt │ │ ├── main.py │ │ └── readme.md │ ├── 02_async_walk │ │ ├── main.py │ │ └── readme.md │ ├── 03_the_tower_of_asyncio │ │ └── readme.md │ ├── 04_coroutine │ │ ├── main.py │ │ └── readme.md │ ├── 05_event_loop │ │ ├── main.py │ │ └── readme.md │ ├── 06_async_context_manager │ │ ├── main.py │ │ └── readme.md │ ├── 07_async_iterators │ │ ├── main.py │ │ └── readme.md │ ├── 08_async_generators │ │ ├── main.py │ │ └── readme.md │ ├── 09_async_comprehensions │ │ ├── main.py │ │ └── readme.md │ ├── 10_stating_and_stopping_gracefully │ │ ├── main.py │ │ └── readme.md │ ├── 11_signal │ │ └── readme.md │ └── readme.md └── readme.md ├── 03_data_structures ├── 00_datatype_and_object_in_python │ ├── images │ │ ├── 01.jpg │ │ ├── 02.jpg │ │ └── 03.jpg │ └── readme.md ├── 01_collections_modules │ └── readme.md └── readme.md ├── 04_algorithms ├── 01_introducing_algorithm │ ├── images │ │ └── 01.jpg │ └── readme.md ├── 02_analysis_of_an_algorithm │ ├── images │ │ ├── 01.jpg │ │ ├── 02.jpg │ │ └── 03.jpg │ └── readme.md ├── 03_time_complexity_50_problems │ └── readme.md ├── 04_amortized_analysis.md │ └── readme.md ├── 05_composing_complexity_classes │ └── readme.md ├── 06_alogorithm_design_techniques_and_strategies │ ├── 01_algorithm_design_techniques │ │ └── readme.md │ ├── 02_recursion_algorithm │ │ ├── diagram │ │ │ ├── 01.jpg │ │ │ └── 02.png │ │ └── readme.md │ ├── 03_divide_and_conquer │ │ ├── diagrams │ │ │ ├── 01.jpg │ │ │ ├── 02.jpg │ │ │ ├── 03.jpg │ │ │ └── 04.jpg │ │ ├── main.py │ │ └── readme.md │ ├── 04_dynamic_programming_technique │ │ ├── images │ │ │ ├── 01.jpg │ │ │ ├── 02.jpg │ │ │ └── 03.jpg │ │ └── readme.md │ ├── 05_greedy_algorithm │ │ ├── images │ │ │ ├── 01.jpg │ │ │ ├── 02.jpg │ │ │ ├── 03.jpg │ │ │ ├── 04.jpg │ │ │ ├── 05.jpg │ │ │ ├── 06.jpg │ │ │ ├── 07.jpg │ │ │ ├── 08.jpg │ │ │ ├── 09.jpg │ │ │ └── 10.jpg │ │ └── readme.md │ └── readme.md └── readme.md ├── 05_design_patterns ├── 01_foundation_design_principles │ ├── 01_encapsulate_principle │ │ ├── encapsulate.py │ │ ├── encapsulate_bis.py │ │ └── readme.md │ ├── 02_composition_over_inheritance_principle │ │ ├── composition.py │ │ ├── computer_composition.py │ │ └── readme.md │ ├── 03_interface_not_implemented_principle │ │ ├── interfaces.py │ │ ├── interfaces_bis.py │ │ ├── log.txt │ │ └── readme.md │ ├── 04_loose_coupling_principle │ │ ├── loose_coupling.py │ │ └── readme.md │ ├── 05_uml_diagram │ │ └── readme.md │ └── readme.md ├── 02_solid_principles │ ├── 01_single_responsible_principle │ │ ├── main.py │ │ ├── readme.md │ │ └── report.txt │ ├── 02_open_closed_principle │ │ ├── main.py │ │ └── readme.md │ ├── 03_liskov_substitution_principle │ │ ├── main.py │ │ └── readme.md │ ├── 04_interface_segregation_principle │ │ ├── main.py │ │ └── readme.md │ ├── 05_dependency_inversion_principle │ │ ├── main.py │ │ └── readme.md │ └── readme.md ├── 03_creational_design_patterns │ ├── 01_factory_design_pattern │ │ ├── abstract_factory.py │ │ ├── factory_method.py │ │ ├── main.py │ │ ├── movies.json │ │ ├── person.xml │ │ ├── readme.md │ │ └── simple.md │ ├── 02_builder_design_pattern │ │ ├── code.md │ │ ├── main.py │ │ ├── readme.md │ │ └── simple.md │ ├── 03_prototype_pattern │ │ ├── main.md │ │ ├── main.py │ │ ├── prototype.md │ │ ├── readme.md │ │ ├── setattr.md │ │ └── str_method.md │ └── readme.md └── readme.md ├── 06_python_projects_for_beginners └── readme.md ├── 07_cpython └── readme.md ├── LICENSE ├── README.md └── The-Zen-Of-Python └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | __pycache__/ 4 | migrations/ 5 | *.bak 6 | *.dat 7 | *.dir 8 | .mypy_cache/ 9 | .pytest_cache/ 10 | envs/ 11 | db.sqlite3 12 | readme.txt -------------------------------------------------------------------------------- /01_python_fundamentals/02_lines_and_indentation/main.py: -------------------------------------------------------------------------------- 1 | x = 10 # This is a comment 2 | y = 20 # Python will ignore everything after the hash sign 3 | 4 | 5 | # Using a backslash to continue the statement 6 | total = 1 + 2 + 3 + \ 7 | 4 + 5 + 6 8 | 9 | # Using parentheses to continue the statement 10 | total = (1 + 2 + 3 + 11 | 4 + 5 + 6) 12 | 13 | 14 | text = """This is a long string 15 | that spans multiple lines.""" 16 | 17 | 18 | if x > 0: 19 | print("Positive number") 20 | y = x 21 | else: 22 | print("Non-positive number") 23 | y = 0 24 | 25 | def greet(name): 26 | print(f"Hello, {name}!") # Correct: 4 spaces for indentation 27 | -------------------------------------------------------------------------------- /01_python_fundamentals/03_character_sets/main.py: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | print("こんにちは, 世界!") 3 | -------------------------------------------------------------------------------- /01_python_fundamentals/03_character_sets/readme.md: -------------------------------------------------------------------------------- 1 | # Character Sets in Python 🅰️🅱️🔤 2 | 3 | In this section, we will explore how Python handles different character sets in source files. Understanding character encoding is crucial for writing Python code that works across different languages and platforms. 4 | 5 | ## Unicode and ASCII in Python 🌐 6 | 7 | ### Python v3 8 | 9 | - **Python v3** source files can use **any Unicode character**, encoded as **UTF-8**. 10 | - **UTF-8** is a variable-width character encoding that can represent every character in the Unicode character set. 11 | - **ASCII characters** (with codes between 0 and 127) are encoded in UTF-8 as single bytes, making an ASCII text file perfectly valid as a Python v3 source file. 12 | 13 | ### Python v2 14 | 15 | - **Python v2** source files are typically made up of characters from the **ASCII set**. 16 | - ASCII characters have codes between 0 and 127. 17 | 18 | ## Specifying a Different Encoding 🎨 19 | 20 | - In both Python v2 and v3, you can inform Python that a source file uses a different encoding. 21 | - Python will use this specified encoding to read the file. 22 | 23 | ### Example: 24 | ```python 25 | # coding: utf-8 26 | ``` 27 | 28 | - This comment should be placed at the very beginning of your Python source file (right after the “shebang line” if present). 29 | - The `coding:` directive is also known as an **encoding declaration**. 30 | 31 | ## Using Non-ASCII Characters ✒️ 32 | 33 | - In **Python v2**, non-ASCII characters can only be used in **comments** and **string literals**. 34 | - The **coding directive** allows you to use these characters by specifying the appropriate encoding. 35 | 36 | ### Example: 37 | ```python 38 | # coding: iso-8859-1 39 | # This is a comment with a non-ASCII character: ñ 40 | print("Hola, señor!") 41 | ``` 42 | 43 | ## Best Practices for Encoding 🚀 44 | 45 | - It is considered best practice to use **UTF-8** for all your text files, including Python source files. 46 | - **UTF-8** is widely supported and ensures that your code can handle a wide variety of characters from different languages. 47 | 48 | ### Example: 49 | ```python 50 | # coding: utf-8 51 | print("こんにちは, 世界!") # Prints "Hello, World!" in Japanese 52 | ``` 53 | 54 | --- 55 | This section covered how Python handles character sets and encodings. By understanding and using the correct encoding, you can write Python code that is robust, flexible, and compatible across different systems. 🌍 56 | -------------------------------------------------------------------------------- /01_python_fundamentals/04_tokens/main.py: -------------------------------------------------------------------------------- 1 | # Incorrect: Python would parse 'ifx' as a single identifier 2 | ifx = 10 3 | 4 | # Correct: Adding whitespace to separate 'if' and 'x' 5 | # if x == 10: 6 | # print("x is 10") -------------------------------------------------------------------------------- /01_python_fundamentals/04_tokens/readme.md: -------------------------------------------------------------------------------- 1 | # Tokens in Python 🔠🔍 2 | 3 | In this section, we'll delve into how Python processes each line of code by breaking it down into basic components known as **tokens**. Understanding tokens is essential for grasping how Python interprets your code. 4 | 5 | ## What Are Tokens? 🧩 6 | 7 | - Python breaks each **logical line** into a sequence of elementary lexical components called **tokens**. 8 | - Each token corresponds to a specific substring of the logical line. 9 | 10 | ### Types of Tokens 🏷️ 11 | 12 | The main types of tokens in Python include: 13 | 14 | 1. **Identifiers**: Names used for variables, functions, classes, etc. 15 | 2. **Keywords**: Reserved words that have special meanings in Python (e.g., `if`, `else`, `while`). 16 | 3. **Operators**: Symbols that perform operations on variables and values (e.g., `+`, `-`, `*`, `/`). 17 | 4. **Delimiters**: Characters used to separate tokens (e.g., `()`, `[]`, `{}`, `,`). 18 | 5. **Literals**: Fixed values like numbers, strings, and booleans. 19 | 20 | ## The Role of Whitespace 🧩 21 | 22 | - **Whitespace** (spaces, tabs, and newlines) can be freely used between tokens to separate them. 23 | - Some whitespace is **necessary** to separate logically adjacent identifiers or keywords. 24 | 25 | ### Example: 26 | ```python 27 | # Incorrect: Python would parse 'ifx' as a single identifier 28 | ifx = 10 29 | 30 | # Correct: Adding whitespace to separate 'if' and 'x' 31 | if x == 10: 32 | print("x is 10") 33 | ``` 34 | 35 | - In the above example, without the space between `if` and `x`, Python would mistakenly interpret `ifx` as a single identifier rather than the keyword `if` followed by the identifier `x`. 36 | 37 | --- 38 | 39 | This section covered the basic building blocks of Python code—**tokens**. Understanding how Python breaks down lines into tokens is crucial for writing clear and error-free code. 💡 40 | -------------------------------------------------------------------------------- /01_python_fundamentals/05_identifiers/main.py: -------------------------------------------------------------------------------- 1 | my_variable = 10 # Valid identifier 2 | MyVariable = 20 # Another valid identifier (different from the first due to case sensitivity) 3 | # 1stVariable = 30 # Invalid identifier (cannot start with a digit) 4 | नंबर१ = 5 5 | نمبر۲ = 10 6 | # Perform addition 7 | result = नंबर१ + نمبر۲ 8 | print(result) # In Python v3, Unicode characters classified as digits or combining marks are also allowed. 9 | 10 | -------------------------------------------------------------------------------- /01_python_fundamentals/07_operators/main.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/01_python_fundamentals/07_operators/main.py -------------------------------------------------------------------------------- /01_python_fundamentals/07_operators/practice.md: -------------------------------------------------------------------------------- 1 | # Python Operators Practice Tasks 🎯✨ 2 | 3 | ## Task 1: Simple Calculator 🧮🔢 4 | 5 | **Objective:** Create a simple calculator that takes two numbers as input from the user and performs basic arithmetic operations. 6 | 7 | **Instructions:** 8 | 1. 🖊️ **Prompt** the user to enter two numbers. 9 | 2. ➕➖✖️➗ **Perform** addition, subtraction, multiplication, division, and modulus operations on these numbers. 10 | 3. 📝 **Print** the results of each operation. 11 | 12 | --- 13 | 14 | ## Task 2: Compare Ages 🎂👶👵 15 | 16 | **Objective:** Write a program that compares the ages of two people and shows the results using comparison operators. 17 | 18 | **Instructions:** 19 | 1. 🖊️ **Prompt** the user to enter the ages of two people. 20 | 2. 🔍 **Use** comparison operators to compare the ages (e.g., greater than, less than, equal to). 21 | 3. 📝 **Print** the comparison results. 22 | 23 | --- 24 | 25 | ## Task 3: Even or Odd Checker 🔢🤔 26 | 27 | **Objective:** Create a program that checks whether a given number is even or odd. 28 | 29 | **Instructions:** 30 | 1. 🖊️ **Prompt** the user to enter a number. 31 | 2. 🧮 **Use** the modulus operator to find the remainder when the number is divided by 2. 32 | 3. 📝 **Print** the remainder (0 for even, 1 for odd). 33 | 34 | --- 35 | 36 | ## Task 4: Power Calculation 💪⚡ 37 | 38 | **Objective:** Write a program that calculates the power of a number using exponentiation. 39 | 40 | **Instructions:** 41 | 1. 🖊️ **Prompt** the user to enter a base number and an exponent. 42 | 2. 💥 **Calculate** the result using the exponentiation operator (`**`). 43 | 3. 📝 **Print** the result. 44 | 45 | --- 46 | 47 | ## Task 5: Temperature Converter 🌡️🔥❄️ 48 | 49 | **Objective:** Create a program that converts a temperature from Celsius to Fahrenheit. 50 | 51 | **Instructions:** 52 | 1. 🖊️ **Prompt** the user to enter a temperature in Celsius. 53 | 2. 🔄 **Use** the formula `F = (C * 9/5) + 32` to convert the temperature to Fahrenheit. 54 | 3. 📝 **Print** the result. 55 | 56 | -------------------------------------------------------------------------------- /01_python_fundamentals/07_operators/readme.md: -------------------------------------------------------------------------------- 1 | # Operators in Python ➕➖✖️➗ 2 | 3 | In this section, we'll discuss **operators** in Python, which are symbols or combinations of symbols that perform operations on variables and values. Understanding operators is crucial for working with expressions and performing calculations in Python. 4 | 5 | ## What are Operators? ⚙️ 6 | 7 | - **Operators** are non-alphanumeric characters and combinations of characters that Python uses to perform operations on data. 8 | - Python recognizes a variety of operators, each with its specific functionality. 9 | 10 | ## List of Common Operators in Python 🔢 11 | 12 | Here is a list of the most commonly used operators in Python: 13 | 14 | ### Arithmetic Operators ➕➖✖️➗ 15 | 16 | - `+` : Addition 17 | - `-` : Subtraction 18 | - `*` : Multiplication 19 | - `/` : Division 20 | - `%` : Modulus (remainder after division) 21 | - `**` : Exponentiation (power) 22 | - `//` : Floor division (division that results in the largest whole number less than or equal to the result) 23 | 24 | ### Example: 25 | ```python 26 | a = 10 27 | b = 3 28 | 29 | print(a + b) # 13 30 | print(a - b) # 7 31 | print(a * b) # 30 32 | print(a / b) # 3.3333... 33 | print(a % b) # 1 34 | print(a ** b) # 1000 35 | print(a // b) # 3 36 | ``` 37 | ### Comparison Operators 🔍 38 | 39 | - `<` : Less than 40 | - `<=` : Less than or equal to 41 | - `>` : Greater than 42 | - `>=` : Greater than or equal to 43 | - `==` : Equal to 44 | - `!=` : Not equal to 45 | 46 | ### Example: 47 | ```python 48 | a = 5 49 | b = 3 50 | 51 | print(a < b) # False 52 | print(a <= b) # False 53 | print(a > b) # True 54 | print(a >= b) # True 55 | print(a == b) # False 56 | print(a != b) # True 57 | ``` 58 | 59 | 60 | ## Summary 📝 61 | 62 | Python provides a wide range of operators that you can use for arithmetic calculations, bitwise operations, comparisons, and more. In Python v3, the `@` operator was introduced for matrix multiplication, expanding the capabilities of Python in handling mathematical operations efficiently. 63 | -------------------------------------------------------------------------------- /01_python_fundamentals/07_operators/solution.md: -------------------------------------------------------------------------------- 1 | # Solutions: Python Operators Practice Tasks 🎯✨ 2 | 3 | ## Task 1: Simple Calculator 🧮🔢 4 | 5 | **Solution:** 6 | ```python 7 | # Simple Calculator 8 | 9 | # Prompt the user to enter two numbers 10 | a = int(input("Enter first number: ")) 11 | b = int(input("Enter second number: ")) 12 | 13 | # Perform arithmetic operations 14 | addition = a + b 15 | subtraction = a - b 16 | multiplication = a * b 17 | division = a / b 18 | modulus = a % b 19 | 20 | # Print the results using f-strings 21 | print(f"Addition: {addition}") 22 | print(f"Subtraction: {subtraction}") 23 | print(f"Multiplication: {multiplication}") 24 | print(f"Division: {division}") 25 | print(f"Modulus: {modulus}") 26 | ``` 27 | 28 | --- 29 | 30 | ## Task 2: Compare Ages 🎂👶👵 31 | 32 | **Solution:** 33 | ```python 34 | # Compare Ages 35 | 36 | # Prompt the user to enter the ages of two people 37 | age1 = int(input("Enter age of person 1: ")) 38 | age2 = int(input("Enter age of person 2: ")) 39 | 40 | # Perform age comparisons 41 | result_greater = age1 > age2 42 | result_lesser = age1 < age2 43 | result_equal = age1 == age2 44 | 45 | # Print the comparison results using f-strings 46 | print(f"Is person 1 older than person 2? {result_greater}") 47 | print(f"Is person 1 younger than person 2? {result_lesser}") 48 | print(f"Are both persons the same age? {result_equal}") 49 | ``` 50 | 51 | --- 52 | 53 | ## Task 3: Even or Odd Checker 🔢🤔 54 | 55 | **Solution:** 56 | ```python 57 | # Even or Odd Checker 58 | 59 | # Prompt the user to enter a number 60 | number = int(input("Enter a number: ")) 61 | 62 | # Calculate modulus to check even or odd 63 | remainder = number % 2 64 | 65 | # Print the remainder using f-strings (0 indicates even, 1 indicates odd) 66 | print(f"Remainder when divided by 2: {remainder}") 67 | ``` 68 | 69 | --- 70 | 71 | ## Task 4: Power Calculation 💪⚡ 72 | 73 | **Solution:** 74 | ```python 75 | # Power Calculation 76 | 77 | # Prompt the user to enter a base number and an exponent 78 | base = int(input("Enter base number: ")) 79 | exponent = int(input("Enter exponent: ")) 80 | 81 | # Calculate the power 82 | result = base ** exponent 83 | 84 | # Print the result using f-strings 85 | print(f"{base} raised to the power of {exponent} is {result}") 86 | ``` 87 | 88 | --- 89 | 90 | ## Task 5: Temperature Converter 🌡️🔥❄️ 91 | 92 | **Solution:** 93 | ```python 94 | # Temperature Converter 95 | 96 | # Prompt the user to enter a temperature in Celsius 97 | celsius = float(input("Enter temperature in Celsius: ")) 98 | 99 | # Convert Celsius to Fahrenheit 100 | fahrenheit = (celsius * 9/5) + 32 101 | 102 | # Print the result using f-strings 103 | print(f"{celsius}°C is {fahrenheit}°F") 104 | ``` -------------------------------------------------------------------------------- /01_python_fundamentals/08_delimiters/main.py: -------------------------------------------------------------------------------- 1 | # This is a comment 2 | 3 | text = "Hello, World!" # Double quotes for string 4 | path = 'C:\\Users\\Hashim\\Documents' # Escape character in string -------------------------------------------------------------------------------- /01_python_fundamentals/09_literals/main.py: -------------------------------------------------------------------------------- 1 | 42 # Integer literal 2 | 3.14 # Floating-point literal 3 | 1.0j # Imaginary literal 4 | 5 | 6 | 'hello' # Single-quoted string literal 7 | "world" # Double-quoted string literal 8 | """Good 9 | night""" # Triple-quoted string literal (spans multiple lines) 10 | 11 | 12 | [42, 3.14, 'hello'] # List 13 | [] # Empty list 14 | 100, 200, 300 # Tuple 15 | () # Empty tuple 16 | {'x':42, 'y':3.14} # Dictionary 17 | {} # Empty dictionary 18 | {1, 2, 4, 8, 'string'} # Set 19 | 20 | 21 | empty_set = set() # Correct way to create an empty set -------------------------------------------------------------------------------- /01_python_fundamentals/10_data_types/main.py: -------------------------------------------------------------------------------- 1 | x = 42 # x is an object of type 'int' 2 | y = "Hello" # y is an object of type 'str' 3 | 4 | 5 | x = 42 # Immutable integer object 6 | y = "Hello" # Immutable string object 7 | 8 | x = x + 1 # x now points to a new integer object 43 9 | y += " World" # y now points to a new string object "Hello World" 10 | 11 | 12 | x = 42 13 | print(type(x)) # Output: 14 | print(isinstance(x, int)) # Output: True 15 | 16 | 17 | 18 | x = 42 # Original immutable integer object 19 | original_x = x # Storing the original value in a different variable 20 | 21 | x = x + 1 # Now x points to a new integer object 43 22 | 23 | print("Current x:", x) # Output: Current x: 43 24 | print("Original x:", original_x) # Output: Original x: 42 25 | 26 | 27 | 28 | # Decimal integer literals 29 | print(1, 23, 3493) 30 | 31 | # Binary integer literals 32 | print(0b010101, 0b110010) 33 | 34 | # Octal integer literals 35 | print(0o1, 0o27, 0o6645) 36 | 37 | # Hexadecimal integer literals 38 | print(0x1, 0x17, 0xDA5) 39 | 40 | 41 | # Examples of floating-point literals 42 | print(0., 0.0, .0, 1., 1.0, 1e0, 1.e0, 1.0e0) 43 | # Output: 0.0 0.0 0.0 1.0 1.0 1.0 1.0 1.0 44 | 45 | 46 | # Examples of imaginary literals 47 | print(0j, 0.j, 0.0j, .0j, 1j, 1.j, 1.0j, 1e0j, 1.e0j, 1.0e0j) 48 | 49 | # Complex numbers 50 | print(1+0j, 1.0+0.0j) 51 | 52 | 53 | print(100_000.000_0001, 0x_FF_FF, 0o7_777, 0b_1010_1010) 54 | # Outputs: (100000.0000001, 65535, 4095, 170) 55 | 56 | 57 | print('hello') # Single-quoted string 58 | print("world") # Double-quoted string 59 | print("""Good 60 | night""") # Triple-quoted string, spans multiple lines 61 | 62 | 63 | 64 | is_python_fun = True 65 | print(type(is_python_fun)) # Output: 66 | print(is_python_fun and False) # Output: False 67 | print(not is_python_fun) # Output: False 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /01_python_fundamentals/11_variables_as_references/main.py: -------------------------------------------------------------------------------- 1 | x = 42 # x is a reference to an integer object 2 | x = "hello" # x is now a reference to a string object 3 | 4 | x = 10 # Binding: x is now a reference to the integer 10 5 | x = 20 # Rebinding: x is now a reference to the integer 20 6 | 7 | x = 10 # x is bound to the integer 10 8 | del x # x is unbound, and the name x no longer exists 9 | 10 | x = [1, 2, 3] # x is bound to a list object 11 | y = x # y is also bound to the same list object 12 | del x # x is unbound, but the list object still exists because y refers to 13 | 14 | global_var = "I am global" # Global variable 15 | 16 | def my_function(): 17 | local_var = "I am local" # Local variable 18 | print(local_var) 19 | 20 | my_function() # Outputs: I am local 21 | print(global_var) # Outputs: I am global -------------------------------------------------------------------------------- /01_python_fundamentals/12_decisions_statements/exercise.md: -------------------------------------------------------------------------------- 1 | # 📝 Create these Exercise 2 | 3 | ## 1. **Login System Verification** 4 | 5 | **Objective**: Create a simple login system that checks if the entered username and password match predefined values. 6 | 7 | **Steps**: 8 | 1. Prompt the user to input a username. 9 | 2. Prompt the user to input a password. 10 | 3. If the entered username and password match the predefined values, print a "Login successful!" message. 11 | 4. If either the username or password is incorrect, print an "Invalid credentials" message. 12 | 13 | ## 2. **Temperature Advisor** 14 | 15 | **Objective**: Write a program that suggests actions based on the current temperature. 16 | 17 | **Steps**: 18 | 1. Ask the user to input the current temperature. 19 | 2. If the temperature is above 30°C, suggest staying hydrated. 20 | 3. If the temperature is between 20°C and 30°C, suggest it's a nice day for a walk. 21 | 4. If the temperature is below 20°C, suggest wearing a jacket. 22 | 23 | ## 3. **Simple Tax Calculator** 24 | 25 | **Objective**: Calculate the tax rate based on the user's income. 26 | 27 | **Steps**: 28 | 1. Ask the user to input their annual income. 29 | 2. If the income is less than 50,000, the tax rate is 10%. 30 | 3. If the income is between 50,000 and 100,000, the tax rate is 20%. 31 | 4. If the income is above 100,000, the tax rate is 30%. 32 | 5. Display the calculated tax amount. 33 | 34 | ## 4. **Day of the Week Checker** 35 | 36 | **Objective**: Determine if the day entered by the user is a weekday or a weekend. 37 | 38 | **Steps**: 39 | 1. Ask the user to input a day of the week (e.g., "Monday"). 40 | 2. If the day is "Saturday" or "Sunday," print "It's a weekend!" 41 | 3. If the day is any other day, print "It's a weekday." 42 | 43 | ## 5. **Driving Eligibility Checker** 44 | 45 | **Objective**: Check if a person is eligible to get a driving license based on their age. 46 | 47 | **Steps**: 48 | 1. Ask the user to input their age. 49 | 2. If the age is below 16, print "You are too young to get a driving license." 50 | 3. If the age is between 16 and 18, print "You need parental consent to get a driving license." 51 | 4. If the age is 18 or older, print "You are eligible to get a driving license." 52 | 53 | -------------------------------------------------------------------------------- /01_python_fundamentals/12_decisions_statements/main.py: -------------------------------------------------------------------------------- 1 | age = 19 2 | if age >= 18: 3 | print("You are old enough to vote!") 4 | 5 | 6 | my_age = 17 7 | 8 | if my_age >= 18: 9 | print("You are old enough to vote!") 10 | else: 11 | print("Sorry, you are too young to vote.") 12 | 13 | 14 | 15 | our_age = 12 16 | 17 | if our_age < 4: 18 | print("Your admission cost is $0.") 19 | elif our_age < 18: 20 | print("Your admission cost is $25.") 21 | else: 22 | print("Your admission cost is $40.") 23 | 24 | 25 | 26 | marks = 85 27 | 28 | if marks >= 90: 29 | print("You got an A grade! 🎉") 30 | elif marks >= 75 and marks < 90: 31 | print("You got a B grade! 👍") 32 | elif marks >= 60 and marks < 75: 33 | print("You got a C grade! 👌") 34 | else: 35 | print("You need to improve. 📚") 36 | 37 | 38 | 39 | 40 | mood = "energetic" 41 | 42 | if mood == "happy": 43 | print("How about listening to some pop music? 🎤") 44 | elif mood == "sad": 45 | print("Try some blues to feel those emotions! 🎷") 46 | elif mood == "energetic": 47 | print("Rock music is your go-to! 🎸") 48 | elif mood == "relaxed": 49 | print("Smooth jazz will be perfect for you. 🎹") 50 | else: 51 | print("Discover some new indie tracks! 🎧") -------------------------------------------------------------------------------- /01_python_fundamentals/12_decisions_statements/solutions.md: -------------------------------------------------------------------------------- 1 | # 🔧 Solutions 2 | 3 | ## 1. **Login System Verification** 4 | 5 | ```python 6 | # Predefined username and password 7 | correct_username = "admin" 8 | correct_password = "password123" 9 | 10 | # User input 11 | username = input("Enter your username: ") 12 | password = input("Enter your password: ") 13 | 14 | # Check if the input matches the predefined values 15 | if username == correct_username and password == correct_password: 16 | print("✨ Login successful! Welcome back! ✨") 17 | print("You have access to all the features.") 18 | print(f'Welcome, {username}!') 19 | else: 20 | print("❌ Invalid credentials. Please try again.") 21 | ``` 22 | 23 | ## 2. **Temperature Advisor** 24 | 25 | ```python 26 | # User input for temperature 27 | temperature = int(input("Enter the current temperature in °C: ")) 28 | 29 | # Temperature check and suggestions 30 | if temperature > 30: 31 | print("It's quite hot! Make sure to stay hydrated. 💧") 32 | elif 20 <= temperature <= 30: 33 | print("It's a pleasant day! How about a walk in the park? 🚶‍♂️") 34 | else: 35 | print("It's a bit chilly. Don't forget your jacket! 🧥") 36 | ``` 37 | 38 | ## 3. **Simple Tax Calculator** 39 | 40 | ```python 41 | # User input for income 42 | income = float(input("Enter your annual income: ")) 43 | 44 | # Tax calculation based on income 45 | if income < 50000: 46 | tax = income * 0.10 47 | print(f"Your tax is 10%, which amounts to ${tax:.2f}.") 48 | elif 50000 <= income <= 100000: 49 | tax = income * 0.20 50 | print(f"Your tax is 20%, which amounts to ${tax:.2f}.") 51 | else: 52 | tax = income * 0.30 53 | print(f"Your tax is 30%, which amounts to ${tax:.2f}.") 54 | ``` 55 | 56 | ## 4. **Day of the Week Checker** 57 | 58 | ```python 59 | # User input for the day of the week 60 | day = input("Enter a day of the week: ").capitalize() 61 | 62 | # Check if the day is a weekday or weekend 63 | if day == "Saturday" or day == "Sunday": 64 | print("It's a weekend! Enjoy your time off! 🎉") 65 | elif day in ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]: 66 | print("It's a weekday. Back to work! 💼") 67 | else: 68 | print("Invalid input. Please enter a valid day of the week.") 69 | ``` 70 | 71 | ## 5. **Driving Eligibility Checker** 72 | 73 | ```python 74 | # User input for age 75 | age = int(input("Enter your age: ")) 76 | 77 | # Eligibility check based on age 78 | if age < 16: 79 | print("You are too young to get a driving license. 🚫") 80 | elif 16 <= age < 18: 81 | print("You need parental consent to get a driving license. 📝") 82 | else: 83 | print("You are eligible to get a driving license. 🚗") 84 | ``` 85 | 86 | These exercises cover various real-world scenarios where `if`, `if-else`, and `if-elif` statements can be effectively used. Let me know if you'd like more exercises or any additional details! 😊 -------------------------------------------------------------------------------- /01_python_fundamentals/13_iterations/exercise.md: -------------------------------------------------------------------------------- 1 | # 📝 Loop-Focused Exercise Statements 2 | 3 | 1. **Rental Car Availability**: Write a program that repeatedly asks the user what kind of rental car they would like. If the user types "exit," the program should end. Otherwise, it should print a message such as "Let me see if I can find you a [car]." The program should keep running until the user decides to exit. 4 | 5 | 2. **Restaurant Seating Check**: Write a program that repeatedly asks the user how many people are in their dinner group. If the answer is more than eight, print a message saying they’ll have to wait for a table. Otherwise, report that their table is ready. The program should continue asking until the user types "done." 6 | 7 | 3. **Check Multiples of Ten Continuously**: Write a program that continuously asks the user for a number and then reports whether the number is a multiple of 10 or not. The program should stop if the user types "stop." 8 | 9 | 4. **Password Validation Loop**: Write a program that prompts the user to enter a password and checks if it meets a specific condition (e.g., length of at least 8 characters). If it doesn’t, the program should ask the user to input the password again. This should continue until the user enters a valid password. 10 | 11 | 5. **Number Pyramid Pattern**: Write a program that generates a number pyramid pattern based on the number of levels specified by the user. The program should use nested loops to generate the pattern. 12 | 13 | 6. **Guess the Number Game**: Write a loop-based program that generates a random number between 1 and 10. The user has to guess the number, and the program should provide feedback ("too high" or "too low") until the user guesses the correct number. The game should continue asking until the correct number is guessed. 14 | 15 | 7. **Factorial Calculator Using a Loop**: Write a program that repeatedly asks the user to input a number and then calculates the factorial of that number using a loop. The program should continue asking until the user types "exit." 16 | 17 | 8. **Fibonacci Sequence Generator**: Write a program that generates the Fibonacci sequence up to a certain number entered by the user. Use a loop to generate the sequence until the user decides to stop. 18 | -------------------------------------------------------------------------------- /01_python_fundamentals/13_iterations/main.py: -------------------------------------------------------------------------------- 1 | no = int(input("Please Enter Table no: ")) 2 | i = 1 3 | print(f'{no} x {i} = {no * i}') 4 | i += 1 5 | print(f'{no} x {i} = {no * i}') 6 | i += 1 7 | print(f'{no} x {i} = {no * i}') 8 | i += 1 9 | print(f'{no} x {i} = {no * i}') 10 | i += 1 11 | -------------------------------------------------------------------------------- /01_python_fundamentals/14_strings/function.md: -------------------------------------------------------------------------------- 1 | # 🌟 Simple uppercase Function 2 | ```python 3 | def to_uppercase(input_string): 4 | uppercase_string = "" # 🌟 Initialize an empty string to store the result 5 | 6 | # 🔄 Iterate through each character in the input string 7 | for char in input_string: 8 | # 🧐 Check if the character is a lowercase letter 9 | if 'a' <= char <= 'z': 10 | # 🔠 Convert the lowercase letter to uppercase by subtracting 32 from its ASCII value 11 | uppercase_char = chr(ord(char) - 32) 12 | # ➕ Append the converted character to the result string 13 | uppercase_string += uppercase_char 14 | else: 15 | # ➕ If the character is not a lowercase letter, add it as is 16 | uppercase_string += char 17 | 18 | return uppercase_string # 🚀 Return the final uppercase string 19 | 20 | # 📝 Example usage 21 | example_str = "hello world!" 22 | result = to_uppercase(example_str) 23 | print(result) # Output: HELLO WORLD! 🌟 24 | ``` 25 | 26 | ### Explanation with Emojis: 27 | 28 | 1. **🌟 Initialize an Empty String:** 29 | - We start by creating an empty string `uppercase_string` to store the final result. 30 | 31 | 2. **🔄 Iterate Through Each Character:** 32 | - The function iterates through each character in the `input_string` using a `for` loop. 33 | 34 | 3. **🧐 Check if Character is Lowercase:** 35 | - We check if the character is a lowercase letter by comparing its ASCII value. 36 | 37 | 4. **🔠 Convert Lowercase to Uppercase:** 38 | - If the character is a lowercase letter, we convert it to its uppercase equivalent by subtracting 32 from its ASCII value. 39 | 40 | 5. **➕ Append to Result String:** 41 | - The converted uppercase character is appended to `uppercase_string`. 42 | 43 | 6. **➕ Handle Non-Lowercase Characters:** 44 | - If the character is not a lowercase letter, it is directly appended to `uppercase_string`. 45 | 46 | 7. **🚀 Return the Uppercase String:** 47 | - Finally, the function returns the `uppercase_string` containing all the uppercase characters. -------------------------------------------------------------------------------- /01_python_fundamentals/17_set/exercise.md: -------------------------------------------------------------------------------- 1 | # 📋 Exercises: 2 | 3 | 1. **Remove Duplicates**: Given a list of numbers, remove all duplicates using a set. 4 | 2. **Set Union**: Create two sets of integers and find the union of the sets. 5 | 3. **Set Intersection**: Given two sets of strings, find their intersection. 6 | 4. **Set Difference**: Create two sets of integers and find the difference between the first set and the second set. 7 | 5. **Symmetric Difference**: Given two sets, find the symmetric difference between them. 8 | 6. **Check Subset**: Given two sets, check if one set is a subset of the other. 9 | 7. **Set Membership**: Write a program to check if an element exists in a set. 10 | 8. **Set Update**: Create a set and add multiple items to it using the `update()` method. 11 | 9. **Frozen Set**: Create a frozen set and try to add or remove elements from it. 12 | 10. **Remove Common Elements**: Given two sets, remove all elements from the first set that are also present in the second set. 13 | 14 | -------------------------------------------------------------------------------- /01_python_fundamentals/17_set/solutions.md: -------------------------------------------------------------------------------- 1 | # 🚀 Solutions: 2 | 3 | ## 1. **Remove Duplicates** 4 | 5 | ```python 6 | # 🌟 Remove Duplicates from a List Using a Set 7 | numbers = [1, 2, 2, 3, 4, 4, 5, 5, 5, 6] 8 | unique_numbers = set(numbers) 9 | print(unique_numbers) # Output: {1, 2, 3, 4, 5, 6} 10 | ``` 11 | 12 | ## 2. **Set Union** 13 | 14 | ```python 15 | # 🔗 Union of Two Sets 16 | set1 = {1, 2, 3} 17 | set2 = {3, 4, 5} 18 | union_set = set1.union(set2) 19 | print(union_set) # Output: {1, 2, 3, 4, 5} 20 | ``` 21 | 22 | ## 3. **Set Intersection** 23 | 24 | ```python 25 | # ⛓️ Intersection of Two Sets 26 | set1 = {"apple", "banana", "cherry"} 27 | set2 = {"banana", "cherry", "date"} 28 | intersection_set = set1.intersection(set2) 29 | print(intersection_set) # Output: {'banana', 'cherry'} 30 | ``` 31 | 32 | ## 4. **Set Difference** 33 | 34 | ```python 35 | # ➖ Difference Between Two Sets 36 | set1 = {1, 2, 3, 4, 5} 37 | set2 = {4, 5, 6, 7, 8} 38 | difference_set = set1.difference(set2) 39 | print(difference_set) # Output: {1, 2, 3} 40 | ``` 41 | 42 | ## 5. **Symmetric Difference** 43 | 44 | ```python 45 | # 🔄 Symmetric Difference Between Two Sets 46 | set1 = {1, 2, 3, 4} 47 | set2 = {3, 4, 5, 6} 48 | symmetric_diff_set = set1.symmetric_difference(set2) 49 | print(symmetric_diff_set) # Output: {1, 2, 5, 6} 50 | ``` 51 | 52 | ## 6. **Check Subset** 53 | 54 | ```python 55 | # ✅ Check If One Set is a Subset of Another 56 | set1 = {1, 2, 3} 57 | set2 = {1, 2, 3, 4, 5} 58 | is_subset = set1.issubset(set2) 59 | print(is_subset) # Output: True 60 | ``` 61 | 62 | ## 7. **Set Membership** 63 | 64 | ```python 65 | # 🔍 Check If an Element Exists in a Set 66 | fruits = {"apple", "banana", "cherry"} 67 | print("banana" in fruits) # Output: True 68 | print("orange" in fruits) # Output: False 69 | ``` 70 | 71 | ## 8. **Set Update** 72 | 73 | ```python 74 | # 🔄 Adding Multiple Items to a Set Using update() 75 | fruits = {"apple", "banana"} 76 | fruits.update(["cherry", "date", "elderberry"]) 77 | print(fruits) # Output: {'apple', 'banana', 'cherry', 'date', 'elderberry'} 78 | ``` 79 | 80 | ## 9. **Frozen Set** 81 | 82 | ```python 83 | # 🧊 Create a Frozen Set and Attempt Modifications 84 | frozen_fruits = frozenset(["apple", "banana", "cherry"]) 85 | print(frozen_fruits) # Output: frozenset({'apple', 'banana', 'cherry'}) 86 | 87 | # Attempting to add or remove elements will raise an AttributeError 88 | # frozen_fruits.add("date") # Uncommenting this line will raise an AttributeError 89 | ``` 90 | 91 | ## 10. **Remove Common Elements** 92 | 93 | ```python 94 | # 🗑️ Remove All Elements from Set1 that are Present in Set2 95 | set1 = {1, 2, 3, 4, 5} 96 | set2 = {3, 4, 5, 6, 7} 97 | set1.difference_update(set2) 98 | print(set1) # Output: {1, 2} 99 | ``` 100 | -------------------------------------------------------------------------------- /01_python_fundamentals/18_dict/exercise.md: -------------------------------------------------------------------------------- 1 | # 🎉Exercises 2 | 3 | 1. **Build a Custom User Profile**: 4 | Create a function that accepts a dictionary of user data and returns a formatted user profile using unpacking. 5 | 6 | 2. **Combine and Filter Dictionaries**: 7 | Write a program that merges two dictionaries and filters out key-value pairs based on a specific condition. 8 | 9 | 3. **Create a Frequency Dictionary**: 10 | Use dictionary comprehensions to count the frequency of each word in a given sentence. 11 | 12 | 4. **Nested Dictionary Manipulation**: 13 | Use dictionary comprehensions to flatten a nested dictionary structure. 14 | 15 | 5. **Advanced Dictionary Unpacking**: 16 | Create a function that merges multiple dictionaries using dictionary unpacking and handles conflicting keys by summing the values. 17 | 18 | -------------------------------------------------------------------------------- /01_python_fundamentals/18_dict/main.py: -------------------------------------------------------------------------------- 1 | alien_0 = {'color': 'green', 'points': 5} 2 | print(alien_0['color']) 3 | print(alien_0['points']) 4 | 5 | alien_0 = {'color': 'green', 'points': 5} 6 | alien_0['x_position'] = 0 7 | alien_0['y_position'] = 25 8 | print(alien_0) 9 | 10 | 11 | alien_0 = {'color': 'green'} 12 | print(f"The alien is {alien_0['color']}.") 13 | alien_0['color'] = 'yellow' 14 | print(f"The alien is now {alien_0['color']}.") 15 | 16 | alien_0 = {'color': 'green', 'points': 5} 17 | del alien_0['points'] 18 | print(alien_0) 19 | 20 | 21 | alien_0 = {'color': 'green', 'speed': 'slow'} 22 | print(alien_0.get('points', 'No point value assigned.')) 23 | 24 | 25 | user_0 = {'username': 'efermi', 'first': 'enrico', 'last': 'fermi'} 26 | for key, value in user_0.items(): 27 | print(f"Key: {key}") 28 | print(f"Value: {value}") 29 | 30 | 31 | favorite_languages = {'jen': 'python', 'sarah': 'c', 'edward': 'rust'} 32 | for name in favorite_languages.keys(): 33 | print(name.title()) 34 | 35 | 36 | print("The following languages have been mentioned:") 37 | for language in favorite_languages.values(): 38 | print(language.title()) 39 | 40 | 41 | for name in sorted(favorite_languages.keys()): 42 | print(f"{name.title()}, thank you for taking the poll.") 43 | 44 | def greet_user(name, age): 45 | print(f"Hello, {name}! You are {age} years old.") 46 | 47 | user_info = {'name': 'Alice', 'age': 30} 48 | 49 | # Unpacking the dictionary into the function call 50 | greet_user(**user_info) 51 | 52 | dict1 = {'a': 1, 'b': 2} 53 | dict2 = {'c': 3, 'd': 4} 54 | 55 | merged_dict = {**dict1, **dict2} 56 | print(merged_dict) 57 | 58 | original = {'x': 1, 'y': 2} 59 | new_dict = {**original, 'z': 3} 60 | print(new_dict) 61 | 62 | numbers = [1, 2, 3, 4, 5] 63 | squares = {num: num ** 2 for num in numbers} 64 | print(squares) 65 | 66 | cubes = {num: num ** 3 for num in numbers if num > 2} 67 | print(cubes) 68 | 69 | 70 | -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.1.py: -------------------------------------------------------------------------------- 1 | import re 2 | text_to_match = """The punctuation characters in the ASCII table are: !"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~.""" 3 | pattern = r"The punctuation characters in the ASCII table are: !\"#\$%&'\(\)\*\+,-\./:;<=>\?@\[\\\]\^_`\{\|}~\." 4 | if re.fullmatch(pattern, text_to_match): 5 | print("The text matches the pattern exactly! ✅") 6 | else: 7 | print("The text does not match the pattern. ❌") 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.2.py: -------------------------------------------------------------------------------- 1 | import re 2 | nonprintable_string = "\a\x1B\f\n\r\t\v" 3 | pattern = r"\a\x1B\f\n\r\t\v" 4 | if re.fullmatch(pattern, nonprintable_string): 5 | print("The text matches the pattern exactly! ✅") 6 | else: 7 | print("The text does not match the pattern. ❌") 8 | -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.3.py: -------------------------------------------------------------------------------- 1 | import re 2 | calendar_regex = r'c[ae]l[ae]nd[ae]r' 3 | text = "calender, calandar, celendar, celandar, cielendar, cialendar cAlandAr" 4 | matches = re.findall(calendar_regex, text) 5 | print(matches) # Output: ['calender', 'calandar', 'celendar', 'celandar'] 6 | 7 | 8 | # Regex to match a single hexadecimal character 9 | hex_char_regex = r'[a-fA-F0-9]' 10 | # Example usage 11 | text = "1, A, g, 3F, Z" 12 | matches = re.findall(hex_char_regex, text) 13 | print(matches) # Output: ['1', 'A', '3', 'F'] 14 | 15 | 16 | # Regex to match a single non-hexadecimal character 17 | non_hex_char_regex = r'[^a-fA-F0-9]' 18 | # Example usage 19 | text = "1, A, g, 3F, Z, !" 20 | matches = re.findall(non_hex_char_regex, text) 21 | print(matches) # Output: [',', ' ', ',', ' ', 'g', ',', ' ', ',', ' ', 'Z', ',', ' ', '!'] 22 | 23 | 24 | # Case insensitive match for hexadecimal characters 25 | case_insensitive_hex_regex = r'(?i)[a-f0-9]' 26 | # Example usage 27 | text = "A, b, C, d, 3, F" 28 | matches = re.findall(case_insensitive_hex_regex, text) 29 | print(matches) # Output: ['A', 'b', 'C', 'd', '3', 'F'] -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.4.py: -------------------------------------------------------------------------------- 1 | import re 2 | regex_any_char_except_linebreaks = r'.' 3 | text = "Hello! How are \nyou?" 4 | matches = re.findall(regex_any_char_except_linebreaks, text) 5 | print(matches) # Output: ['H', 'e', 'l', 'l', 'o', '!', ' ', 'H', 'o', 'w', ' ', 'a', 'r', 'e', ' ', 'y', 'o', 'u', '?'] 6 | 7 | 8 | regex_any_char_including_linebreaks = r'[\s\S]' 9 | text = "Hello!\nHow are you?" 10 | matches = re.findall(regex_any_char_including_linebreaks, text) 11 | print(matches) # Output: ['H', 'e', 'l', 'l', 'o', '!', '\n', 'H', 'o', 'w', ' ', 'a', 'r', 'e', ' ', 'y', 'o', 'u', '?'] 12 | 13 | 14 | # Regex with mode modifier to match any character, including line breaks 15 | regex_with_mode_modifier = r'(?s).' 16 | # Example usage 17 | text = "Hello!\nHow are you?" 18 | matches = re.findall(regex_with_mode_modifier, text) 19 | print(matches) # Output: ['H', 'e', 'l', 'l', 'o', '!', '\n', 'H', 'o', 'w', ' ', 'a', 'r', 'e', ' ', 'y', 'o', 'u', '?'] -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.5.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Regex to match "alpha" at the start of the text 4 | regex_start_alpha = r'^\balpha\b' 5 | 6 | # Example usage 7 | text1 = "alpha is at the beginning. Not in between." 8 | text2 = "Is alpha at the beginning. Not in between." 9 | matche1 = re.findall(regex_start_alpha, text1) 10 | matche2 = re.findall(regex_start_alpha, text2) 11 | print(matche1) # Output: ['alpha'] 12 | print(matche2) # Output: No match 13 | 14 | 15 | # Regex to match "omega" at the end of the text 16 | regex_end_omega = r'\bomega\b$' 17 | # Example usage 18 | text1 = "Not in between, but omega is at the end." 19 | text2 = "Not in between, but is at the end omega" 20 | matche1 = re.findall(regex_end_omega, text1) 21 | matche2 = re.findall(regex_end_omega, text2) 22 | print(matche1) # Output: no match 23 | print(matche2) # Output: no ['omega'] 24 | 25 | 26 | # Regex to match "begin" at the start of a line 27 | regex_line_start_begin = r'^begin' 28 | # Example usage 29 | text1 = "Not in between.\nbegin is at the start of a line." 30 | text2 = "Not in between.\n begin is at the start of a line." 31 | matche1 = re.findall(regex_line_start_begin, text1, re.MULTILINE) 32 | matche2 = re.findall(regex_line_start_begin, text2, re.MULTILINE) 33 | print(matche1) # Output: ['begin'] 34 | print(matche2) # Output: no match 35 | 36 | regex_line_end_end = r'end$' 37 | 38 | # Example usage 39 | text1 = "Not in between.\nBut this line ends with end" 40 | text2 = "Not in between.\nBut this line ends end with" 41 | matche1 = re.findall(regex_line_end_end, text1, re.MULTILINE) 42 | matche2 = re.findall(regex_line_end_end, text2, re.MULTILINE) 43 | print(matche1) # Output: ['end'] 44 | print(matche2) # Output: no match -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.6.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Regex to match "cat" as a whole word 4 | regex_whole_word_cat = r'\bcat\b' 5 | 6 | # Example usage 7 | text = "My cat is brown. A category is different from a cat." 8 | matches = re.findall(regex_whole_word_cat, text) 9 | print(matches) # Output: ['cat', 'cat'] 10 | 11 | 12 | 13 | # Regex to match "cat" only if it is part of another word 14 | regex_non_whole_word_cat = r'\Bcat\B' 15 | 16 | # Example usage 17 | text = "My cat is brown. staccato is a word, and so is bobcat." 18 | matches = re.findall(regex_non_whole_word_cat, text) 19 | print(matches) # Output: ['cat', 'cat'] -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.7.py: -------------------------------------------------------------------------------- 1 | import re as r 2 | import regex as re 3 | # Regex to find the trademark sign (™) 4 | regex_trademark = r'\u2122' 5 | 6 | text = "This is a trademark symbol ™ and here is another one: ™." 7 | matches = r.findall(regex_trademark, text) 8 | print(matches) # Output: ['™', '™'] 9 | 10 | regex_currency = r'\p{Sc}' 11 | text = "The price is $100 or €85 or ¥1000." 12 | matches = re.findall(regex_currency, text, re.UNICODE) 13 | print(matches) # Output: ['$', '€', '¥'] 14 | 15 | # Regex to match any character in the "Greek Extended" block 16 | regex_greek_extended = r'[\u1F00-\u1FFF]' 17 | 18 | text = "Greek letters: ἀ ἁ ἂ ἃ." 19 | matches = r.findall(regex_greek_extended, text) 20 | print(matches) # Output: ['ἀ', 'ἁ', 'ἂ', 'ἃ'] 21 | 22 | # Regex to match any character in the "Greek" script 23 | regex_greek_script = r'[\u0370-\u03FF]' 24 | 25 | text = "Greek letters: Α, Β, Γ, Δ, ε, ζ." 26 | matches = r.findall(regex_greek_script, text) 27 | print(matches) # Output: ['Α', 'Β', 'Γ', 'Δ', 'ε', 'ζ'] 28 | 29 | # Regex to match a grapheme (base character + combining marks) 30 | regex_grapheme = r'(\P{M}\p{M}*)' 31 | 32 | text = "à is a grapheme." 33 | matches = re.findall(regex_grapheme, text) 34 | print(matches) # Output: ['à', ' ', 'i', 's', ' ', 'a', ' ', 'g', 'r', 'a', 'p', 'h', 'e', 'm', 'e', '.'] 35 | 36 | -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.8.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Regex to match "Muhammad", "Hashim", or "Ali" 4 | regex_names = r'Muhammad|Hashim|Ali' 5 | 6 | # Example text 7 | text = "Muhammad, Hashim, and Ali went to Muhammad's house." 8 | 9 | # Find all matches 10 | matches = re.findall(regex_names, text) 11 | print(matches) # Output: ['Muhammad', 'Hashim', 'Ali', 'Muhammad'] 12 | 13 | 14 | # Regex to match whole words "Muhammad", "Hashim", or "Ali" 15 | regex_names_whole = r'\bMuhammad\b|\bHashim\b|\bAli\b' 16 | 17 | # Example text 18 | text = "Muhammad, Hashim, and Ali went to Muhammad's house." 19 | 20 | # Find all matches 21 | matches = re.findall(regex_names_whole, text) 22 | print(matches) # Output: ['Muhammad', 'Hashim', 'Ali', 'Muhammad'] -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem1.9.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | regex_names = r'\b(Muhammad|Hashim|Ali)\b' 4 | text = "Muhammad, Hashim, and Ali went to Muhammad's house." 5 | matches = re.findall(regex_names, text) 6 | print(matches) # Output: ['Muhammad', 'Hashim', 'Ali', 'Muhammad'] 7 | 8 | # Regex to match and capture date parts in yyyy-mm-dd format 9 | regex_date = r'\b(\d{4})-(\d{2})-(\d{2})\b' 10 | regex_date_without_capture = r'\b(?:\d{4})-(?:\d{2})-(?:\d{2})\b' 11 | # Example text with today's date 12 | text = "The event is scheduled for 2024-09-12." 13 | 14 | # Find all matches with captured groups 15 | matches = re.findall(regex_date, text) 16 | print(matches) # Output: [('2024', '09', '12')] 17 | # Extracting year, month, and day from the first match 18 | if matches: 19 | year, month, day = matches[0] 20 | print(f"Year: {year}, Month: {month}, Day: {day}") 21 | # Output: Year: 2024, Month: 09, Day: 12 22 | 23 | matches = re.findall(regex_date_without_capture, text) 24 | print(matches) # Output: ['2024-09-12'] 25 | 26 | -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.0.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Regex to match "magical" dates in yyyy-mm-dd format 4 | regex_magical_date = r'\b\d\d(\d\d)-\1-\1\b' 5 | 6 | # Example text containing dates 7 | text_dates = "Here are some dates: 2008-08-08, 1999-12-12, 2024-09-12, and 2011-11-11, 2010-10-10." 8 | 9 | # Find all magical dates 10 | matches_magical_dates = re.findall(regex_magical_date, text_dates) 11 | print("Magical Dates:", matches_magical_dates) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.1.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Regex to match dates and capture year, month, and day 4 | regex_named_date = r'\b(?P\d{4})-(?P\d{2})-(?P\d{2})\b' 5 | 6 | # Example text containing dates 7 | text_dates = "Here are some dates: 2024-09-12, 1999-12-25, 2008-08-08." 8 | 9 | # Find all matches and extract named groups 10 | matches_dates = re.finditer(regex_named_date, text_dates) 11 | for match in matches_dates: 12 | print(f"Year: {match.group('year')}, Month: {match.group('month')}, Day: {match.group('day')}") 13 | 14 | 15 | 16 | # Regex to match "magical" dates and capture the magical number 17 | regex_magical_named = r'\b\d{2}(?P\d{2})-(?P=magic)-(?P=magic)\b' 18 | 19 | # Example text containing dates 20 | text_magical_dates = "Some magical dates are: 2008-08-08, 2024-09-12, and 2011-11-11." 21 | 22 | # Find all matches and extract the "magic" number 23 | matches_magical = re.finditer(regex_magical_named, text_magical_dates) 24 | for match in matches_magical: 25 | print(f"Magical Number: {match.group('magic')}") -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.2.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Match a googol (100-digit number) 4 | regex_googol = r'\b\d{100}\b' 5 | 6 | text = "Number: 12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890." 7 | 8 | matches = re.findall(regex_googol, text) 9 | print("Googol Matches:", matches) 10 | 11 | 12 | 13 | 14 | # Match a 32-bit hexadecimal number 15 | regex_hex = r'\b[a-f0-9]{1,8}\b' 16 | 17 | text = "Hex numbers: 1a2b, deadbeef, 01234567." 18 | 19 | matches = re.findall(regex_hex, text, re.IGNORECASE) 20 | print("Hexadecimal Matches:", matches) 21 | 22 | 23 | 24 | # Match a 32-bit hexadecimal number with optional 'h' 25 | regex_hex_suffix = r'\b[a-f0-9]{1,8}h?\b' 26 | 27 | text = "Hex numbers: 1a2bh, deadbeef, 01234567h." 28 | 29 | matches = re.findall(regex_hex_suffix, text, re.IGNORECASE) 30 | print("Hexadecimal Matches with 'h' suffix:", matches) 31 | 32 | 33 | # Match a floating-point number 34 | regex_float = r'\d*\.\d+(?:e\d+)?' 35 | 36 | text = "Floating-point numbers: 0.5, 1.23e10, .75." 37 | 38 | matches = re.findall(regex_float, text) 39 | print("Floating-Point Matches:", matches) 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.3.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Correct regex to match text between

and

tags 4 | regex_lazy = r'

.*?

' 5 | 6 | text = """ 7 |

8 | The very first task is to find the beginning of a paragraph. 9 |

10 |

11 | Then you have to find the end of the paragraph 12 |

13 | """ 14 | 15 | # Find all matches for text between

and

tags 16 | matches = re.findall(regex_lazy, text, re.DOTALL) 17 | print("Matched Paragraphs:", matches) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.4.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Example text with numbers and letters 4 | text = "123abc 456" 5 | 6 | # Greedy quantifier example 7 | regex_greedy = r'\b\d+\b' 8 | matches_greedy = re.findall(regex_greedy, text) 9 | print("Greedy Matches:", matches_greedy) 10 | 11 | # Lazy quantifier example 12 | regex_lazy = r'\b\d+?\b' 13 | matches_lazy = re.findall(regex_lazy, text) 14 | print("Lazy Matches:", matches_lazy) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.5.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Sample HTML text where there is content after but before 4 | text = """ 5 | 6 | 7 | Some content here. 8 | 9 |
10 | More content without closing the HTML tag. 11 | """ 12 | 13 | # Updated regex with atomic grouping to prevent runaway repetition 14 | regex_atomic = r'(?>.*?).*?' 15 | 16 | # Try to find matches 17 | matches_atomic = re.findall(regex_atomic, text, re.DOTALL) 18 | print("Matches with Atomic Grouping:", matches_atomic) 19 | -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.6.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Sample text with bold tags 4 | text = "My cat is furry" 5 | 6 | # Regex pattern with lookbehind and lookahead 7 | regex_pattern = r"(?<=)\w+(?=)" 8 | 9 | # Find matches 10 | matches = re.findall(regex_pattern, text) 11 | print("Matches:", matches) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.7.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Sample list of comma-separated words 4 | text = "one,two,three,one,two" 5 | 6 | # Regex pattern with conditionals to ensure each word appears at least once 7 | regex_pattern = r'\b(?:(?:(one)|(two)|(three))(?:,|\b)){3,}(?(1)|(?!))(?(2)|(?!))(?(3)|(?!))' 8 | 9 | # Test for matches 10 | match = re.match(regex_pattern, text) 11 | 12 | # Check if the pattern matched the text 13 | if match: 14 | print("Valid match: All three words are present at least once.") 15 | else: 16 | print("Invalid match: Not all three words are present.") -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.8.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Regular expression with comments using free-spacing mode 4 | regex_pattern = r''' 5 | \d{4} # Year (four digits) 6 | - # Separator (hyphen) 7 | \d{2} # Month (two digits) 8 | - # Separator (hyphen) 9 | \d{2} # Day (two digits) 10 | ''' 11 | 12 | # Sample text 13 | text = "The date is 2024-08-19." 14 | 15 | # Finding matches with re.VERBOSE to allow comments and spaces 16 | matches = re.findall(regex_pattern, text, re.VERBOSE) 17 | print("Matches:", matches) 18 | 19 | 20 | import re 21 | 22 | # Regular expression with inline comments 23 | regex_pattern = r'(?#Year)\d{4}-(?#Separator)\d{2}-(?#Day)\d{2}' 24 | 25 | # Sample text 26 | text = "The date is 2024-08-19." 27 | 28 | # Finding matches without re.VERBOSE 29 | matches = re.findall(regex_pattern, text) 30 | print("Matches with inline comments:", matches) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem2.9.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Sample text to perform search-and-replace 4 | text = "This is a test: 123-456." 5 | 6 | # Regex pattern to match any three-digit number 7 | regex_pattern = r"\d{3}" 8 | 9 | # Replacement text: insert literal text including backreferences 10 | replacement_text = r"$%\*$1\\1" 11 | 12 | # Perform search-and-replace 13 | # `re.sub` is used to substitute matches with replacement text 14 | result = re.sub(regex_pattern, replacement_text, text) 15 | 16 | print("Result after replacement:", result) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem3.0.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Sample text where we want to wrap matches in tags 4 | text = "Visit example.com for more info." 5 | 6 | # Regex pattern to match words (simple example) 7 | regex_pattern = r"\b\w+\b" 8 | 9 | # Replacement text: wrap the entire match (group 0) with an tag 10 | replacement_text = r'\g<0>' 11 | 12 | # Perform search-and-replace 13 | result = re.sub(regex_pattern, replacement_text, text) 14 | 15 | print("Result after replacement:", result) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem3.1.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Sample text containing a 10-digit number 4 | text = "My number is 1234567890, please call me!" 5 | 6 | # Regex pattern to match a 10-digit phone number 7 | regex_pattern = r"\b(\d{3})(\d{3})(\d{4})\b" 8 | 9 | # Replacement pattern to format the matched phone number 10 | replacement_text = r"(\1) \2-\3" 11 | 12 | # Perform search-and-replace using re.sub() 13 | formatted_text = re.sub(regex_pattern, replacement_text, text) 14 | 15 | print("Formatted text:", formatted_text) -------------------------------------------------------------------------------- /01_python_fundamentals/20_regular_expressions/01_basic_regular_expressions/problem3.2.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | # Sample text where "Match" is the regex match 4 | text = "BeforeMatchAfter" 5 | 6 | # Regex pattern to capture the left context, the match, and the right context 7 | regex_pattern = r"(.*?)(Match)(.*)" 8 | 9 | # Replacement function to construct the desired replacement text 10 | def replacement_function(match): 11 | # Group 1: Left context (text before the match) 12 | left_context = match.group(1) 13 | # Group 2: The regex match (the word "Match") 14 | matched_text = match.group(2) 15 | # Group 3: Right context (text after the match) 16 | right_context = match.group(3) 17 | 18 | # Construct the replacement text: Left + Whole + Right 19 | return f"{left_context}{left_context}{matched_text}{right_context}{right_context}" 20 | 21 | # Perform the search-and-replace using re.sub() 22 | result = re.sub(regex_pattern, replacement_function, text) 23 | 24 | print("Result after replacement:", result) -------------------------------------------------------------------------------- /01_python_fundamentals/21_working_with_files/logs.txt: -------------------------------------------------------------------------------- 1 | [2024-10-08 12:00:00] System started 2 | [2024-10-08 12:05:00] User logged in 3 | [2024-10-08 12:10:30] User accessed settings 4 | [2024-10-08 12:15:45] User logged out 5 | 6 | -------------------------------------------------------------------------------- /01_python_fundamentals/21_working_with_files/logs_backup.txt: -------------------------------------------------------------------------------- 1 | [2024-10-08 12:00:00] System started 2 | [2024-10-08 12:05:00] User logged in 3 | [2024-10-08 12:10:30] User accessed settings 4 | [2024-10-08 12:15:45] User logged out 5 | [2024-10-10 14:48:36] User logged in 6 | [2024-10-10 14:48:36] User updated settings 7 | -------------------------------------------------------------------------------- /01_python_fundamentals/21_working_with_files/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | from datetime import datetime 3 | 4 | def read_logs(file_path): 5 | """Reads the existing log file and returns its content.""" 6 | try: 7 | with open(file_path, 'r') as log_file: 8 | logs = log_file.readlines() 9 | print("Existing Logs:") 10 | for log in logs: 11 | print(log.strip()) 12 | return logs 13 | except FileNotFoundError: 14 | print(f"Error: The file '{file_path}' does not exist.") 15 | return [] 16 | 17 | def append_log(file_path, log_message): 18 | """Appends a new log entry to the log file.""" 19 | with open(file_path, 'a') as log_file: 20 | timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 21 | log_file.write(f"[{timestamp}] {log_message}\n") 22 | print(f"Log entry added: [{timestamp}] {log_message}") 23 | 24 | def write_backup(file_path, backup_path, logs): 25 | """Writes all the logs into a backup file.""" 26 | with open(backup_path, 'w') as backup_file: 27 | backup_file.writelines(logs) 28 | print(f"Backup file '{backup_path}' created successfully.") 29 | 30 | def main(): 31 | log_file_path = 'logs.txt' 32 | backup_file_path = 'logs_backup.txt' 33 | 34 | # Step 1: Read existing logs 35 | logs = read_logs(log_file_path) 36 | 37 | # Step 2: Add new log entries 38 | append_log(log_file_path, "User logged in") 39 | append_log(log_file_path, "User updated settings") 40 | 41 | # Step 3: Read logs again after appending new entries 42 | logs = read_logs(log_file_path) 43 | 44 | # Step 4: Write logs to a backup file 45 | write_backup(log_file_path, backup_file_path, logs) 46 | 47 | if __name__ == "__main__": 48 | main() 49 | -------------------------------------------------------------------------------- /01_python_fundamentals/22_scope/example.txt: -------------------------------------------------------------------------------- 1 | This is safe! -------------------------------------------------------------------------------- /01_python_fundamentals/22_scope/main.py: -------------------------------------------------------------------------------- 1 | import builtins 2 | 3 | # Safely using built-ins after overriding 4 | open = "Overridden Open" # Overrides the built-in open 5 | print(open) # Prints the string 6 | 7 | # Access the original built-in open 8 | with builtins.open("example.txt", "w") as file: 9 | file.write("This is safe!") 10 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/01_import_and_attributes/b.py: -------------------------------------------------------------------------------- 1 | # File: b.py 2 | import c 3 | 4 | 5 | version = "1.0" 6 | 7 | def spam(text): 8 | print(f"{text} spam") 9 | 10 | 11 | def spam(text): 12 | c.ham(text) 13 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/01_import_and_attributes/c.py: -------------------------------------------------------------------------------- 1 | # File: c.py 2 | 3 | def ham(text): 4 | print(f"{text} ham") 5 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/01_import_and_attributes/main.py: -------------------------------------------------------------------------------- 1 | # File: main.py 2 | 3 | import b 4 | 5 | b.spam("Muhammad Hashim") 6 | print(b.version) # Outputs: 1.0 7 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/03_byte_code/my_module.py: -------------------------------------------------------------------------------- 1 | # File: my_module.py 2 | 3 | def greet(name): 4 | print(f"Hello, {name}! Welcome to Python 3.12.") 5 | 6 | # Example usage (uncomment to test) 7 | # greet("Muhammad Hashim") 8 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/01_extend_module/common_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/01_python_fundamentals/23_modules/04_module_search_path/01_extend_module/common_utils/__init__.py -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/01_extend_module/common_utils/helper.py: -------------------------------------------------------------------------------- 1 | def get_name(): 2 | return "Muhammad Hashim" -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/01_extend_module/modules/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/01_python_fundamentals/23_modules/04_module_search_path/01_extend_module/modules/__init__.py -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/01_extend_module/modules/main.py: -------------------------------------------------------------------------------- 1 | import user 2 | import sys 3 | 4 | for path in sys.path: 5 | print(path) 6 | 7 | 8 | import helper 9 | name = helper.get_name() 10 | myname = user.username(name) 11 | print(myname) -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/01_extend_module/modules/user.py: -------------------------------------------------------------------------------- 1 | user_data = 'User Details' 2 | 3 | def get_user_info(**user_data): 4 | return f''' 5 | User name is {user_data['name']} 6 | User age is {user_data['age']} 7 | User profession is {user_data['profession']} 8 | User hobbies are {', '.join(user_data['hobbies'])} 9 | ''' 10 | 11 | def username(name): 12 | return f'User name is {name}' -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/main.py: -------------------------------------------------------------------------------- 1 | # File: main.py 2 | 3 | import sys 4 | import os 5 | 6 | # Get the current directory (where main.py is located) 7 | current_dir = os.path.dirname(os.path.abspath(__file__)) 8 | 9 | # Get the parent directory (up one level) 10 | parent_dir = os.path.dirname(os.path.dirname(current_dir)) 11 | 12 | # Construct the path to 'common_utils' 13 | common_utils_path = os.path.join(parent_dir, 'common_utils') 14 | 15 | # Add 'common_utils' to sys.path if it's not already there 16 | if common_utils_path not in sys.path: 17 | sys.path.append(common_utils_path) 18 | 19 | # Now we can import modules from 'common_utils' 20 | from mypackage import my_module 21 | import helper # Importing from 'common_utils' directory 22 | 23 | def main(): 24 | name = helper.get_name() 25 | my_module.greet(name) 26 | my_module.farewell(name) 27 | 28 | if __name__ == "__main__": 29 | main() 30 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/mypackage/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/01_python_fundamentals/23_modules/04_module_search_path/mypackage/__init__.py -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/04_module_search_path/mypackage/my_module.py: -------------------------------------------------------------------------------- 1 | # File: mypackage/my_module.py 2 | 3 | def greet(name): 4 | print(f"Hello, {name}! Welcome to Python modules.") 5 | 6 | def farewell(name): 7 | print(f"Goodbye, {name}! Happy coding.") 8 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/05_configuring_the_search_path/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | for path in sys.path: 4 | print(path) 5 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/06_sys_path/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | # Print out each path in sys.path 4 | for path in sys.path: 5 | print(path) 6 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/07_module_coding_basic/main.py: -------------------------------------------------------------------------------- 1 | import module1 2 | module1.printer("Hello world!") 3 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/07_module_coding_basic/module1.py: -------------------------------------------------------------------------------- 1 | # File: module1.py 2 | 3 | def printer(x): 4 | print(x) 5 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/08_module_namespace/main.py: -------------------------------------------------------------------------------- 1 | import module2, module_example 2 | print(module2.name) # Outputs: Muhammad Hashim 3 | print(module2.age) # Outputs: 24 4 | 5 | print(dir(module2)) 6 | print(module2.__dict__.keys()) 7 | 8 | 9 | print(module_example.x) 10 | print(module_example.y) -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/08_module_namespace/module2.py: -------------------------------------------------------------------------------- 1 | # File: module2.py 2 | name = "Muhammad Hashim" 3 | age = 24 4 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/08_module_namespace/module_example.py: -------------------------------------------------------------------------------- 1 | # module_example.py 2 | print("Module is loading...") 3 | x = 5 4 | y = [1, 2, 3] 5 | print("Module is ended.") 6 | 7 | -------------------------------------------------------------------------------- /01_python_fundamentals/23_modules/09_reloading_modules/changer.py: -------------------------------------------------------------------------------- 1 | # File: changer.py 2 | message = "After editing" 3 | 4 | def printer(): 5 | print("reloaded:", message) 6 | -------------------------------------------------------------------------------- /01_python_fundamentals/24_modules_packages/01_package_import_basic/main.py: -------------------------------------------------------------------------------- 1 | # File: web_scraper/main.py 2 | 3 | from web_scraper import * 4 | from web_scraper.utils import clean_html, format_url 5 | 6 | # Example usage 7 | html = " Welcome to my site! " 8 | cleaned_html = clean_html(html) 9 | print(cleaned_html) # Outputs: Welcome to my site! 10 | 11 | formatted_url = format_url("HTTP://EXAMPLE.COM") 12 | print(formatted_url) # Outputs: http://example.com 13 | 14 | parsed_content = parse_page(cleaned_html) 15 | print(parsed_content) # Outputs: Parsed content from: Welcome to my site! 16 | -------------------------------------------------------------------------------- /01_python_fundamentals/24_modules_packages/01_package_import_basic/web_scraper/__init__.py: -------------------------------------------------------------------------------- 1 | from web_scraper.crawler import parse_page 2 | 3 | __all__ = ["parse_page"] -------------------------------------------------------------------------------- /01_python_fundamentals/24_modules_packages/01_package_import_basic/web_scraper/crawler/__init__.py: -------------------------------------------------------------------------------- 1 | # File: web_scraper/crawler/__init__.py 2 | 3 | from .page_parser import parse_page 4 | 5 | 6 | __all__ = ["parse_page"] 7 | 8 | # Initialize any settings needed for the crawler package 9 | print("Crawler package loaded!") 10 | -------------------------------------------------------------------------------- /01_python_fundamentals/24_modules_packages/01_package_import_basic/web_scraper/crawler/page_parser.py: -------------------------------------------------------------------------------- 1 | # File: web_scraper/crawler/page_parser.py 2 | 3 | def parse_page(html): 4 | return f"Parsed content from: {html}" 5 | -------------------------------------------------------------------------------- /01_python_fundamentals/24_modules_packages/01_package_import_basic/web_scraper/utils/__init__.py: -------------------------------------------------------------------------------- 1 | # File: web_scraper/utils/__init__.py 2 | 3 | from .helper import clean_html, format_url 4 | 5 | __all__ = ["clean_html", "format_url"] # Defines what can be imported using 'from utils import *' 6 | -------------------------------------------------------------------------------- /01_python_fundamentals/24_modules_packages/01_package_import_basic/web_scraper/utils/helper.py: -------------------------------------------------------------------------------- 1 | # File: web_scraper/utils/helper.py 2 | 3 | def clean_html(html): 4 | return html.strip() 5 | 6 | def format_url(url): 7 | return url.lower() 8 | -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/02_data_hiding_in_modules/main.py: -------------------------------------------------------------------------------- 1 | from module import * 2 | from underscore_example import * 3 | print(a, c) # Outputs: 1 3 4 | print(_b) # Raises NameError 5 | print(a) # Works 6 | # print(_b) -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/02_data_hiding_in_modules/module.py: -------------------------------------------------------------------------------- 1 | # __all__ = ['a', '_b'] 2 | 3 | a = 10 4 | _b = 20 5 | c = 30 6 | d = 40 7 | 8 | -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/02_data_hiding_in_modules/underscore_example.py: -------------------------------------------------------------------------------- 1 | a, _b, c, _d = 1, 2, 3, 4 -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/03_enable_future_feature/main.py: -------------------------------------------------------------------------------- 1 | import __future__ 2 | print(dir(__future__)) -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/04_mixed_usage_modes/calculator.py: -------------------------------------------------------------------------------- 1 | # File: calculator.py 2 | 3 | def add(a, b): 4 | return a + b 5 | 6 | def subtract(a, b): 7 | return a - b 8 | 9 | if __name__ == "__main__": 10 | # Run tests when executed directly 11 | print("Testing calculator functions:") 12 | print("3 + 5 =", add(3, 5)) 13 | print("10 - 4 =", subtract(10, 4)) 14 | -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/04_mixed_usage_modes/greetings.py: -------------------------------------------------------------------------------- 1 | # File: greetings.py 2 | 3 | def say_hello(): 4 | print("Hello, World!") 5 | 6 | if __name__ == "__main__": 7 | # This will only run if the script is executed directly 8 | say_hello() 9 | -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/04_mixed_usage_modes/main.py: -------------------------------------------------------------------------------- 1 | import greetings 2 | 3 | # Another script or interactive shell 4 | from calculator import add 5 | 6 | result = add(7, 3) 7 | print("7 + 3 =", result) 8 | -------------------------------------------------------------------------------- /01_python_fundamentals/25_advance_module/05_changig_the_module_search/main.py: -------------------------------------------------------------------------------- 1 | import sys 2 | print(sys.path) 3 | -------------------------------------------------------------------------------- /01_python_fundamentals/26_testing_your_code/name.py: -------------------------------------------------------------------------------- 1 | from name_function import get_formatted_name 2 | 3 | print("Enter 'q' at any time to quit.") 4 | 5 | while True: 6 | first = input("\nPlease give me a first name: ") 7 | if first == 'q': 8 | break 9 | last = input("Please give me a last name: ") 10 | if last == 'q': 11 | break 12 | 13 | formatted_name = get_formatted_name(first, last) 14 | print(f"\tNeatly formatted name: {formatted_name}.") -------------------------------------------------------------------------------- /01_python_fundamentals/26_testing_your_code/name_function.py: -------------------------------------------------------------------------------- 1 | # name_function.py 2 | 3 | def get_formatted_name(first, last, middle=''): 4 | """Generate a neatly formatted full name.""" 5 | if middle: 6 | full_name = f"{first} {middle} {last}" 7 | else: 8 | full_name = f"{first} {last}" 9 | return full_name.title() -------------------------------------------------------------------------------- /01_python_fundamentals/26_testing_your_code/survey.py: -------------------------------------------------------------------------------- 1 | # survey.py 2 | 3 | class AnonymousSurvey: 4 | """Collect anonymous answers to a survey question.""" 5 | 6 | def __init__(self, question): 7 | """Store a question, and prepare to store responses.""" 8 | self.question = question 9 | self.responses = [] 10 | 11 | def show_question(self): 12 | """Show the survey question.""" 13 | print(self.question) 14 | 15 | def store_response(self, new_response): 16 | """Store a single response to the survey.""" 17 | self.responses.append(new_response) 18 | 19 | def show_results(self): 20 | """Show all the responses that have been given.""" 21 | print("Survey results:") 22 | for response in self.responses: 23 | print(f"- {response}") -------------------------------------------------------------------------------- /01_python_fundamentals/26_testing_your_code/survey_program.py: -------------------------------------------------------------------------------- 1 | # survey_program.py 2 | 3 | from survey import AnonymousSurvey 4 | 5 | # Define a question, and make a survey. 6 | question = "What language did you first learn to speak?" 7 | language_survey = AnonymousSurvey(question) 8 | 9 | # Show the question, and store responses to the question. 10 | language_survey.show_question() 11 | print("Enter 'q' at any time to quit.\n") 12 | 13 | while True: 14 | response = input("Language: ") 15 | if response == 'q': 16 | break 17 | language_survey.store_response(response) 18 | 19 | # Show the survey results. 20 | print("\nThank you to everyone who participated in the survey!") 21 | language_survey.show_results() -------------------------------------------------------------------------------- /01_python_fundamentals/26_testing_your_code/test_name_function.py: -------------------------------------------------------------------------------- 1 | from name_function import get_formatted_name 2 | 3 | def test_first_last_name(): 4 | """Do names like 'muhammad hashim' work?""" 5 | formatted_name = get_formatted_name('muhammad', 'hashim') 6 | assert formatted_name == 'Muhammad Hashim' 7 | 8 | def test_first_last_middle_name(): 9 | """Do names like 'Wolfgang Amadeus Mozart' work?""" 10 | formatted_name = get_formatted_name('wolfgang', 'mozart', 'amadeus') 11 | assert formatted_name == 'Wolfgang Amadeus Mozart' -------------------------------------------------------------------------------- /01_python_fundamentals/26_testing_your_code/test_survey.py: -------------------------------------------------------------------------------- 1 | # test_survey.py 2 | 3 | import pytest 4 | from survey import AnonymousSurvey 5 | 6 | @pytest.fixture 7 | def language_survey(): 8 | """A survey that will be available to all test functions.""" 9 | question = "What language did you first learn to speak?" 10 | return AnonymousSurvey(question) 11 | 12 | def test_store_single_response(language_survey): 13 | """Test that a single response is stored properly.""" 14 | language_survey.store_response('English') 15 | assert 'English' in language_survey.responses 16 | 17 | def test_store_three_responses(language_survey): 18 | """Test that three individual responses are stored properly.""" 19 | responses = ['English', 'Spanish', 'Mandarin'] 20 | for response in responses: 21 | language_survey.store_response(response) 22 | for response in responses: 23 | assert response in language_survey.responses -------------------------------------------------------------------------------- /01_python_fundamentals/27_virtual_environment/Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | url = "https://pypi.org/simple" 3 | verify_ssl = true 4 | name = "pypi" 5 | 6 | [packages] 7 | django = "==5.2" 8 | asgiref = "*" 9 | sqlparse = "*" 10 | tzdata = "*" 11 | 12 | [dev-packages] 13 | 14 | [requires] 15 | python_version = "3.12" 16 | -------------------------------------------------------------------------------- /01_python_fundamentals/27_virtual_environment/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "1434b8b53095306d37791ec3eb2bc67947ffd5b944dd5ed889d1281b8fb95191" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": { 8 | "python_version": "3.12" 9 | }, 10 | "sources": [ 11 | { 12 | "name": "pypi", 13 | "url": "https://pypi.org/simple", 14 | "verify_ssl": true 15 | } 16 | ] 17 | }, 18 | "default": { 19 | "asgiref": { 20 | "hashes": [ 21 | "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", 22 | "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590" 23 | ], 24 | "index": "pypi", 25 | "markers": "python_version >= '3.8'", 26 | "version": "==3.8.1" 27 | }, 28 | "django": { 29 | "hashes": [ 30 | "sha256:031ccb717782f6af83a0063a1957686e87cb4581ea61b47b3e9addf60687989a", 31 | "sha256:032f8a6fc7cf05ccd1214e4a2e21dfcd6a23b9d575c6573cacc8c67828dbe642" 32 | ], 33 | "index": "pypi", 34 | "markers": "python_version >= '3.8'", 35 | "version": "==4.1" 36 | }, 37 | "sqlparse": { 38 | "hashes": [ 39 | "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272", 40 | "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca" 41 | ], 42 | "index": "pypi", 43 | "markers": "python_version >= '3.8'", 44 | "version": "==0.5.3" 45 | }, 46 | "tzdata": { 47 | "hashes": [ 48 | "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8", 49 | "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9" 50 | ], 51 | "index": "pypi", 52 | "markers": "python_version >= '2'", 53 | "version": "==2025.2" 54 | } 55 | }, 56 | "develop": {} 57 | } 58 | -------------------------------------------------------------------------------- /01_python_fundamentals/27_virtual_environment/poetry.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. 2 | 3 | [[package]] 4 | name = "asgiref" 5 | version = "3.8.1" 6 | description = "ASGI specs, helper code, and adapters" 7 | optional = false 8 | python-versions = ">=3.8" 9 | groups = ["main"] 10 | files = [ 11 | {file = "asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47"}, 12 | {file = "asgiref-3.8.1.tar.gz", hash = "sha256:c343bd80a0bec947a9860adb4c432ffa7db769836c64238fc34bdc3fec84d590"}, 13 | ] 14 | 15 | [package.extras] 16 | tests = ["mypy (>=0.800)", "pytest", "pytest-asyncio"] 17 | 18 | [[package]] 19 | name = "django" 20 | version = "5.2" 21 | description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." 22 | optional = false 23 | python-versions = ">=3.10" 24 | groups = ["main"] 25 | files = [ 26 | {file = "Django-5.2-py3-none-any.whl", hash = "sha256:91ceed4e3a6db5aedced65e3c8f963118ea9ba753fc620831c77074e620e7d83"}, 27 | {file = "Django-5.2.tar.gz", hash = "sha256:1a47f7a7a3d43ce64570d350e008d2949abe8c7e21737b351b6a1611277c6d89"}, 28 | ] 29 | 30 | [package.dependencies] 31 | asgiref = ">=3.8.1" 32 | sqlparse = ">=0.3.1" 33 | tzdata = {version = "*", markers = "sys_platform == \"win32\""} 34 | 35 | [package.extras] 36 | argon2 = ["argon2-cffi (>=19.1.0)"] 37 | bcrypt = ["bcrypt"] 38 | 39 | [[package]] 40 | name = "sqlparse" 41 | version = "0.5.3" 42 | description = "A non-validating SQL parser." 43 | optional = false 44 | python-versions = ">=3.8" 45 | groups = ["main"] 46 | files = [ 47 | {file = "sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca"}, 48 | {file = "sqlparse-0.5.3.tar.gz", hash = "sha256:09f67787f56a0b16ecdbde1bfc7f5d9c3371ca683cfeaa8e6ff60b4807ec9272"}, 49 | ] 50 | 51 | [package.extras] 52 | dev = ["build", "hatch"] 53 | doc = ["sphinx"] 54 | 55 | [[package]] 56 | name = "tzdata" 57 | version = "2025.2" 58 | description = "Provider of IANA time zone data" 59 | optional = false 60 | python-versions = ">=2" 61 | groups = ["main"] 62 | markers = "sys_platform == \"win32\"" 63 | files = [ 64 | {file = "tzdata-2025.2-py2.py3-none-any.whl", hash = "sha256:1a403fada01ff9221ca8044d701868fa132215d84beb92242d9acd2147f667a8"}, 65 | {file = "tzdata-2025.2.tar.gz", hash = "sha256:b60a638fcc0daffadf82fe0f57e53d06bdec2f36c4df66280ae79bce6bd6f2b9"}, 66 | ] 67 | 68 | [metadata] 69 | lock-version = "2.1" 70 | python-versions = ">=3.12" 71 | content-hash = "512af195e8778a91be8783684b9cd25e53d54eea985986e800a87063dc789ef6" 72 | -------------------------------------------------------------------------------- /01_python_fundamentals/27_virtual_environment/pyproject.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | name = "django-project" 3 | version = "0.1.0" 4 | description = "Poetry For Django" 5 | authors = [ 6 | {name = "Muhammad Hashim",email = "hashiimtahir@gmail.com"} 7 | ] 8 | license = {text = "MIT"} 9 | readme = "README.md" 10 | requires-python = ">=3.12" 11 | dependencies = [ 12 | "django (>=5.2,<6.0)" 13 | ] 14 | 15 | 16 | [build-system] 17 | requires = ["poetry-core>=2.0.0,<3.0.0"] 18 | build-backend = "poetry.core.masonry.api" 19 | -------------------------------------------------------------------------------- /01_python_fundamentals/common_utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/01_python_fundamentals/common_utils/__init__.py -------------------------------------------------------------------------------- /01_python_fundamentals/common_utils/helper.py: -------------------------------------------------------------------------------- 1 | # File: utils/helper.py 2 | 3 | def get_name(): 4 | return "Muhammad Hashim" 5 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/01_what_is_classes/main.py: -------------------------------------------------------------------------------- 1 | class TextBox: 2 | def __init__(self): 3 | self.text = "" # Default text content is an empty string 4 | self.size = 0 # Default size is 0 5 | 6 | def set_text(self, text: str): 7 | """Sets the text content of the textbox.""" 8 | self.text = text 9 | 10 | def get_text(self) -> str: 11 | """Retrieves the current text content of the textbox.""" 12 | return self.text 13 | 14 | def set_size(self, size: int): 15 | """Sets the size of the textbox.""" 16 | self.size = size 17 | 18 | def get_size(self) -> int: 19 | """Retrieves the current size of the textbox.""" 20 | return self.size 21 | 22 | 23 | # Create an instance of the TextBox class 24 | textbox = TextBox() 25 | 26 | # Set text content 27 | textbox.set_text("Hello, World!") 28 | 29 | # Set size 30 | textbox.set_size(10) 31 | 32 | # Retrieve text content and size 33 | print("Text content:", textbox.get_text()) 34 | print("Size:", textbox.get_size()) -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/02_creating_a_class/main.py: -------------------------------------------------------------------------------- 1 | class TextBox: 2 | def __init__(self): 3 | self.text = "" 4 | 5 | def set_text(self, text): 6 | self.text = text 7 | 8 | def clear(self): 9 | self.text = "" 10 | 11 | textbox = TextBox() 12 | textbox.set_text("Hello, Python!") 13 | print(textbox.text) 14 | textbox.clear() 15 | print(textbox.text) -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/04_procedural_programming/main.py: -------------------------------------------------------------------------------- 1 | def create_employee(base_salary, hourly_rate): 2 | return { 3 | "base_salary": base_salary, 4 | "hourly_rate": hourly_rate 5 | } 6 | 7 | def calculate_wage(employee, extra_hours): 8 | return employee["base_salary"] + (employee["hourly_rate"] * extra_hours) 9 | 10 | if __name__ == "__main__": 11 | employee = create_employee(50000, 20) 12 | wage = calculate_wage(employee, 20) 13 | print(wage) 14 | 15 | 16 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/05_encapsulation/main.py: -------------------------------------------------------------------------------- 1 | class Employee: 2 | def __init__(self): 3 | self.base_salary = 0 4 | self.hourly_rate = 0 5 | 6 | def calculate_wage(self, extra_hours): 7 | return self.base_salary + (self.hourly_rate * extra_hours) 8 | 9 | 10 | if __name__ == "__main__": 11 | employee = Employee() 12 | employee.base_salary = 50000 13 | employee.hourly_rate = 20 14 | wage = employee.calculate_wage(20) 15 | print(wage) 16 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/06_getter_and_setter/main.py: -------------------------------------------------------------------------------- 1 | class Employee: 2 | def __init__(self, s, h): 3 | self.__salary = s 4 | self.__hours = h 5 | 6 | def calculate_wage(self, extra_hours): 7 | return f"The Calculated Wage is {self.__salary * self.__hours + 50 * extra_hours}" 8 | 9 | def set_salary(self, salary): 10 | if salary <= 0: 11 | raise ValueError("Salary can not less then or equal to zero") 12 | self.__salary = salary 13 | 14 | def get_salary(self): 15 | return self.__salary 16 | 17 | def set_hours(self, hours): 18 | if hours <= 0: 19 | raise ValueError("Hours can not less then or equal to zero") 20 | self.__hours = hours 21 | 22 | def get_hours(self): 23 | return self.__hours 24 | 25 | e = Employee(10000, 20) 26 | print(e.calculate_wage(10)) 27 | e.set_salary(2000000.124) 28 | e.set_hours(30) 29 | print(e.calculate_wage(10)) 30 | try: 31 | print(e.set_salary(0)) 32 | except ValueError as e: 33 | print("Salary can not less then or equal to zero") 34 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/07_abstraction/01_coupling/main.py: -------------------------------------------------------------------------------- 1 | class Employee: 2 | def __init__(self): 3 | self._base_salary = 0 4 | self._hourly_rate = 0 5 | 6 | def calculate_wage(self, extra_hours): 7 | return self._base_salary + (self._hourly_rate * extra_hours) 8 | 9 | def set_base_salary(self, base_salary): 10 | if base_salary <= 0: 11 | raise ValueError("Salary cannot be 0 or less.") 12 | self._base_salary = base_salary 13 | 14 | def __get_base_salary(self): 15 | return self._base_salary 16 | 17 | def __get_hourly_rate(self): 18 | return self._hourly_rate 19 | 20 | def set_hourly_rate(self, hourly_rate): 21 | if hourly_rate < 0: 22 | raise ValueError("Hourly rate cannot be negative.") 23 | self._hourly_rate = hourly_rate 24 | 25 | 26 | if __name__ == "__main__": 27 | employee = Employee() 28 | employee.set_base_salary(50000) 29 | employee.set_hourly_rate(20) 30 | wage = employee.calculate_wage(10) 31 | print(f'Calculate wages: {wage}') 32 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/07_abstraction/01_coupling/readme.md: -------------------------------------------------------------------------------- 1 | # 🔗 Reducing Coupling in `Employee` Class Example 2 | 3 | This guide shows how **encapsulation** in the `Employee` class helps reduce **coupling**—the dependency between parts of the code. By controlling access to data, encapsulation improves flexibility and maintainability. 4 | 5 | --- 6 | 7 | ## 📑 Table of Contents 8 | 9 | - [🔗 Reducing Coupling in `Employee` Class Example](#-reducing-coupling-in--employee-class-example) 10 | - [📑 Table of Contents](#-table-of-contents) 11 | - [⚙️ Original Code](#️-original-code) 12 | - [🔍 How the Code Reduces Coupling](#-how-the-code-reduces-coupling) 13 | - [🔒 Encapsulation](#-encapsulation) 14 | - [🔐 Controlled Access](#-controlled-access) 15 | - [📜 Summary](#-summary) 16 | 17 | --- 18 | 19 | ### ⚙️ Original Code 20 | 21 | ```python 22 | class Employee: 23 | def __init__(self): 24 | self._base_salary = 0 25 | self._hourly_rate = 0 26 | 27 | def calculate_wage(self, extra_hours): 28 | return self._base_salary + (self._hourly_rate * extra_hours) 29 | 30 | def set_base_salary(self, base_salary): 31 | if base_salary <= 0: 32 | raise ValueError("Salary cannot be 0 or less.") 33 | self._base_salary = base_salary 34 | 35 | def _get_base_salary(self): 36 | return self._base_salary 37 | 38 | def _get_hourly_rate(self): 39 | return self._hourly_rate 40 | 41 | def set_hourly_rate(self, hourly_rate): 42 | if hourly_rate < 0: 43 | raise ValueError("Hourly rate cannot be negative.") 44 | self._hourly_rate = hourly_rate 45 | ``` 46 | 47 | --- 48 | 49 | ### 🔍 How the Code Reduces Coupling 50 | 51 | #### 🔒 Encapsulation 52 | 53 | - **Private Attributes**: `_base_salary` and `_hourly_rate` are private and cannot be accessed directly, so only class methods control them. 54 | - **Single Control Point**: The `Employee` class fully manages its internal state, minimizing outside dependencies and making the class self-contained. 55 | 56 | #### 🔐 Controlled Access 57 | 58 | - **Setters with Validation**: `set_base_salary` and `set_hourly_rate` validate inputs, ensuring that only valid data is assigned, protecting the object’s state. 59 | - **Getters**: `_get_base_salary` and `_get_hourly_rate` provide a controlled way to access private attributes without exposing details. 60 | - **Loose Coupling**: Methods let external code interact with the class without knowing how attributes are stored or processed, reducing dependency on the class’s internals. 61 | 62 | --- 63 | 64 | ### 📜 Summary 65 | 66 | Encapsulation in the `Employee` class reduces coupling by: 67 | 68 | - Hiding internal data, 69 | - Providing controlled access through methods, 70 | - Making code easier to maintain, test, and extend. 71 | 72 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/07_abstraction/02_coupling/main.py: -------------------------------------------------------------------------------- 1 | class Browser: 2 | def navigate(self, address): 3 | ip = self.__find_ip_address(address) 4 | html = self.__send_http_request(ip) 5 | print(html) 6 | 7 | def __send_http_request(self, ip): 8 | if ip is not None: 9 | return "" 10 | else: 11 | return "No IP address or domain found" 12 | 13 | def __find_ip_address(self, address): 14 | if address: 15 | return address 16 | else: 17 | # return '127.0.0.1' 18 | return None 19 | 20 | browser = Browser() 21 | browser.navigate('127.0.0.0') 22 | browser.navigate(None) 23 | 24 | 25 | # A = ' ' 26 | # print(ord(A)) 27 | 28 | 29 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/07_abstraction/02_coupling/readme.md: -------------------------------------------------------------------------------- 1 | # 🔐 Reducing Coupling with Mangling Convention in Python 2 | 3 | In Python, **name mangling** (using `__` before method names) makes members private to a class. This convention doesn’t directly reduce coupling but supports **encapsulation** and **information hiding**, which help lower dependencies between classes. 4 | 5 | --- 6 | 7 | ## 📑 Table of Contents 8 | 9 | - [🔐 Reducing Coupling with Mangling Convention in Python](#-reducing-coupling-with-mangling-convention-in-python) 10 | - [📑 Table of Contents](#-table-of-contents) 11 | - [⚙️ Original Code](#️-original-code) 12 | - [🔍 How Mangling Reduces Coupling](#-how-mangling-reduces-coupling) 13 | - [📜 Summary](#-summary) 14 | 15 | --- 16 | 17 | ### ⚙️ Original Code 18 | 19 | ```python 20 | class Browser: 21 | def navigate(self, address): 22 | ip = self.__find_ip_address(address) 23 | html = self.__send_http_request(ip) 24 | print(html) 25 | 26 | def __send_http_request(self, ip): 27 | return "" if ip else "No IP address or domain found" 28 | 29 | def __find_ip_address(self, address): 30 | return address or None 31 | 32 | browser = Browser() 33 | browser.navigate('127.0.0.0') 34 | browser.navigate(None) 35 | ``` 36 | 37 | --- 38 | 39 | ### 🔍 How Mangling Reduces Coupling 40 | 41 | 1. **Encapsulation**: Methods `__send_http_request` and `__find_ip_address` are hidden, keeping internal logic private and reducing dependencies on the class’s internal details. 42 | 43 | 2. **Preventing External Access**: Name mangling prevents external access and unintended overrides, ensuring `Browser` only exposes necessary methods. 44 | 45 | 3. **Information Hiding**: Mangling limits what external code sees, enforcing interaction solely through `navigate` and lowering coupling. 46 | 47 | --- 48 | 49 | ### 📜 Summary 50 | 51 | Name mangling in Python supports encapsulation and information hiding, indirectly reducing coupling by keeping internals private, preventing interference, and promoting flexible, maintainable code. -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/07_abstraction/03_abstraction/main.py: -------------------------------------------------------------------------------- 1 | class Browser: 2 | def navigate(self, address: str): 3 | ip = self.__find_ip_address(address) 4 | if ip is None: 5 | return "No IP address or domain found" 6 | html = self.__send_http_request(ip) 7 | return html 8 | 9 | def __find_ip_address(self, ip: str) -> str: 10 | if not ip: 11 | return None 12 | return ip 13 | 14 | def __send_http_request(self, ip: str): 15 | if self.__is_valid_ip(ip) or ip == 'localhost': 16 | return """ 17 | 18 | 19 | 20 | 21 | Document 22 | 23 | 24 |

Welcome to the website

25 | 26 | """ 27 | else: 28 | return "404 Not Found" 29 | 30 | 31 | def __is_valid_ip(self, ip: str) -> bool: 32 | parts = ip.split('.') 33 | if len(parts) == 4 and all(p.isdigit() and 0 <= int(p) <= 255 for p in parts): 34 | return True 35 | return False 36 | 37 | # Testing the code 38 | nav = Browser() 39 | print(nav.navigate('localhost')) # Should return HTML content 40 | print(nav.navigate('')) # Should return "No IP address or domain found" 41 | print(nav.navigate(None)) # Should return "No IP address or domain found" 42 | print(nav.navigate('127.0.0.1')) # Should return HTML content for the IP # Should return HTML content for a valid domain 43 | print(nav.navigate('invalid-ip')) # Should return "404 Not Found" 44 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/08_constructor/main.py: -------------------------------------------------------------------------------- 1 | class Employee: 2 | def __init__(self, base_salary, hourly_rate): 3 | self.__set_base_salary(base_salary) 4 | self.__set_hourly_rate(hourly_rate) 5 | 6 | def calculate_wage(self, extra_hours): 7 | return self.__base_salary + (self.__hourly_rate * extra_hours) 8 | 9 | def __set_base_salary(self, base_salary): 10 | if base_salary <= 0: 11 | raise ValueError("Salary cannot be 0 or less.") 12 | self.__base_salary = base_salary 13 | 14 | def __set_hourly_rate(self, hourly_rate): 15 | if hourly_rate < 0: 16 | raise ValueError("Hourly rate cannot be negative.") 17 | self.__hourly_rate = hourly_rate 18 | 19 | # Getter methods can be public if needed 20 | def get_base_salary(self): 21 | return self.__base_salary 22 | 23 | def get_hourly_rate(self): 24 | return self.__hourly_rate 25 | 26 | 27 | if __name__ == "__main__": 28 | employee = Employee(50000, 20) 29 | wage = employee.calculate_wage(10) 30 | print(f'Calculate wages: {wage}') -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/09_default_arguments/main.py: -------------------------------------------------------------------------------- 1 | class Employee: 2 | numberOfEmployees = 0 3 | 4 | def __init__(self, base_salary, hourly_rate=0): 5 | self.__set_base_salary(base_salary) 6 | self.__set_hourly_rate(hourly_rate) 7 | Employee.numberOfEmployees += 1 8 | 9 | def calculate_wage(self, extra_hours=0): 10 | return self.base_salary + (self.hourly_rate * extra_hours) 11 | 12 | @staticmethod 13 | def print_number_of_employees(): 14 | print(Employee.numberOfEmployees) 15 | 16 | def __set_base_salary(self, base_salary): 17 | if base_salary <= 0: 18 | raise ValueError("Salary cannot be 0 or less.") 19 | self.base_salary = base_salary 20 | 21 | def __get_base_salary(self): 22 | return self.base_salary 23 | 24 | def __get_hourly_rate(self): 25 | return self.hourly_rate 26 | 27 | def __set_hourly_rate(self, hourly_rate): 28 | if hourly_rate < 0: 29 | raise ValueError("Hourly rate cannot be negative.") 30 | self.hourly_rate = hourly_rate 31 | 32 | 33 | if __name__ == "__main__": 34 | employee = Employee(50000, 20) 35 | Employee.print_number_of_employees() 36 | wage = employee.calculate_wage() 37 | print(wage) 38 | 39 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/10_constructor_overloading/main.py: -------------------------------------------------------------------------------- 1 | class Employee: 2 | def __init__(self, base_salary, hourly_rate=None): 3 | self.__set_base_salary(base_salary) 4 | if hourly_rate is None: 5 | self.hourly_rate = 0 6 | else: 7 | self.__set_hourly_rate(hourly_rate) 8 | 9 | def calculate_wage(self, extra_hours=0): 10 | return self.base_salary + (self.hourly_rate * extra_hours) 11 | 12 | def __set_base_salary(self, base_salary): 13 | if base_salary < 0: 14 | raise ValueError("Salary cannot be less than 0.") 15 | self.base_salary = base_salary 16 | 17 | def __get_base_salary(self): 18 | return self.base_salary 19 | 20 | def __get_hourly_rate(self): 21 | return self.hourly_rate 22 | 23 | def __set_hourly_rate(self, hourly_rate): 24 | if hourly_rate < 0: 25 | raise ValueError("Hourly rate cannot be negative.") 26 | self.hourly_rate = hourly_rate 27 | 28 | 29 | 30 | employee1 = Employee(10000) 31 | employee2 = Employee(50000, 20) 32 | wage1 = employee1.calculate_wage() 33 | wage2 = employee2.calculate_wage() 34 | print(wage1) 35 | print(wage2) 36 | -------------------------------------------------------------------------------- /02_object_oriented_programming/01_Classes/11_static_method_and_class_variable/main.py: -------------------------------------------------------------------------------- 1 | class Employee: 2 | number_of_employees = 0 # Class variable to keep track of the number of employees 3 | 4 | def __init__(self, base_salary, hourly_rate=None): 5 | """ 6 | Constructor method to initialize an Employee object. 7 | 8 | Parameters: 9 | - base_salary: Base salary of the employee. 10 | - hourly_rate: Hourly rate for extra hours worked (default is None). 11 | """ 12 | self.__set_base_salary(base_salary) # Set base salary using a private method 13 | if hourly_rate is None: 14 | self.hourly_rate = 0 # If hourly_rate is not provided, set it to 0 15 | else: 16 | self.__set_hourly_rate(hourly_rate) # Set hourly rate using a private method 17 | Employee.number_of_employees += 1 # Increment the number of employees 18 | 19 | @staticmethod 20 | def print_number_of_employees(): 21 | """Static method to print the total number of employees.""" 22 | print(f'Number of Employees is {Employee.number_of_employees}') 23 | 24 | def calculate_wage(self, extra_hours=0): 25 | """ 26 | Method to calculate the wage of the employee. 27 | 28 | Parameters: 29 | - extra_hours: Number of extra hours worked (default is 0). 30 | 31 | Returns: 32 | - Total wage of the employee. 33 | """ 34 | return self.base_salary + (self.hourly_rate * extra_hours) 35 | 36 | def __set_base_salary(self, base_salary): 37 | """ 38 | Private method to set the base salary of the employee. 39 | 40 | Parameters: 41 | - base_salary: Base salary of the employee. 42 | """ 43 | if base_salary < 0: 44 | raise ValueError("Salary cannot be less than 0.") 45 | self.base_salary = base_salary 46 | 47 | def __set_hourly_rate(self, hourly_rate): 48 | """ 49 | Private method to set the hourly rate of the employee. 50 | 51 | Parameters: 52 | - hourly_rate: Hourly rate for extra hours worked. 53 | """ 54 | if hourly_rate < 0: 55 | raise ValueError("Hourly rate cannot be negative.") 56 | self.hourly_rate = hourly_rate 57 | 58 | 59 | # Creating employee objects 60 | employee1 = Employee(10000) 61 | employee2 = Employee(50000, 20) 62 | 63 | # Calculating wages for each employee 64 | wage1 = employee1.calculate_wage() 65 | wage2 = employee2.calculate_wage() 66 | 67 | # Printing wages 68 | print(wage1) 69 | print(wage2) 70 | 71 | # Printing the total number of employees 72 | Employee.print_number_of_employees() 73 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/01_inheritance/main.py: -------------------------------------------------------------------------------- 1 | class UIControl: 2 | def __init__(self): 3 | self._is_enabled = True 4 | 5 | def enable(self): 6 | self._is_enabled = True 7 | 8 | def disable(self): 9 | self._is_enabled = False 10 | 11 | def is_enabled(self): 12 | return self._is_enabled 13 | 14 | class TextBox(UIControl): 15 | def __init__(self): 16 | super().__init__() 17 | self._text = "" 18 | 19 | def set_text(self, text): 20 | self._text = text 21 | 22 | def clear(self): 23 | self._text = "" 24 | 25 | control = TextBox() 26 | control.disable() 27 | print(control.is_enabled()) 28 | 29 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/02_the_object_class/main.py: -------------------------------------------------------------------------------- 1 | class User(object): 2 | def __new__(cls, username, *args, **kwargs): 3 | # Ensure username is always stored in uppercase 4 | username = username.upper() # Convert to uppercase 5 | # Create the instance using the superclass's __new__ method 6 | instance = super().__new__(cls) 7 | # Store the converted username in the instance 8 | instance.username = username 9 | return instance 10 | 11 | def __init__(self, username): 12 | # Constructor will not modify username; __new__ handles it 13 | self.original_username = username # Just to store the original (if needed) 14 | 15 | def __str__(self): 16 | # Provide a friendly string representation of the object 17 | return f"User(username={self.username})" 18 | # Creating User objects with different username inputs 19 | user1 = User("hashim") 20 | user2 = User("ali") 21 | user3 = User("Fatima") 22 | 23 | # Displaying the users 24 | print(user1) # Output: User(username=HASHIM) 25 | print(user2) # Output: User(username=ALI) 26 | print(user3) # Output: User(username=FATIMA) 27 | 28 | # Accessing the username attribute 29 | print(user1.username) # Output: HASHIM 30 | print(user1.original_username) # Output: hashim -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/03_constructors_and_inheritance/main.py: -------------------------------------------------------------------------------- 1 | class UIControl: 2 | def __init__(self, is_enabled=True): 3 | self._is_enabled = is_enabled 4 | print('UIControl Constructor') 5 | 6 | def enable(self): 7 | self._is_enabled = True 8 | 9 | def disable(self): 10 | self._is_enabled = False 11 | 12 | def is_enabled(self): 13 | return self._is_enabled 14 | 15 | 16 | class TextBox(UIControl): 17 | def __init__(self): 18 | super().__init__(True) 19 | self._text = "" 20 | print('TextBox Constructor') 21 | 22 | def set_text(self, text): 23 | self._text = text 24 | 25 | def clear(self): 26 | self._text = "" 27 | 28 | 29 | # Creating an instance of TextBox 30 | control = TextBox() 31 | control.disable() 32 | print(control.is_enabled()) # This will print: False 33 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/04_overriding_methods/main.py: -------------------------------------------------------------------------------- 1 | class UIControl: 2 | def __init__(self, is_enabled=True): 3 | self._is_enabled = is_enabled 4 | 5 | def __str__(self): 6 | return str(self.is_enabled()) 7 | 8 | def enable(self): 9 | self._is_enabled = True 10 | 11 | def disable(self): 12 | self._is_enabled = False 13 | 14 | def is_enabled(self): 15 | return self._is_enabled 16 | 17 | 18 | class TextBox(UIControl): 19 | def __init__(self): 20 | super().__init__(True) 21 | self.text = "" 22 | 23 | def __str__(self): 24 | return self.text 25 | 26 | def set_text(self, text): 27 | self.text = text 28 | 29 | def clear(self): 30 | self.text = "" 31 | 32 | 33 | # Creating an instance of TextBox and setting text 34 | control = UIControl() 35 | print(str(control)) 36 | textbox = TextBox() 37 | textbox.set_text("Muhammad Hashim") 38 | print(textbox) 39 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/08_polymorphism/data.csv: -------------------------------------------------------------------------------- 1 | name,age,city,hobbies,passion,career 2 | Muhammad Hashim,24,Islamabad,"Coffee, Mountain trips","Software Engineering","Python Instructor" 3 | Ibrahim,23,Islamabad,"Reading, Fitness","Finance","Financial Analyst" 4 | Hafiz,23,Lahore,"Gaming, Travel","Mechanical Engineering","Engineer" 5 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/08_polymorphism/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Muhammad Hashim", 4 | "age": 24, 5 | "city": "Islamabad", 6 | "hobbies": ["Coffee", "Mountain trips"], 7 | "passion": "Software Engineering", 8 | "career": "Python Instructor" 9 | }, 10 | { 11 | "name": "Ibrahim", 12 | "age": 23, 13 | "city": "Islamabad", 14 | "hobbies": ["Reading", "Fitness"], 15 | "passion": "Finance", 16 | "career": "Financial Analyst" 17 | }, 18 | { 19 | "name": "Hafiz", 20 | "age": 23, 21 | "city": "Lahore", 22 | "hobbies": ["Gaming", "Travel"], 23 | "passion": "Mechanical Engineering", 24 | "career": "Engineer" 25 | } 26 | ] 27 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/08_polymorphism/data.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Muhammad Hashim 4 | 24 5 | Islamabad 6 | 7 | Coffee 8 | Mountain trips 9 | 10 | Software Engineering 11 | Python Instructor 12 | 13 | 14 | Ibrahim 15 | 23 16 | Islamabad 17 | 18 | Reading 19 | Fitness 20 | 21 | Finance 22 | Financial Analyst 23 | 24 | 25 | Hafiz 26 | 23 27 | Lahore 28 | 29 | Gaming 30 | Travel 31 | 32 | Mechanical Engineering 33 | Engineer 34 | 35 | 36 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/08_polymorphism/main.py: -------------------------------------------------------------------------------- 1 | class FileParser: 2 | def parse(self, filepath): 3 | raise NotImplementedError("Subclass must implement parse method") 4 | 5 | class CSVParser(FileParser): 6 | def parse(self, filepath): 7 | import csv 8 | with open(filepath, 'r', encoding='utf-8') as file: 9 | reader = csv.DictReader(file) 10 | data = list(reader) 11 | return data 12 | 13 | class JSONParser(FileParser): 14 | def parse(self, filepath): 15 | import json 16 | with open(filepath, 'r', encoding='utf-8') as file: 17 | data = json.load(file) 18 | return data 19 | 20 | class XMLParser(FileParser): 21 | def parse(self, filepath): 22 | import xml.etree.ElementTree as ET 23 | tree = ET.parse(filepath) 24 | root = tree.getroot() 25 | data = [] 26 | for person in root.findall('person'): 27 | person_data = { 28 | 'name': person.find('name').text, 29 | 'age': person.find('age').text, 30 | 'city': person.find('city').text, 31 | 'hobbies': [hobby.text for hobby in person.find('hobbies').findall('hobby')], 32 | 'passion': person.find('passion').text, 33 | 'career': person.find('career').text 34 | } 35 | data.append(person_data) 36 | return data 37 | 38 | def process_file(parser, filepath): 39 | data = parser.parse(filepath) 40 | # Process data 41 | print(f"Processed data from {filepath}:") 42 | for item in data: 43 | print(item) 44 | print('-' * 40) 45 | 46 | # Usage 47 | parsers = [CSVParser(), JSONParser(), XMLParser()] 48 | filepaths = ['data.csv', 'data.json', 'data.xml'] 49 | 50 | for parser, filepath in zip(parsers, filepaths): 51 | process_file(parser, filepath) 52 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/09_abstract_classes_and_methods/example.py: -------------------------------------------------------------------------------- 1 | from abc import ABCMeta, abstractmethod 2 | 3 | class MyABC(metaclass=ABCMeta): 4 | @abstractmethod 5 | def my_abstract_method(self): 6 | pass 7 | 8 | 9 | my = MyABC() # TypeError: Can't instantiate abstract class MyABC with abstract methods -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/09_abstract_classes_and_methods/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class UIControl(ABC): 4 | def __init__(self): 5 | self._is_enabled = True 6 | 7 | @abstractmethod 8 | def render(self): 9 | pass 10 | 11 | def enable(self): 12 | self._is_enabled = True 13 | 14 | def disable(self): 15 | self._is_enabled = False 16 | 17 | def is_enabled(self): 18 | return self._is_enabled 19 | 20 | 21 | class TextBox(UIControl): 22 | def __init__(self): 23 | super().__init__() 24 | self._text = "" 25 | 26 | def render(self): 27 | print("Render TextBox") 28 | 29 | def __str__(self): 30 | return self._text 31 | 32 | def set_text(self, text): 33 | self._text = text 34 | 35 | def clear(self): 36 | self._text = "" 37 | 38 | 39 | class CheckBox(UIControl): 40 | def render(self): 41 | print("Render CheckBox") 42 | 43 | 44 | # Usage 45 | textbox = TextBox() 46 | checkbox = CheckBox() 47 | textbox.set_text("Muhammad Hashim") 48 | print(textbox) 49 | textbox.render() 50 | checkbox.render() 51 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/09_abstract_classes_and_methods/uicontrol.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | import tkinter as tk 3 | 4 | # Abstract base class for GUI elements 5 | class GUIElement(ABC): 6 | def __init__(self, master, x, y): 7 | self.master = master 8 | self.x = x 9 | self.y = y 10 | 11 | @abstractmethod 12 | def create(self): 13 | pass 14 | 15 | # Concrete class for Textbox 16 | class Textbox(GUIElement): 17 | def create(self): 18 | self.entry = tk.Entry(self.master) 19 | self.entry.place(x=self.x, y=self.y) 20 | 21 | # Concrete class for Checkbox 22 | class Checkbox(GUIElement): 23 | def create(self): 24 | self.var = tk.IntVar() 25 | self.checkbox = tk.Checkbutton(self.master, text="Check me", variable=self.var) 26 | self.checkbox.place(x=self.x, y=self.y) 27 | 28 | # Concrete class for Radiobutton 29 | class Radiobutton(GUIElement): 30 | def create(self): 31 | self.var = tk.StringVar() 32 | self.radio1 = tk.Radiobutton(self.master, text="Option 1", variable=self.var, value="1") 33 | self.radio2 = tk.Radiobutton(self.master, text="Option 2", variable=self.var, value="2") 34 | self.radio1.place(x=self.x, y=self.y) 35 | self.radio2.place(x=self.x, y=self.y + 30) 36 | 37 | # Main GUI application 38 | class Application(tk.Tk): 39 | def __init__(self): 40 | super().__init__() 41 | self.title("Abstract Class GUI Elements") 42 | self.geometry("300x200") 43 | 44 | # Creating GUI elements 45 | textbox = Textbox(self, 50, 20) 46 | textbox.create() 47 | 48 | checkbox = Checkbox(self, 50, 60) 49 | checkbox.create() 50 | 51 | radiobutton = Radiobutton(self, 50, 100) 52 | radiobutton.create() 53 | 54 | 55 | app = Application() 56 | app.mainloop() 57 | -------------------------------------------------------------------------------- /02_object_oriented_programming/02_inheritance/10_callable_object/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Shape(ABC): 5 | def __init__(self, name): 6 | self.name = name 7 | 8 | @abstractmethod 9 | def area(self): 10 | raise NotImplementedError("Subclasses must implement area() method") 11 | 12 | 13 | class Circle(Shape): 14 | def __init__(self, name, radius): 15 | super().__init__(name) 16 | self.radius = radius 17 | 18 | def area(self): 19 | return 3.14 * self.radius ** 2 20 | 21 | 22 | class Square(Shape): 23 | def __init__(self, name, side_length): 24 | super().__init__(name) 25 | self.side_length = side_length 26 | 27 | def area(self): 28 | return self.side_length ** 2 29 | 30 | 31 | class AreaCalculator: 32 | def __init__(self, shape): 33 | self.shape = shape 34 | 35 | def __call__(self): 36 | return self.shape.area() 37 | 38 | 39 | # Create instances of Circle and Square 40 | circle = Circle("Circle", 5) 41 | square = Square("Square", 4) 42 | 43 | # Create instances of AreaCalculator with Circle and Square objects 44 | circle_calculator = AreaCalculator(circle) 45 | square_calculator = AreaCalculator(square) 46 | 47 | # Call the instances of AreaCalculator as if they were functions to calculate the area 48 | circle_area = circle_calculator() 49 | square_area = square_calculator() 50 | 51 | # Output the results 52 | print("Area of", circle.name, ":", circle_area) 53 | print("Area of", square.name, ":", square_area) 54 | -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/01_tightly_coupled_code/main.py: -------------------------------------------------------------------------------- 1 | class TaxCalculator: 2 | def __init__(self, taxable_income: float): 3 | self.__taxable_income = taxable_income 4 | 5 | def calculate_tax(self) -> float: 6 | return self.__taxable_income * 0.3 7 | 8 | class TaxReport: 9 | def __init__(self): 10 | self.__calculator = TaxCalculator(1000000) 11 | 12 | def show(self): 13 | tax = self.__calculator.calculate_tax() 14 | print(tax) 15 | 16 | # Create an instance of TaxReport and show the tax 17 | report = TaxReport() 18 | report.show() 19 | -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/02_creating_an_interface/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class TaxCalculator(ABC): 4 | @abstractmethod 5 | def calculate_tax(self) -> float: 6 | pass 7 | 8 | class TaxCalculator24(TaxCalculator): 9 | def __init__(self, taxable_income: float): 10 | self.taxable_income = taxable_income 11 | 12 | def calculate_tax(self) -> float: 13 | return self.taxable_income * 0.3 14 | 15 | class TaxReport: 16 | def __init__(self, calculator: TaxCalculator): 17 | self.calculator = calculator 18 | 19 | def show(self): 20 | tax = self.calculator.calculate_tax() 21 | print(f"The calculated tax is: {tax}") 22 | 23 | # Example usage: 24 | taxable_income = 1000000 25 | calculator = TaxCalculator24(taxable_income) 26 | report = TaxReport(calculator) 27 | report.show() 28 | -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/04_what_is_dependency_injection/01_constructor_injection/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class TaxCalculator(ABC): 4 | @abstractmethod 5 | def calculate_tax(self) -> float: 6 | pass 7 | 8 | class TaxCalculator24(TaxCalculator): 9 | def __init__(self, taxable_income: float): 10 | self.__taxable_income = taxable_income 11 | 12 | def calculate_tax(self) -> float: 13 | return self.__taxable_income * 0.3 14 | 15 | class TaxReport: 16 | def __init__(self, calculator: TaxCalculator24): 17 | self.__calculator = calculator 18 | 19 | def show(self): 20 | tax = self.__calculator.calculate_tax() 21 | print(tax) 22 | 23 | # Example usage 24 | if __name__ == "__main__": 25 | calculator = TaxCalculator24(100000) 26 | report = TaxReport(calculator) 27 | report.show() 28 | -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/04_what_is_dependency_injection/02_setter_injection/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class TaxCalculator(ABC): 5 | @abstractmethod 6 | def calculate_tax(self) -> float: 7 | pass 8 | 9 | 10 | class TaxCalculator24(TaxCalculator): 11 | def __init__(self, taxable_income: float): 12 | self.__taxable_income = taxable_income 13 | 14 | def calculate_tax(self) -> float: 15 | return self.__taxable_income * 0.3 16 | 17 | 18 | class TaxCalculator23(TaxCalculator): 19 | def calculate_tax(self) -> float: 20 | return 0.0 21 | 22 | 23 | class TaxReport: 24 | def __init__(self, calculator: TaxCalculator): 25 | self.__calculator = calculator 26 | 27 | def set_calculator(self, calculator: TaxCalculator): 28 | self.__calculator = calculator 29 | 30 | def show(self): 31 | tax = self.__calculator.calculate_tax() 32 | print(tax) 33 | 34 | 35 | calculator = TaxCalculator24(100000) 36 | report = TaxReport(calculator) 37 | report.show() 38 | 39 | report.set_calculator(TaxCalculator23()) 40 | report.show() 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/04_what_is_dependency_injection/03_method_injection/main.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class TaxCalculator(ABC): 4 | @abstractmethod 5 | def calculate_tax(self) -> float: 6 | pass 7 | 8 | class TaxCalculator24(TaxCalculator): 9 | def __init__(self, taxable_income: float): 10 | self.__taxable_income = taxable_income 11 | 12 | def calculate_tax(self) -> float: 13 | return self.__taxable_income * 0.3 14 | 15 | class TaxCalculator23(TaxCalculator): 16 | def calculate_tax(self) -> float: 17 | return 0.0 18 | 19 | class TaxReport: 20 | def __init__(self): 21 | self.__calculator = None 22 | 23 | def show(self, calculator: TaxCalculator): 24 | tax = calculator.calculate_tax() 25 | print(tax) 26 | 27 | calculator = TaxCalculator24(100000) 28 | report = TaxReport() 29 | report.show(calculator) 30 | report.show(TaxCalculator23()) 31 | -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/06_Project_Video/01_mytube_video_platform/main.py: -------------------------------------------------------------------------------- 1 | class User: 2 | def __init__(self, email): 3 | self.__email = email 4 | 5 | def get_email(self): 6 | return self.__email 7 | 8 | def set_email(self, email): 9 | self.__email = email 10 | 11 | 12 | class Video: 13 | def __init__(self): 14 | self.__file_name = None 15 | self.__title = None 16 | self.__user = None 17 | 18 | def get_file_name(self): 19 | return self.__file_name 20 | 21 | def set_file_name(self, file_name): 22 | self.__file_name = file_name 23 | 24 | def get_title(self): 25 | return self.__title 26 | 27 | def set_title(self, title): 28 | self.__title = title 29 | 30 | def get_user(self): 31 | return self.__user 32 | 33 | def set_user(self, user): 34 | self.__user = user 35 | 36 | 37 | class VideoDatabase: 38 | def store(self, video): 39 | print("Storing video metadata in a SQL database...") 40 | print("Title:", video.get_title()) 41 | print("File Name:", video.get_file_name()) 42 | print("Done!\n") 43 | 44 | 45 | class VideoEncoder: 46 | def encode(self, video): 47 | print("Encoding video...") 48 | print("Done!\n") 49 | 50 | 51 | class VideoProcessor: 52 | def process(self, video): 53 | encoder = VideoEncoder() 54 | encoder.encode(video) 55 | 56 | database = VideoDatabase() 57 | database.store(video) 58 | 59 | email_service = EmailService() 60 | email_service.send_email(video.get_user()) 61 | 62 | 63 | class EmailService: 64 | def send_email(self, user): 65 | print("Notifying", user.get_email(), "...") 66 | print("Done!\n") 67 | 68 | 69 | video = Video() 70 | video.set_file_name("birthday.mp4") 71 | video.set_title("Jennifer's birthday") 72 | video.set_user(User("john@domain.com")) 73 | 74 | processor = VideoProcessor() 75 | processor.process(video) 76 | -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/06_Project_Video/01_mytube_video_platform/readme.md: -------------------------------------------------------------------------------- 1 | This code exhibits tight coupling because of the direct dependencies between classes without abstraction layers or interfaces. Tight coupling occurs when classes are highly dependent on each other and changes in one class require corresponding changes in other classes. Here's how the code demonstrates tight coupling: 2 | 3 | 1. **Direct Class Instantiation**: In the `VideoProcessor` class, instances of `VideoEncoder`, `VideoDatabase`, and `EmailService` are directly instantiated using the `VideoProcessor` class. This means `VideoProcessor` is tightly coupled to these concrete classes, making it difficult to replace them or extend functionality without modifying `VideoProcessor`. 4 | 5 | 2. **Dependency in Method Calls**: In the `VideoProcessor` class, the `process` method directly calls methods from `VideoEncoder`, `VideoDatabase`, and `EmailService` classes. This directly couples `VideoProcessor` to specific implementations of these functionalities, making it less flexible and harder to maintain. 6 | 7 | 3. **Getter and Setter Methods**: The `Video` class exposes getter and setter methods for its properties (`file_name`, `title`, `user`). While not inherently bad, these methods can contribute to tight coupling if multiple classes access and modify the internal state of `Video` directly. 8 | 9 | 4. **Concrete Class Dependencies**: `VideoProcessor` directly depends on concrete implementations of `VideoEncoder`, `VideoDatabase`, and `EmailService`. If we want to change any of these dependencies or add new ones, we would need to modify the `VideoProcessor` class, violating the Open/Closed Principle. 10 | 11 | To reduce coupling and increase flexibility, we could introduce abstractions such as interfaces or abstract classes, and use dependency injection to provide implementations at runtime. This way, classes would depend on abstractions rather than concrete implementations, allowing for easier changes and extensions without modifying existing code. -------------------------------------------------------------------------------- /02_object_oriented_programming/04_interfaces/06_Project_Video/readme.md: -------------------------------------------------------------------------------- 1 | # Project Video Platform 2 | 3 | We will start to decouple to avoid dependencies -------------------------------------------------------------------------------- /02_object_oriented_programming/05_exceptions/main.py: -------------------------------------------------------------------------------- 1 | class InsufficientFunds(Exception): 2 | def __init__(self, balance, amount): 3 | self.balance = balance 4 | self.amount = amount 5 | super().__init__(f"Insufficient funds, balance: {balance}, withdraw: {amount}") 6 | 7 | def deficit(self): 8 | return self.amount - self.balance 9 | 10 | 11 | class InvalidTransaction(Exception): 12 | """Raise when an invalid transaction (negative or zero amount) is attempted.""" 13 | def __init__(self, amount): 14 | super().__init__(f"Invalid transaction: amount must be positive, got {amount}") 15 | 16 | 17 | class Bank: 18 | def __init__(self, balance): 19 | if balance < 0: 20 | raise ValueError("Balance cannot be negative.") 21 | self.balance = balance 22 | 23 | def withdraw(self, amount): 24 | if amount <= 0: 25 | raise InvalidTransaction(amount) 26 | if amount > self.balance: 27 | raise InsufficientFunds(self.balance, amount) 28 | self.balance -= amount 29 | return f"Withdrew ${amount}, remaining balance is ${self.balance}" 30 | 31 | def deposit(self, amount): 32 | if amount <= 0: 33 | raise InvalidTransaction(amount) 34 | self.balance += amount 35 | return f"Deposited ${amount}, total balance is ${self.balance}" 36 | 37 | def __str__(self): 38 | return f"BankAccount(balance=${self.balance})" 39 | 40 | 41 | # ✅ Testing the improved Bank class 42 | account = Bank(5000) 43 | print(account) 44 | 45 | print(account.deposit(1000)) # ✅ Valid deposit 46 | print(account) 47 | 48 | print(account.withdraw(2000)) # ✅ Valid withdrawal 49 | print(account) 50 | 51 | try: 52 | account.withdraw(6000) # ❌ Insufficient funds 53 | except InsufficientFunds as e: 54 | print(e) 55 | print(f"Deficit: ${e.deficit()}") 56 | 57 | try: 58 | account.deposit(-500) # ❌ Invalid deposit 59 | except InvalidTransaction as e: 60 | print(e) 61 | 62 | try: 63 | account.withdraw(-100) # ❌ Invalid withdrawal 64 | except InvalidTransaction as e: 65 | print(e) 66 | 67 | print(account.deposit(2000)) # ✅ Valid deposit 68 | print(account) 69 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/02_object_oriented_programming/08_lambda_expression/cart/__init__.py -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class CartConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'cart' 7 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/static/script.js: -------------------------------------------------------------------------------- 1 | // Wait for the page to fully load 2 | document.addEventListener('DOMContentLoaded', function () { 3 | // Select all checkboxes for products and the element to display the total 4 | const productCheckboxes = document.querySelectorAll('.product-checkbox'); 5 | const totalDisplay = document.getElementById('total'); 6 | 7 | // Add event listener to each checkbox to update the total when clicked 8 | for (let i = 0; i < productCheckboxes.length; i++) { 9 | productCheckboxes[i].addEventListener('change', calculateAndUpdateTotal); 10 | } 11 | 12 | // Function to calculate and update the total cost based on selected products 13 | async function calculateAndUpdateTotal() { 14 | // Initialize an empty array to store IDs of selected products 15 | const selectedProductIds = []; 16 | 17 | // Loop through each checkbox to check if it's selected 18 | for (let i = 0; i < productCheckboxes.length; i++) { 19 | if (productCheckboxes[i].checked) { 20 | // If checked, add the product ID to the array 21 | selectedProductIds.push(productCheckboxes[i].getAttribute('data-id')); 22 | } 23 | } 24 | 25 | // Make a request to the server to get the total cost of selected products 26 | try { 27 | const response = await fetch(`/calculate_total?selected_ids[]=` + selectedProductIds.join('&selected_ids[]='), { 28 | method: 'GET' 29 | }); 30 | 31 | const data = await response.json(); // Parse the response as JSON 32 | // Update the total display element with the calculated total from the server 33 | totalDisplay.textContent = data.total; 34 | } catch (error) { 35 | console.error('Error fetching total:', error); 36 | } 37 | } 38 | }); 39 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/static/styles.css: -------------------------------------------------------------------------------- 1 | /* styles.css */ 2 | body { 3 | font-family: Arial, sans-serif; 4 | background-color: #f4f4f4; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .container { 10 | width: 90%; 11 | max-width: 600px; 12 | margin: 50px auto; 13 | background: #fff; 14 | padding: 20px; 15 | box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); 16 | border-radius: 10px; 17 | } 18 | 19 | h1 { 20 | color: #333; 21 | text-align: center; 22 | } 23 | 24 | .products, .cart { 25 | margin-top: 20px; 26 | } 27 | 28 | .product { 29 | display: flex; 30 | align-items: center; 31 | margin-bottom: 10px; 32 | } 33 | 34 | .product-checkbox { 35 | margin-right: 10px; 36 | } 37 | 38 | .cart { 39 | border-top: 1px solid #ddd; 40 | padding-top: 10px; 41 | text-align: center; 42 | } 43 | 44 | #total { 45 | font-weight: bold; 46 | font-size: 1.5em; 47 | color: #0b57d0; 48 | } 49 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/templates/product.html: -------------------------------------------------------------------------------- 1 | 2 | {% load static %} 3 | 4 | 5 | 6 | 7 | Shopping Cart 8 | 9 | 10 | 11 |
12 |

🛒 Product Selection

13 | 14 |
15 | {% for product in products %} 16 |
17 | 18 | {{ product.name }} - Rs. {{ product.price }} 19 |
20 | {% endfor %} 21 |
22 | 23 |
24 |

Cart Total

25 |

Total: Rs. 0

26 |
27 |
28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/urls.py: -------------------------------------------------------------------------------- 1 | # urls.py 2 | from django.urls import path 3 | from . import views 4 | 5 | urlpatterns = [ 6 | path('', views.products_view, name='products'), 7 | path('calculate_total/', views.calculate_total, name='calculate_total'), 8 | ] 9 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/cart/views.py: -------------------------------------------------------------------------------- 1 | # views.py 2 | from django.shortcuts import render 3 | from django.http import JsonResponse 4 | 5 | # Sample products 6 | PRODUCTS = [ 7 | {'id': 1, 'name': 'Product A', 'price': 150}, 8 | {'id': 2, 'name': 'Product B', 'price': 250}, 9 | {'id': 3, 'name': 'Product C', 'price': 100}, 10 | ] 11 | 12 | def products_view(request): 13 | return render(request, 'product.html', {'products': PRODUCTS}) 14 | 15 | def calculate_total(request): 16 | # Receive selected product IDs and calculate the total 17 | selected_ids = request.GET.getlist('selected_ids[]') 18 | total = sum((lambda p: p['price'])(p) for p in PRODUCTS if str(p['id']) in selected_ids) 19 | return JsonResponse({'total': total}) 20 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/ecommerce/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/02_object_oriented_programming/08_lambda_expression/ecommerce/__init__.py -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/ecommerce/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for ecommerce project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/ecommerce/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for ecommerce project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/5.1/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.contrib import admin 18 | from django.urls import path, include 19 | 20 | urlpatterns = [ 21 | path('admin/', admin.site.urls), 22 | path('', include('cart.urls')), 23 | ] 24 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/ecommerce/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for ecommerce project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /02_object_oriented_programming/08_lambda_expression/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'ecommerce.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /02_object_oriented_programming/10_python_types/01_typing_system_in_python/main.py: -------------------------------------------------------------------------------- 1 | # Trying to add a list and a dictionary will raise an error. 2 | # print([] + {}) 3 | # Output: TypeError: can only concatenate list (not "dict") to list 4 | 5 | # def process_age(age): 6 | # return age + 1 7 | 8 | # Using a string accidentally 9 | # print(process_age("25")) 10 | # Output: TypeError: can only concatenate str (not "int") to str 11 | 12 | 13 | from typing import Union, List 14 | 15 | def parse_input(data: Union[str, List[int]]) -> List[str]: 16 | if isinstance(data, str): 17 | return data.split(',') 18 | elif isinstance(data, list): 19 | return [str(item) for item in data] 20 | else: 21 | raise ValueError("Unsupported data type!") 22 | 23 | # This function can handle both strings and lists 24 | print(parse_input("apple,banana,cherry")) # Output: ['apple', 'banana', 'cherry'] 25 | print(parse_input([1, 2, 3])) # Output: ['1', '2', '3'] 26 | print(parse_input(123)) # Output: ValueError: Unsupported data type! 27 | -------------------------------------------------------------------------------- /02_object_oriented_programming/10_python_types/01_typing_system_in_python/script.js: -------------------------------------------------------------------------------- 1 | console.log([] + {}); // Output: "[object Object]" 2 | console.log({} + []); // Output: 0 3 | -------------------------------------------------------------------------------- /02_object_oriented_programming/10_python_types/02_understanding_ducky_typing/main.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable 2 | def print_items(items: Iterable): 3 | for item in items: 4 | print(item) 5 | print(" ",end='') 6 | 7 | print_items([1, 2, 3]) # List input 8 | print_items({4, 5, 6}) # Set input 9 | print_items({"A": 1, "B": 2}) # Dictionary input 10 | 11 | # This will raise an error because an integer is not iterable. 12 | # print_items(5) 13 | # Output: TypeError: 'int' object is not iterable 14 | 15 | def double_value(value): 16 | return value + value 17 | 18 | print(double_value(5)) # Output: 10 19 | print(double_value("abc")) # Output: abcabc 20 | print(double_value([1, 2, 3])) # Output: [1, 2, 3, 1, 2, 3] 21 | -------------------------------------------------------------------------------- /02_object_oriented_programming/10_python_types/main.py: -------------------------------------------------------------------------------- 1 | from ctypes import string_at 2 | from sys import getsizeof 3 | from binascii import hexlify 4 | 5 | a = 0b01010000_01000001_01010100 # Binary number 6 | print(a) # Output: 5259604 7 | 8 | text = "PAT" 9 | print(string_at(id(text), getsizeof(text))) 10 | print(hexlify(string_at(id(text), getsizeof(text)))) 11 | -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/01_type_annotations/main.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | import random 3 | 4 | def schedule_restaurant_open(open_time: datetime, workers_needed: int) -> None: 5 | available_workers: list[Worker] = find_workers_available_for_time(open_time) 6 | if len(available_workers) < workers_needed: 7 | print("Not enough workers available.") 8 | else: 9 | # Use random.sample to pick the required number of workers 10 | for worker in random.sample(available_workers, workers_needed): 11 | worker.schedule(open_time) 12 | 13 | class Worker: 14 | def __init__(self, name: str): 15 | self.name = name 16 | 17 | def schedule(self, time: datetime) -> None: 18 | print(f"Worker {self.name} scheduled at {time}.") 19 | 20 | def find_workers_available_for_time(open_time: datetime) -> list[Worker]: 21 | return [Worker("Alice"), Worker("Bob"), Worker("Charlie")] 22 | 23 | # Call the function 24 | schedule_restaurant_open(datetime(2024, 10, 9, 9, 0), 2) 25 | -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/02_benefits_of_type_annotations/main.py: -------------------------------------------------------------------------------- 1 | # Function to double values and add to the list 2 | def add_doubled_values(my_list: list[int]) -> None: 3 | my_list.update([x * 2 for x in my_list]) # Correct method: extend 4 | 5 | # Function call 6 | numbers = [1, 2, 3] 7 | add_doubled_values(numbers) 8 | print(numbers) # Output: [1, 2, 3, 2, 4, 6] 9 | -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/04_optional_type/failure.py: -------------------------------------------------------------------------------- 1 | def get_user_age(name): 2 | user_data = database_lookup(name) 3 | return user_data.age # What if user_data is None? 4 | 5 | 6 | def database_lookup(name): 7 | return None # Simulate a database lookup failure 8 | 9 | 10 | get_user_age("Alice") # AttributeError: 'NoneType' object has no attribute 'age' -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/04_optional_type/main.py: -------------------------------------------------------------------------------- 1 | def parse_integer(s: str) -> int | None: 2 | try: 3 | return int(s) 4 | except ValueError: 5 | return None 6 | 7 | def process_number(s: str) -> None: 8 | number = parse_integer(s) 9 | if number is not None: 10 | print(f"The number multiplied by 2 is {number * 2}") 11 | else: 12 | print(f"Invalid integer: '{s}'") 13 | 14 | if __name__ == "__main__": 15 | process_number("42") # Valid integer 16 | process_number("not a number") # Invalid integer -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/04_optional_type/pizza_making_with_return.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def cook_1(): 4 | """First cook: Prepares the pizza dough and adds sauce.""" 5 | steps = [] 6 | steps.append("🥖 Cook 1: Preparing the pizza dough...") 7 | time.sleep(1) # Simulating time delay 8 | steps.append("🍅 Cook 1: Adding tomato sauce...") 9 | time.sleep(1) 10 | return steps # Returning progress 11 | 12 | def cook_2(previous_steps): 13 | """Second cook: Adds toppings and preheats the oven.""" 14 | steps = previous_steps # Continuing from previous steps 15 | steps.append("🧀 Cook 2: Adding cheese and toppings...") 16 | time.sleep(1) 17 | steps.append("🔥 Cook 2: Preheating the oven...") 18 | time.sleep(1) 19 | return steps # Returning progress 20 | 21 | def cook_3(previous_steps): 22 | """Third cook: Bakes the pizza and serves it.""" 23 | steps = previous_steps # Continuing from previous steps 24 | steps.append("🍕 Cook 3: Placing the pizza in the oven...") 25 | time.sleep(1) 26 | steps.append("⏳ Cook 3: Waiting for the pizza to bake...") 27 | time.sleep(1) 28 | steps.append("✅ Cook 3: Pizza is ready to serve! 🍽️") 29 | return steps # Final result 30 | 31 | # Simulating the cooking process 32 | steps_1 = cook_1() # Cook 1 starts 33 | steps_2 = cook_2(steps_1) # Cook 2 continues 34 | final_steps = cook_3(steps_2) # Cook 3 finishes 35 | 36 | # Display the step-by-step process 37 | for step in final_steps: 38 | print(step) 39 | -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/04_optional_type/user.py: -------------------------------------------------------------------------------- 1 | class UserData: 2 | def __init__(self, name: str, age: int): 3 | self.name = name 4 | self.age = age 5 | 6 | 7 | # Mock database as a dictionary 8 | database = { 9 | "Muhammad Hashim": UserData("Muhammad Hashim", 25), 10 | "Alice": UserData("Alice", 30), 11 | "Bob": UserData("Bob", 28), 12 | } 13 | 14 | def database_lookup(name: str) -> UserData | None: 15 | return database.get(name) 16 | 17 | 18 | def get_user_age(name: str) -> int | None: 19 | user_data = database_lookup(name) 20 | if user_data is not None: 21 | return user_data.age 22 | return None 23 | 24 | 25 | def display_user_age(name: str): 26 | age = get_user_age(name) 27 | if age is not None: 28 | print(f"{name} is {age} years old. 🎉") 29 | else: 30 | print(f"User '{name}' not found. 😢") 31 | 32 | 33 | def main(): 34 | display_user_age("Muhammad Hashim") 35 | display_user_age("Alice") 36 | display_user_age("Unknown User") 37 | 38 | if __name__ == "__main__": 39 | main() 40 | 41 | -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/05_union_type/main.py: -------------------------------------------------------------------------------- 1 | from typing import Union 2 | 3 | def parse_user_input(value: str) -> int | str: 4 | if value.isdigit(): 5 | return int(value) 6 | return value 7 | 8 | result1: Union[int, str] = parse_user_input("hello") 9 | print(f'Result1 type is {type(result1)} and its value is {result1}') # Output: "hello" (as a str) 10 | result2: Union[int, str] = parse_user_input("42") 11 | print(f'Result2 type is {type(result2)} and its value is {result2}') # Output: "hello" (as a str) -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/06_literals/main.py: -------------------------------------------------------------------------------- 1 | from typing import Literal 2 | def process_payment(method: Literal["Credit Card", "PayPal", "Crypto"]) -> str: 3 | return f"Processing {method} payment." 4 | 5 | # Valid Examples 6 | print(process_payment("Credit Card")) # ✅ 7 | print(process_payment("PayPal")) # ✅ 8 | 9 | # Invalid Example 10 | print(process_payment("Cash")) # ❌ Type error 11 | -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/07_annotated/main.py: -------------------------------------------------------------------------------- 1 | from typing import Annotated 2 | 3 | class StringLength: 4 | def __init__(self, min_len: int, max_len: int): 5 | self.min_len = min_len 6 | self.max_len = max_len 7 | 8 | username: Annotated[str, StringLength(5, 15)] = "Hi" # Invalid, too short 9 | 10 | def validate_annotated(var, metadata) -> None: 11 | if isinstance(metadata, StringLength): 12 | if not (metadata.min_len <= len(var) <= metadata.max_len): 13 | raise ValueError(f"Length of '{var}' is not between {metadata.min_len} and {metadata.max_len}.") 14 | 15 | # Validation 16 | try: 17 | validate_annotated(username, StringLength(5, 15)) 18 | print(f"'{username}' is valid!") 19 | except ValueError as e: 20 | print(e) 21 | -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/08_newtype/main.py: -------------------------------------------------------------------------------- 1 | from typing import NewType 2 | 3 | # Document class define kar rahe hain jo content aur review status ko handle karti hai 4 | class Document: 5 | def __init__(self, content: str): 6 | self.content = content 7 | self.reviewed = False 8 | 9 | def mark_reviewed(self): 10 | # Yeh method document ko reviewed mark kar deta hai 11 | self.reviewed = True 12 | 13 | def is_reviewed(self) -> bool: 14 | # Yeh check karta hai ke document reviewed hai ya nahi 15 | return self.reviewed 16 | 17 | # `ReadyToPublishDocument` aik naya type banate hain jo `Document` par base hai 18 | ReadyToPublishDocument = NewType('ReadyToPublishDocument', Document) 19 | 20 | # Yeh function sirf `ReadyToPublishDocument` ko accept karega 21 | def publish_document(doc: ReadyToPublishDocument): 22 | # Document ko publish karta hai 23 | print(f"Publishing document: {doc.content}") 24 | 25 | # Document ko review ke baad `ReadyToPublishDocument` mein convert karne ka function 26 | def prepare_for_publishing(doc: Document) -> ReadyToPublishDocument: 27 | # Ensure karte hain ke document reviewed hai 28 | assert doc.is_reviewed(), "Document must be reviewed before publishing" 29 | return ReadyToPublishDocument(doc) 30 | 31 | # Example workflow 32 | if __name__ == "__main__": 33 | # Ek naya draft document banate hain 34 | draft_doc = Document("Yeh aik draft content hai.") 35 | print("Initial Document:", draft_doc.content) # Output: Draft document ka content 36 | 37 | 38 | draft_doc.mark_reviewed() 39 | ready_doc = prepare_for_publishing(draft_doc) # `ReadyToPublishDocument` mein convert 40 | 41 | publish_document(ready_doc) -------------------------------------------------------------------------------- /02_object_oriented_programming/11_type_annotations/09_final/main.py: -------------------------------------------------------------------------------- 1 | from typing import Final, Tuple 2 | 3 | DB_HOST: Final = "localhost" 4 | 5 | 6 | 7 | def connect_to_test_database(): 8 | global DB_HOST 9 | DB_HOST = "test-db.example.com" # Accidentally reassigning DB_HOST for a test database 10 | # Proceed with test connection... 11 | 12 | # CONFIG: Final[Tuple[str, str]] = ("debug_mode_off", "version_1.0") 13 | 14 | # Attempting to change CONFIG or its content will raise an error with `mypy` 15 | # CONFIG[0] = "debug_mode_on" # Type error: Tuples are immutable -------------------------------------------------------------------------------- /02_object_oriented_programming/12_collection_types/01_annotated_collections/main.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Dict 3 | 4 | @dataclass 5 | class Item: 6 | name: str # Name of the item (e.g., "Laptop") 7 | quantity: int # Quantity of the item ordered 8 | price_per_unit: float # Price per unit of the item 9 | 10 | discounts: Dict[str, float] = { 11 | "Laptop": 0.10, # 10% discount on Laptop 12 | "Keyboard": 0.05, # 5% discount on Keyboard 13 | } 14 | order_items = [ 15 | Item(name="Laptop", quantity=1, price_per_unit=1500.0), 16 | Item(name="Mouse", quantity=2, price_per_unit=25.0), 17 | Item(name="Keyboard", quantity=1, price_per_unit=75.0), 18 | ] 19 | 20 | 21 | def calculate_order_total_with_discounts(items: List[Item], discounts: Dict[str, float]) -> float: 22 | total = 0.0 23 | for item in items: 24 | discount = discounts.get(item.name, 0.0) # Default discount is 0 if not found 25 | total += item.quantity * item.price_per_unit * (1 - discount) 26 | return total 27 | 28 | 29 | total_price_with_discounts = calculate_order_total_with_discounts(order_items, discounts) 30 | print(f"Total Price with Discounts: ${total_price_with_discounts:.2f}") # Output: $1542.50 31 | 32 | -------------------------------------------------------------------------------- /02_object_oriented_programming/12_collection_types/02_homogenous_and_hetrogenous_collections/main.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | from typing import List, Union 3 | 4 | # Step 1: Define Data Classes 5 | @dataclass 6 | class Book: 7 | title: str 8 | author: str 9 | isbn: str 10 | 11 | @dataclass 12 | class Magazine: 13 | title: str 14 | issue_number: int 15 | publisher: str 16 | 17 | @dataclass 18 | class DVD: 19 | title: str 20 | director: str 21 | duration_minutes: int 22 | 23 | # Step 2: Define Type Aliases 24 | MediaItem = Union[Book, Magazine, DVD] 25 | 26 | # Step 3: Create Homogeneous Collections 27 | books: List[Book] = [ 28 | Book(title="1984", author="George Orwell", isbn="1234567890"), 29 | Book(title="To Kill a Mockingbird", author="Harper Lee", isbn="0987654321"), 30 | ] 31 | 32 | magazines: List[Magazine] = [ 33 | Magazine(title="National Geographic", issue_number=202, publisher="National Geographic Society"), 34 | Magazine(title="TIME", issue_number=105, publisher="Time USA, LLC"), 35 | ] 36 | 37 | dvds: List[DVD] = [ 38 | DVD(title="Inception", director="Christopher Nolan", duration_minutes=148), 39 | DVD(title="The Matrix", director="Lana Wachowski", duration_minutes=136), 40 | ] 41 | 42 | # Step 4: Create a Heterogeneous Collection 43 | library_inventory: List[MediaItem] = books + magazines + dvds 44 | 45 | # Step 5: Function to Display Media Information 46 | def display_media_info(media: MediaItem): 47 | if isinstance(media, Book): 48 | print(f"Book: {media.title} by {media.author}, ISBN: {media.isbn}") 49 | elif isinstance(media, Magazine): 50 | print(f"Magazine: {media.title}, Issue: {media.issue_number}, Publisher: {media.publisher}") 51 | elif isinstance(media, DVD): 52 | print(f"DVD: {media.title} directed by {media.director}, Duration: {media.duration_minutes} minutes") 53 | 54 | # Step 6: Display Information for All Items in the Library 55 | for item in library_inventory: 56 | display_media_info(item) 57 | -------------------------------------------------------------------------------- /02_object_oriented_programming/12_collection_types/03_typed_dict/main.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict, Optional, Union 2 | 3 | class Preferences(TypedDict): 4 | theme: str 5 | notifications: bool 6 | 7 | class UserProfile(TypedDict): 8 | username: str 9 | email: str 10 | age: int 11 | preferences: Preferences 12 | 13 | class ProfileUpdates(TypedDict, total=False): 14 | username: Optional[str] 15 | email: Optional[str] 16 | age: Optional[int] # Ensure `age` is expected to be an int 17 | preferences: Optional[Preferences] 18 | 19 | def update_user_profile(profile: UserProfile, updates: ProfileUpdates) -> UserProfile: 20 | for key, value in updates.items(): 21 | if key in profile: 22 | profile[key] = value 23 | else: 24 | raise KeyError(f"Invalid key: {key}") 25 | return profile 26 | 27 | # Correct Usage 28 | user_profile: UserProfile = { 29 | "username": "MuhammadHashim", 30 | "email": "hashim@example.com", 31 | "age": 24, 32 | "preferences": {"theme": "dark", "notifications": True} 33 | } 34 | 35 | # Now if "age" is a string, mypy will detect this as an error 36 | updates: ProfileUpdates = {"age": "twenty-five", "preferences": {"theme": "light", "notifications": False}} 37 | 38 | updated_profile = update_user_profile(user_profile, updates) 39 | print(updated_profile) 40 | -------------------------------------------------------------------------------- /02_object_oriented_programming/12_collection_types/03_typed_dict/typedict.py: -------------------------------------------------------------------------------- 1 | from typing import TypedDict 2 | 3 | class Preferences(TypedDict): 4 | theme: str 5 | notifications: bool 6 | 7 | class UserProfile(TypedDict): 8 | username: str 9 | email: str 10 | age: int 11 | preferences: Preferences 12 | 13 | 14 | class ProfileUpdates(TypedDict, total=False): 15 | username: str | None 16 | email: str | None 17 | age: int | None 18 | preferences: Preferences | None 19 | 20 | 21 | def update_user_profile(profile: UserProfile, updates: ProfileUpdates) -> UserProfile: 22 | if "username" in updates and updates["username"] is not None: 23 | profile["username"] = updates["username"] 24 | if "email" in updates and updates["email"] is not None: 25 | profile["email"] = updates["email"] 26 | if "age" in updates and updates["age"] is not None: 27 | profile["age"] = updates["age"] 28 | if "preferences" in updates and updates["preferences"] is not None: 29 | profile["preferences"] = updates["preferences"] 30 | 31 | return profile 32 | 33 | # Define a correct user profile 34 | user_profile: UserProfile = { 35 | "username": "MuhammadHashim", 36 | "email": "hashim@example.com", 37 | "age": 24, 38 | "preferences": {"theme": "dark", "notifications": True} 39 | } 40 | 41 | # Define updates with an incorrect type for `age` 42 | updates: ProfileUpdates = {"age": 25, "preferences": {"theme": "light", "notifications": False}} 43 | 44 | # Apply updates (mypy will flag this as an error due to incorrect type for `age`) 45 | updated_profile = update_user_profile(user_profile, updates) 46 | print(updated_profile) 47 | -------------------------------------------------------------------------------- /02_object_oriented_programming/13_customizing_typechecker/01_configuring_type_checker/main.py: -------------------------------------------------------------------------------- 1 | def add_numbers(a, b): 2 | return a + b 3 | 4 | result = add_numbers(5, 10) # Ab yeh sirf integers accept karega 5 | print(result) 6 | -------------------------------------------------------------------------------- /02_object_oriented_programming/13_customizing_typechecker/mypy.ini: -------------------------------------------------------------------------------- 1 | [mypy] 2 | python_version = 3.12 3 | warn_return_any = True 4 | disallow_untyped_defs = True 5 | 6 | -------------------------------------------------------------------------------- /02_object_oriented_programming/13_customizing_typechecker/readme.md: -------------------------------------------------------------------------------- 1 | # 🛠️ The Secret to Strong, Reliable Codebases 2 | 3 | Welcome to this guide on how **typecheckers** can elevate your coding game! 🎉 Typecheckers offer a unique combination of documentation and validation, making your code not only understandable but also robust. As Jukka Lehtosalo, the creator of `mypy`, puts it: **"A typechecker is verified documentation."** This guide will show you how to make the most of this essential tool! 💼 4 | 5 | 6 | ## 📖 What Are Typecheckers, and Why Do They Matter? 7 | 8 | When writing code, **type annotations** are like labels that describe what each part of your code should do. They act as a guide for yourself and others to understand your intentions. But typecheckers take this further by verifying that your code **actually behaves** as documented. 🧩 9 | 10 | In essence, **typecheckers make sure your annotations match the actual behavior of your code** — catching inconsistencies that could lead to bugs and confusion. By keeping your documentation and code in sync, typecheckers provide an invaluable safeguard in your coding toolkit. 11 | 12 | 13 | ## 🔍 Why Typecheckers Are Invaluable 14 | 15 | Confucius once said, "The mechanic, who wishes to do his work well, must first sharpen his tools." Typecheckers are just that: a tool that polishes your code to a higher standard. 💎 16 | 17 | ### Here’s Why Typecheckers Are a Must-Have: 18 | 1. **Catch Bugs Early**: Typecheckers help spot mistakes at the development stage, saving time and reducing issues later on. 🐞 19 | 2. **Improve Readability**: By enforcing type annotations, your code becomes more understandable to others (and future you!) 📘 20 | 3. **Enhance Documentation**: Typecheckers serve as both documentation and verification, making it easier to trust and maintain your code over time. 📜 21 | 22 | 23 | ## 🔧 Sharpening Your Typechecker Skills 24 | 25 | Don't stop at just learning your editor or operating system—**become an expert in your typechecker** too! 🧰 This guide will walk you through options and configurations that can optimize your typechecker for more comprehensive, reliable checks. 26 | 27 | 🔹 Great coding techniques get you far, but powerful tools like typecheckers take you to the next level. Let’s make sure you’re getting the most out of your typechecker, turning it into an essential ally in your development process. 28 | 29 | -------------------------------------------------------------------------------- /02_object_oriented_programming/14_defining_our_own_type/02_advance_anums/main.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, auto 2 | 3 | class MotherSauce(Enum): 4 | BECHAMEL = auto() 5 | VELOUTE = auto() 6 | ESPAGNOLE = auto() 7 | TOMATO = auto() 8 | HOLLANDAISE = auto() 9 | # Intentional alias 10 | BECHAMEL_ALIAS = BECHAMEL 11 | 12 | # Usage 13 | print(list(MotherSauce)) 14 | -------------------------------------------------------------------------------- /02_object_oriented_programming/14_defining_our_own_type/10_decorator/main.py: -------------------------------------------------------------------------------- 1 | import functools 2 | import time 3 | 4 | def timer(func): 5 | @functools.wraps(func) 6 | def wrapper(*args, **kwargs): 7 | start_time = time.time() 8 | print(f"⏳ Starting '{func.__name__}'") 9 | result = func(*args, **kwargs) 10 | end_time = time.time() 11 | print(f"⏱️ '{func.__name__}' finished in {end_time - start_time:.4f} seconds") 12 | return result 13 | return wrapper 14 | 15 | @timer 16 | def compute_squares(n): 17 | return [i**2 for i in range(n)] 18 | 19 | # Usage 20 | compute_squares(1000000) 21 | -------------------------------------------------------------------------------- /02_object_oriented_programming/14_defining_our_own_type/11_pydantic/main.py: -------------------------------------------------------------------------------- 1 | from pydantic import BaseModel, EmailStr, Field, validator 2 | 3 | class User(BaseModel): 4 | name: str = Field(..., min_length=1) 5 | email: EmailStr 6 | age: int = Field(..., ge=0, le=120) # age >= 0 and <= 120 7 | phone: str 8 | 9 | @validator('phone') 10 | def phone_must_be_digits(cls, v): 11 | if not v.isdigit(): 12 | raise ValueError('Phone number must contain only digits.') 13 | if len(v) != 10: 14 | raise ValueError('Phone number must be exactly 10 digits.') 15 | return v 16 | 17 | def main(): 18 | try: 19 | user = User( 20 | name="Ali Ahmed", 21 | email="ali@example.com", 22 | age=30, 23 | phone="1234567890" 24 | ) 25 | print(user) 26 | except ValidationError as e: 27 | print(e.json()) 28 | 29 | if __name__ == "__main__": 30 | main() -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/01_truth_about_threads/file1.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example Domain 5 | 6 | 7 | 8 | 9 | 36 | 37 | 38 | 39 |
40 |

Example Domain

41 |

This domain is for use in illustrative examples in documents. You may use this 42 | domain in literature without prior coordination or asking for permission.

43 |

More information...

44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/01_truth_about_threads/file2.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example Domain 5 | 6 | 7 | 8 | 9 | 36 | 37 | 38 | 39 |
40 |

Example Domain

41 |

This domain is for use in illustrative examples in documents. You may use this 42 | domain in literature without prior coordination or asking for permission.

43 |

More information...

44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/01_truth_about_threads/file3.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Example Domain 5 | 6 | 7 | 8 | 9 | 36 | 37 | 38 | 39 |
40 |

Example Domain

41 |

This domain is for use in illustrative examples in documents. You may use this 42 | domain in literature without prior coordination or asking for permission.

43 |

More information...

44 |
45 | 46 | 47 | -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/01_truth_about_threads/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import aiohttp 3 | 4 | async def download_file(session, url): 5 | print(f"Starting download from {url}") 6 | async with session.get(url) as response: 7 | content = await response.read() 8 | filename = url.split("/")[-1] 9 | with open(filename, 'wb') as f: 10 | f.write(content) 11 | print(f"Finished downloading {filename}") 12 | 13 | async def main(): 14 | urls = [ 15 | 'https://www.example.com/file1.txt', 16 | 'https://www.example.com/file2.txt', 17 | 'https://www.example.com/file3.txt', 18 | ] 19 | async with aiohttp.ClientSession() as session: 20 | tasks = [download_file(session, url) for url in urls] 21 | await asyncio.gather(*tasks) 22 | 23 | asyncio.run(main()) 24 | -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/02_async_walk/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | import time 3 | from concurrent.futures import ProcessPoolExecutor 4 | 5 | def cpu_bound_task(): 6 | # CPU-bound operation ko simulate karte hain 7 | sum(i * i for i in range(10 ** 7)) 8 | print(f"{time.ctime()} CPU-bound task completed") 9 | 10 | async def main(): 11 | print(f"{time.ctime()} Main coroutine started") 12 | loop = asyncio.get_running_loop() 13 | 14 | # ProcessPoolExecutor create karna 15 | with ProcessPoolExecutor() as executor: 16 | await loop.run_in_executor(executor, cpu_bound_task) 17 | 18 | print(f"{time.ctime()} Main coroutine completed") 19 | 20 | if __name__ == "__main__": 21 | # Event loop ko run karna 22 | asyncio.run(main()) 23 | -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/04_coroutine/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | async def fetch_data(source, delay): 4 | print(f"Fetching data from {source}") 5 | await asyncio.sleep(delay) # Simulate I/O operation 6 | data = f"Data from {source}" 7 | print(f"Finished fetching from {source}") 8 | return data 9 | 10 | async def main(): 11 | # Schedule both fetch_data coroutines concurrently 12 | task1 = asyncio.create_task(fetch_data("Source 1", 2)) 13 | task2 = asyncio.create_task(fetch_data("Source 2", 3)) 14 | 15 | # Await both tasks to complete 16 | results = await asyncio.gather(task1, task2) 17 | 18 | print("Results:") 19 | for result in results: 20 | print(result) 21 | 22 | asyncio.run(main()) 23 | -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/05_event_loop/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | def get_loop(): 4 | loop = asyncio.get_event_loop() 5 | print(f"Event loop: {loop}") 6 | 7 | get_loop() 8 | -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/06_async_context_manager/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | class AsyncConnection: 4 | async def __aenter__(self): 5 | print("🔗 Opening connection...") 6 | await asyncio.sleep(1) # Simulate asynchronous setup 7 | self.connection = "Connected" 8 | print("🔗 Connection opened.") 9 | return self.connection # Resource to be used within the block 10 | 11 | async def __aexit__(self, exc_type, exc, tb): 12 | print("🔗 Closing connection...") 13 | await asyncio.sleep(1) # Simulate asynchronous teardown 14 | self.connection = None 15 | print("🔗 Connection closed.") 16 | 17 | async def main(): 18 | async with AsyncConnection() as conn: 19 | print(f"💬 Using connection: {conn}") 20 | await asyncio.sleep(2) # Simulate using the connection 21 | 22 | asyncio.run(main()) -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/07_async_iterators/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | class AsyncLineReader: 4 | def __init__(self, lines): 5 | self.lines = lines 6 | self.index = 0 7 | 8 | def __aiter__(self): 9 | return self 10 | 11 | async def __anext__(self): 12 | if self.index >= len(self.lines): 13 | raise StopAsyncIteration 14 | await asyncio.sleep(0.5) # Simulate asynchronous I/O operation 15 | line = self.lines[self.index] 16 | self.index += 1 17 | return line 18 | 19 | async def main(): 20 | lines = [ 21 | "🌟 Line 1: Hello, Async World!", 22 | "🚀 Line 2: Asyncio makes concurrency easy.", 23 | "🔍 Line 3: Iterators can be asynchronous." 24 | ] 25 | 26 | async for line in AsyncLineReader(lines): 27 | print(f"📄 Read: {line}") 28 | 29 | asyncio.run(main()) -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/08_async_generators/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | async def get_user_info(user_id): 4 | await asyncio.sleep(1) # Simulate API call delay 5 | return {"id": user_id, "name": f"User{user_id}"} 6 | 7 | async def user_info_generator(user_ids): 8 | for uid in user_ids: 9 | user_info = await get_user_info(uid) 10 | yield user_info 11 | 12 | async def main(): 13 | user_ids = [101, 102, 103] 14 | async for user in user_info_generator(user_ids): 15 | print(f"👤 Retrieved User: {user}") 16 | 17 | asyncio.run(main()) -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/09_async_comprehensions/main.py: -------------------------------------------------------------------------------- 1 | import asyncio 2 | 3 | async def fetch_user_profile(user_id): 4 | await asyncio.sleep(0.2) # Simulate API call delay 5 | return {"id": user_id, "name": f"User{user_id}"} 6 | 7 | async def user_id_generator(): 8 | for uid in range(1, 6): 9 | await asyncio.sleep(0.1) # Simulate data source delay 10 | yield uid 11 | 12 | async def main(): 13 | # Async List Comprehension to fetch user profiles 14 | user_profiles = [await fetch_user_profile(uid) async for uid in user_id_generator()] 15 | print("👥 User Profiles:") 16 | for profile in user_profiles: 17 | print(profile) 18 | 19 | asyncio.run(main()) -------------------------------------------------------------------------------- /02_object_oriented_programming/15_async_programming/10_stating_and_stopping_gracefully/main.py: -------------------------------------------------------------------------------- 1 | # tcp_echo_server.py 2 | import asyncio 3 | 4 | async def handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter): 5 | addr = writer.get_extra_info('peername') 6 | print(f"👤 New connection from {addr}") 7 | try: 8 | while True: 9 | data = await reader.readline() 10 | if not data: 11 | print(f"🔌 Connection closed by {addr}") 12 | break 13 | message = data.decode().strip() 14 | print(f"💬 Received from {addr}: {message}") 15 | response = message.upper() + '\n' 16 | writer.write(response.encode()) 17 | await writer.drain() 18 | except asyncio.CancelledError: 19 | print(f"🚨 Connection with {addr} is being closed gracefully.") 20 | writer.close() 21 | await writer.wait_closed() 22 | raise 23 | except Exception as e: 24 | print(f"❌ Error with {addr}: {e}") 25 | finally: 26 | print(f"🔒 Connection with {addr} has been closed.") 27 | 28 | async def main(host='127.0.0.1', port=8888): 29 | server = await asyncio.start_server(handle_client, host, port) 30 | addr = server.sockets[0].getsockname() 31 | print(f"🚀 Serving on {addr}") 32 | 33 | async with server: 34 | try: 35 | await server.serve_forever() 36 | except asyncio.CancelledError: 37 | print("🛑 Server is shutting down...") 38 | server.close() 39 | await server.wait_closed() 40 | tasks = [task for task in asyncio.all_tasks() if task is not asyncio.current_task()] 41 | print(f"📋 Cancelling {len(tasks)} active tasks...") 42 | for task in tasks: 43 | task.cancel() 44 | await asyncio.gather(*tasks, return_exceptions=True) 45 | print("✅ Shutdown complete.") 46 | 47 | if __name__ == "__main__": 48 | try: 49 | asyncio.run(main()) 50 | except KeyboardInterrupt: 51 | print("✋ Received exit signal. Exiting gracefully...") 52 | -------------------------------------------------------------------------------- /03_data_structures/00_datatype_and_object_in_python/images/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/03_data_structures/00_datatype_and_object_in_python/images/01.jpg -------------------------------------------------------------------------------- /03_data_structures/00_datatype_and_object_in_python/images/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/03_data_structures/00_datatype_and_object_in_python/images/02.jpg -------------------------------------------------------------------------------- /03_data_structures/00_datatype_and_object_in_python/images/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/03_data_structures/00_datatype_and_object_in_python/images/03.jpg -------------------------------------------------------------------------------- /03_data_structures/readme.md: -------------------------------------------------------------------------------- 1 | # **Understanding Data Structures** 💡 2 | 3 | ## 📖 Introduction 📘✨🔍 4 | 5 | Data structures are the **fundamental building blocks** of software development. They define how data is stored, accessed, and organized in the memory of a computer. The choice of an appropriate data structure significantly impacts the performance and efficiency of a program. 🚀 6 | 7 | ## 🏗️ What Are Data Structures? 🏛️ 8 | 9 | Data structures refer to the specialized format for organizing, processing, retrieving, and storing data. They determine how data is arranged in memory and how operations like **insertion, deletion, searching, and updating** are performed. 🔍📂🖥️ 10 | 11 | ### Why Are Data Structures Important? 💡 12 | 13 | - ✅ **Efficient Data Storage:** They optimize memory usage, ensuring that storage is managed effectively. 14 | - ✅ **Fast Data Retrieval:** They enable quick access to data, which enhances the performance of applications. 15 | - ✅ **Optimized Performance:** The right data structure improves the execution speed of algorithms. 16 | - ✅ **Better Data Management:** They provide structured ways to manage large amounts of data. 17 | 18 | ## 🏆 Impact of Data Structures on System Performance 🔄 19 | 20 | The efficiency of a system heavily relies on **how data is structured** and **how efficiently it is retrieved**. If a poor data structure is used, it can lead to: 21 | 22 | - 🐢 **Slow system performance** due to inefficient searching and retrieval. 23 | - 📉 **High memory consumption** as poorly structured data wastes storage space. 24 | - ❌ **Increased computational complexity**, leading to longer execution times. 25 | 26 | ## 💡 Choosing the Right Data Structure 🎯 27 | 28 | **Computer scientists and developers** must carefully choose the appropriate data structure based on the needs of their algorithm. The right data structure ensures: 29 | 30 | - 🚀 **Faster execution times** for applications. 31 | - 🔄 **Seamless data operations** like searching, sorting, and modifying data. 32 | - 📊 **Optimal use of system resources**, including CPU and memory. 33 | 34 | lets dive into the world of data structures and understand the different types of data structures and their applications. 35 | -------------------------------------------------------------------------------- /04_algorithms/01_introducing_algorithm/images/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/01_introducing_algorithm/images/01.jpg -------------------------------------------------------------------------------- /04_algorithms/02_analysis_of_an_algorithm/images/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/02_analysis_of_an_algorithm/images/01.jpg -------------------------------------------------------------------------------- /04_algorithms/02_analysis_of_an_algorithm/images/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/02_analysis_of_an_algorithm/images/02.jpg -------------------------------------------------------------------------------- /04_algorithms/02_analysis_of_an_algorithm/images/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/02_analysis_of_an_algorithm/images/03.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/02_recursion_algorithm/diagram/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/02_recursion_algorithm/diagram/01.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/02_recursion_algorithm/diagram/02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/02_recursion_algorithm/diagram/02.png -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/01.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/02.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/03.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/diagrams/04.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/03_divide_and_conquer/main.py: -------------------------------------------------------------------------------- 1 | # Binary Search Algorithm 2 | def binary_search(array, start, end, target): 3 | while start <= end: 4 | mid = start + (end - start) // 2 5 | if arr[mid] == target: 6 | return arr[mid] 7 | elif arr[mid] < target: 8 | start = mid + 1 9 | else: 10 | end = mid - 1 11 | return -1 12 | 13 | 14 | arr = [4, 6, 9, 13, 14, 18, 21, 24, 38] 15 | target = 90 16 | 17 | result = binary_search(arr, 0, len(arr) - 1, target) 18 | print(f"Element found: {result}") if result != - \ 19 | 1 else print("Element not found") 20 | 21 | # ------------------------------------------------------------------------------------- 22 | 23 | # Merge Sort Algorithm 24 | 25 | def merge_sort(unsorted_list): 26 | if len(unsorted_list) == 1: 27 | return unsorted_list 28 | mid_point = len(unsorted_list) // 2 29 | first_half = unsorted_list[:mid_point] 30 | second_half = unsorted_list[mid_point:] 31 | half_a = merge_sort(first_half) 32 | half_b = merge_sort(second_half) 33 | return merge(half_a, half_b) 34 | 35 | def merge(first_sublist, second_sublist): 36 | i = j = 0 37 | merged_list = [] 38 | while i < len(first_sublist) and j < len(second_sublist): 39 | if first_sublist[i] < second_sublist[j]: 40 | merged_list.append(first_sublist[i]) 41 | i += 1 42 | else: 43 | merged_list.append(second_sublist[j]) 44 | j += 1 45 | while i < len(first_sublist): 46 | merged_list.append(first_sublist[i]) 47 | i += 1 48 | while j < len(second_sublist): 49 | merged_list.append(second_sublist[j]) 50 | j += 1 51 | return merged_list 52 | 53 | a = [11, 12, 7, 41, 61, 13, 16, 14] 54 | print(f'Unsorted List: {a}') 55 | print(f'Sorted List: {merge_sort(a)}') 56 | 57 | 58 | 59 | # Quick Sort 60 | 61 | def quicksort(arr, low, high): 62 | if low < high: 63 | # Partition index find karo 64 | pi = partition(arr, low, high) 65 | 66 | # Left sub-array sort karo 67 | quicksort(arr, low, pi - 1) 68 | # Right sub-array sort karo 69 | quicksort(arr, pi + 1, high) 70 | 71 | def partition(arr, low, high): 72 | # Last element ko pivot chuno 73 | pivot = arr[high] 74 | # Chhote elements ke liye index 75 | i = low - 1 76 | 77 | # Array traverse karo 78 | for j in range(low, high): 79 | # Agar current element pivot se chhota ya barabar hai 80 | if arr[j] <= pivot: 81 | i += 1 # Chhote element ka index increment karo 82 | arr[i], arr[j] = arr[j], arr[i] # Swap karo 83 | 84 | # Pivot ko sahi position par rakho 85 | arr[i + 1], arr[high] = arr[high], arr[i + 1] 86 | return i + 1 # Partition index return karo 87 | 88 | # Test karne ke liye 89 | arr = [10, 7, 8, 9, 1, 5,10, 2, 3, 4, 6, 8] 90 | n = len(arr) 91 | print("Original array:", arr) 92 | quicksort(arr=arr, low=0, high=n-1) 93 | print("Sorted array:", arr) -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/04_dynamic_programming_technique/images/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/04_dynamic_programming_technique/images/01.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/04_dynamic_programming_technique/images/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/04_dynamic_programming_technique/images/02.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/04_dynamic_programming_technique/images/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/04_dynamic_programming_technique/images/03.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/01.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/02.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/03.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/04.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/05.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/06.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/07.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/08.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/09.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HashimThePassionate/Python-Deep-Dive/2258fe18698f626d7752360ebb9231d52006638d/04_algorithms/06_alogorithm_design_techniques_and_strategies/05_greedy_algorithm/images/10.jpg -------------------------------------------------------------------------------- /04_algorithms/06_alogorithm_design_techniques_and_strategies/readme.md: -------------------------------------------------------------------------------- 1 | # **Algorithm Design Techniques and Strategies** 📚 2 | 3 | In the field of computing, **algorithm design** is a fundamental skill for IT professionals, essential for enhancing expertise and enabling career growth. 4 | The algorithm design process begins with a substantial number of real-world computing problems, which must be clearly formulated to efficiently build a solution using appropriate techniques. 5 | 6 | The world of algorithms provides a wide range of techniques and design principles. Mastery of these methods is crucial for tackling complex and sophisticated problems. 7 | Efficient algorithm design is critical in **computer science** for constructing precise and optimal solutions for well-defined problems. 8 | 9 | --- 10 | 11 | ## 🧩 Section Overview 12 | 13 | This Section will cover: 14 | 15 | - Categorization of different kinds of algorithms 16 | - Detailed explanation and illustration of key design techniques 17 | - Analysis of algorithm efficiency and performance 18 | - Implementation of important algorithms 19 | 20 | --- 21 | 22 | ## 🛠️ Core Algorithm Design Techniques 23 | 24 | We will explore the following major algorithm design strategies: 25 | 26 | ### 🔹 1. Divide and Conquer 27 | A technique that breaks a problem into smaller subproblems, solves each subproblem independently, and combines the results to form the solution. 28 | 29 | ### 🔹 2. Dynamic Programming 30 | An approach that solves problems by breaking them down into overlapping subproblems, storing the results of subproblems to avoid redundant computations. 31 | 32 | ### 🔹 3. Greedy Algorithms 33 | A method that builds a solution step-by-step, choosing the locally optimal choice at each step with the aim of reaching a global optimum. 34 | 35 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/01_encapsulate_principle/encapsulate.py: -------------------------------------------------------------------------------- 1 | class PaymentBase: 2 | def __init__(self, amount: int) -> None: 3 | self.amount: int = amount 4 | 5 | def process_payment(self) -> None: 6 | pass 7 | 8 | class CreditCard(PaymentBase): 9 | def process_payment(self) -> None: 10 | print(f"Credit card payment: {self.amount}") 11 | 12 | class PayPal(PaymentBase): 13 | def process_payment(self) -> None: 14 | print(f"PayPal payment: {self.amount}") 15 | 16 | 17 | if __name__ == "__main__": 18 | payments: list[PaymentBase] = [CreditCard(100), PayPal(200)] 19 | for payment in payments: 20 | payment.process_payment() 21 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/01_encapsulate_principle/encapsulate_bis.py: -------------------------------------------------------------------------------- 1 | class Circle: 2 | def __init__(self, radius: int) -> None: 3 | self._radius: int = radius 4 | 5 | @property 6 | def radius(self) -> int: 7 | return self._radius 8 | 9 | @radius.setter 10 | def radius(self, value: int) -> None: 11 | if value < 0: 12 | raise ValueError("Radius cannot be negative!") 13 | self._radius = value 14 | 15 | 16 | if __name__ == "__main__": 17 | circle: Circle = Circle(10) 18 | print(f"Initial radius: {circle.radius}") 19 | circle.radius = 15 20 | print(f"New radius: {circle.radius}") 21 | 22 | try: 23 | circle.radius = -5 24 | except ValueError as e: 25 | print(e) 26 | print(f"Current radius: {circle.radius}") 27 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/02_composition_over_inheritance_principle/composition.py: -------------------------------------------------------------------------------- 1 | class Engine: 2 | def start(self) -> None: 3 | print("Engine started") 4 | 5 | class Car: 6 | def __init__(self) -> None: 7 | self.engine: Engine = Engine() 8 | 9 | def start(self) -> None: 10 | self.engine.start() 11 | print("Car started") 12 | 13 | if __name__ == "__main__": 14 | my_car: Car = Car() 15 | my_car.start() 16 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/02_composition_over_inheritance_principle/computer_composition.py: -------------------------------------------------------------------------------- 1 | class CPU: 2 | def process(self) -> None: 3 | print("CPU is processing data.") 4 | 5 | class RAM: 6 | def load_data(self) -> None: 7 | print("RAM is loading data.") 8 | 9 | class Storage: 10 | def read_data(self) -> None: 11 | print("Storage is reading data.") 12 | 13 | class Computer: 14 | def __init__(self) -> None: 15 | self.cpu: CPU = CPU() 16 | self.ram: RAM = RAM() 17 | self.storage: Storage = Storage() 18 | 19 | def start(self) -> None: 20 | self.cpu.process() 21 | self.ram.load_data() 22 | self.storage.read_data() 23 | print("Computer has started successfully!") 24 | 25 | if __name__ == "__main__": 26 | my_computer: Computer = Computer() 27 | my_computer.start() -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/03_interface_not_implemented_principle/interfaces.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | 4 | class Logger(ABC): 5 | @abstractmethod 6 | def log(self, message: str) -> None: 7 | pass 8 | 9 | 10 | class ConsoleLogger(Logger): 11 | def log(self, message: str) -> None: 12 | print(f"Console: {message}") 13 | 14 | 15 | class FileLogger(Logger): 16 | def log(self, message: str) -> None: 17 | with open("log.txt", "a") as f: 18 | f.write(f"File: {message}\n") 19 | 20 | 21 | def log_message(logger: Logger, message: str) -> None: 22 | logger.log(message) 23 | 24 | if __name__ == "__main__": 25 | log_message(ConsoleLogger(), "A console log.") 26 | log_message(FileLogger(), "A file log.") 27 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/03_interface_not_implemented_principle/interfaces_bis.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol 2 | 3 | class Logger(Protocol): 4 | def log(self, message: str) -> None: 5 | ... 6 | 7 | class ConsoleLogger: 8 | def log(self, message: str) -> None: 9 | print(f"Console: {message}") 10 | 11 | class FileLogger: 12 | def log(self, message: str) -> None: 13 | with open("log.txt", "a") as f: 14 | f.write(f"File: {message}\n") 15 | 16 | def log_message(logger: Logger, message: str) -> None: 17 | logger.log(message) 18 | 19 | 20 | if __name__ == "__main__": 21 | log_message(ConsoleLogger(), "A console log.") 22 | log_message(FileLogger(), "A file log.") 23 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/03_interface_not_implemented_principle/log.txt: -------------------------------------------------------------------------------- 1 | File: A file log. 2 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/04_loose_coupling_principle/loose_coupling.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol 2 | 3 | class Sender(Protocol): 4 | def send(self, message: str) -> None: 5 | ... 6 | 7 | class MessageService: 8 | def __init__(self, sender: Sender) -> None: 9 | self.sender: Sender = sender 10 | 11 | def send_message(self, message: str) -> None: 12 | self.sender.send(message) 13 | 14 | class EmailSender: 15 | def send(self, message: str) -> None: 16 | print(f"Sending email: {message}") 17 | 18 | class SMSSender: 19 | def send(self, message: str) -> None: 20 | print(f"Sending SMS: {message}") 21 | 22 | if __name__ == "__main__": 23 | email_service: MessageService = MessageService(EmailSender()) 24 | email_service.send_message("Hello via Email") 25 | 26 | sms_service: MessageService = MessageService(SMSSender()) 27 | sms_service.send_message("Hello via SMS") 28 | -------------------------------------------------------------------------------- /05_design_patterns/01_foundation_design_principles/readme.md: -------------------------------------------------------------------------------- 1 | # 🌀 **Core Design Principles for Software Development** 🌀 2 | 3 | Welcome! In this section, we will learn **key ideas** that make software easier to build, change, and maintain. 🛠️ These principles will help you create **better applications** that are flexible, reliable, and easy to improve. 🚀 4 | 5 | ## 🗂️ **What Will We Learn?** 6 | 7 | ### 1️⃣ **Encapsulate What Changes** 8 | 💡 Find out how to keep the parts of your code that might change **separate**. This makes it easier to update or add new features without breaking other parts of the app. 9 | ✨ **Result**: Your code will be more organized and easier to update. 10 | 11 | ### 2️⃣ **Prefer Composition Over Inheritance** 12 | 🔧 Learn why it’s better to **combine simple objects** to build complex ones, rather than using inheritance (directly copying behavior). This makes your code more **flexible** and easier to reuse. 13 | ✨ **Result**: You’ll create cleaner and simpler systems. 14 | 15 | ### 3️⃣ **Program to Interfaces, Not Concrete Classes** 16 | 🎭 Discover why coding to **general types** (like blueprints) instead of specific ones is better. This reduces **direct dependencies** and makes your code more flexible. 17 | ✨ **Result**: Your code can handle changes and new features smoothly. 18 | 19 | ### 4️⃣ **Keep Things Loosely Connected** 20 | 🧩 Understand how to reduce **strong links** between parts of your code. This makes it easier to **test, change, and reuse** different parts without breaking everything else. 21 | ✨ **Result**: Your apps will be easier to manage and grow. 22 | 23 | ## 🎯 **What You’ll Gain** 24 | By the end of this section, you’ll know how to: 25 | ✅ Write **organized and flexible code** 26 | ✅ Handle changes without stress 27 | ✅ Build apps that are **easy to test** and **improve** 28 | -------------------------------------------------------------------------------- /05_design_patterns/02_solid_principles/01_single_responsible_principle/main.py: -------------------------------------------------------------------------------- 1 | class Report: 2 | def __init__(self, content: str) -> None: 3 | self.content: str = content 4 | 5 | def generate(self) -> None: 6 | print(f"Report content: {self.content}") 7 | 8 | class ReportSaver: 9 | def __init__(self, report: Report) -> None: 10 | self.report: Report = report 11 | 12 | def save_to_file(self, filename: str) -> None: 13 | with open(filename, "w") as file: 14 | file.write(self.report.content) 15 | 16 | if __name__ == "__main__": 17 | report_content: str = "This is the content." 18 | report: Report = Report(report_content) 19 | report.generate() 20 | 21 | report_saver: ReportSaver = ReportSaver(report) 22 | report_saver.save_to_file("report.txt") 23 | -------------------------------------------------------------------------------- /05_design_patterns/02_solid_principles/01_single_responsible_principle/report.txt: -------------------------------------------------------------------------------- 1 | This is the content. -------------------------------------------------------------------------------- /05_design_patterns/02_solid_principles/02_open_closed_principle/main.py: -------------------------------------------------------------------------------- 1 | import math 2 | from typing import Protocol 3 | 4 | class Shape(Protocol): 5 | def area(self) -> float: 6 | ... 7 | 8 | class Rectangle: 9 | def __init__(self, width: float, height: float) -> None: 10 | self.width: float = width 11 | self.height: float = height 12 | 13 | def area(self) -> float: 14 | return self.width * self.height 15 | 16 | class Circle: 17 | def __init__(self, radius: float) -> None: 18 | self.radius: float = radius 19 | 20 | def area(self) -> float: 21 | return math.pi * (self.radius ** 2) 22 | 23 | def calculate_area(shape: Shape) -> float: 24 | return shape.area() 25 | 26 | if __name__ == "__main__": 27 | rect: Rectangle = Rectangle(12, 8) 28 | rect_area: float = calculate_area(rect) 29 | print(f"Rectangle area: {rect_area}") 30 | 31 | circ: Circle = Circle(6.5) 32 | circ_area: float = calculate_area(circ) 33 | print(f"Circle area: {circ_area:.2f}") -------------------------------------------------------------------------------- /05_design_patterns/02_solid_principles/03_liskov_substitution_principle/main.py: -------------------------------------------------------------------------------- 1 | class Bird: 2 | def move(self) -> None: 3 | print("I'm moving") 4 | 5 | class FlyingBird(Bird): 6 | def move(self) -> None: 7 | print("I'm flying") 8 | 9 | class FlightlessBird(Bird): 10 | def move(self) -> None: 11 | print("I'm walking") 12 | 13 | def make_bird_move(bird: Bird) -> None: 14 | bird.move() 15 | 16 | if __name__ == "__main__": 17 | generic_bird: Bird = Bird() 18 | eagle: FlyingBird = FlyingBird() 19 | penguin: FlightlessBird = FlightlessBird() 20 | 21 | make_bird_move(generic_bird) # Output: I'm moving 22 | make_bird_move(eagle) # Output: I'm flying 23 | make_bird_move(penguin) # Output: I'm walking 24 | -------------------------------------------------------------------------------- /05_design_patterns/02_solid_principles/04_interface_segregation_principle/main.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol 2 | 3 | class Printer(Protocol): 4 | def print_document(self) -> None: 5 | ... 6 | 7 | class Scanner(Protocol): 8 | def scan_document(self) -> None: 9 | ... 10 | 11 | class Fax(Protocol): 12 | def fax_document(self) -> None: 13 | ... 14 | 15 | class AllInOnePrinter: 16 | def print_document(self) -> None: 17 | print("Printing") 18 | 19 | def scan_document(self) -> None: 20 | print("Scanning") 21 | 22 | def fax_document(self) -> None: 23 | print("Faxing") 24 | 25 | class SimplePrinter: 26 | def print_document(self) -> None: 27 | print("Simply Printing") 28 | 29 | def do_the_print(printer: Printer) -> None: 30 | printer.print_document() 31 | 32 | if __name__ == "__main__": 33 | all_in_one = AllInOnePrinter() 34 | all_in_one.scan_document() 35 | all_in_one.fax_document() 36 | do_the_print(all_in_one) 37 | 38 | simple = SimplePrinter() 39 | do_the_print(simple) 40 | -------------------------------------------------------------------------------- /05_design_patterns/02_solid_principles/05_dependency_inversion_principle/main.py: -------------------------------------------------------------------------------- 1 | from typing import Protocol 2 | 3 | class MessageSender(Protocol): 4 | def send(self, message: str) -> None: 5 | ... 6 | 7 | class Email: 8 | def send(self, message: str) -> None: 9 | print(f"Sending email: {message}") 10 | 11 | class Notification: 12 | def __init__(self, sender: MessageSender) -> None: 13 | self.sender: MessageSender = sender 14 | 15 | def send(self, message: str) -> None: 16 | self.sender.send(message) 17 | 18 | def send_notification(sender: MessageSender, message: str) -> None: 19 | notif = Notification(sender=sender) 20 | notif.send(message=message) 21 | 22 | if __name__ == "__main__": 23 | email_sender = Email() 24 | send_notification(sender=email_sender, message="This is the message.") 25 | -------------------------------------------------------------------------------- /05_design_patterns/02_solid_principles/readme.md: -------------------------------------------------------------------------------- 1 | # SOLID Design Principles 🚀 2 | 3 | In the world of software engineering, principles and best practices are essential for building a **robust**, **maintainable**, and **efficient** codebase. Among these, the **SOLID principles**, introduced by Robert C. Martin, are key to creating software that is **understandable**, **flexible**, and **maintainable**. 4 | 5 | ## 📚 Topics Covered 6 | 7 | ### 1️⃣ Single Responsibility Principle (SRP) 8 | - Ensures that a class or module has **one and only one reason to change**. 9 | 10 | ### 2️⃣ Open-Closed Principle (OCP) 11 | - Encourages designing software entities to be **open for extension but closed for modification**. 12 | 13 | ### 3️⃣ Liskov Substitution Principle (LSP) 14 | - Guarantees that derived classes can be **substituted** for their base classes without altering the behavior of the program. 15 | 16 | ### 4️⃣ Interface Segregation Principle (ISP) 17 | - Promotes the creation of **specific** interfaces rather than forcing classes to implement unnecessary methods. 18 | 19 | ### 5️⃣ Dependency Inversion Principle (DIP) 20 | - Advocates for the use of **abstractions** over concrete implementations to **reduce dependency** on low-level modules. 21 | -------------------------------------------------------------------------------- /05_design_patterns/03_creational_design_patterns/01_factory_design_pattern/abstract_factory.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | 3 | class Obstacle(ABC): 4 | @abstractmethod 5 | def action(self) -> str: 6 | pass 7 | 8 | class Frog: 9 | def __init__(self, name: str): 10 | self.name = name 11 | 12 | def __str__(self) -> str: 13 | return self.name 14 | 15 | def interact_with(self, obstacle: "Obstacle") -> None: 16 | act = obstacle.action() 17 | msg = f"{self} the Frog encounters {obstacle} and {act}!" 18 | print(msg) 19 | 20 | class Bug: 21 | def __str__(self) -> str: 22 | return "a bug" 23 | 24 | def action(self) -> str: 25 | return "eats it" 26 | 27 | 28 | class Wizard: 29 | def __init__(self, name: str): 30 | self.name = name 31 | 32 | def __str__(self) -> str: 33 | return self.name 34 | 35 | def interact_with(self, obstacle: "Obstacle") -> None: 36 | act = obstacle.action() 37 | msg = f"{self} the Wizard battles against {obstacle} and {act}!" 38 | print(msg) 39 | 40 | class Ork: 41 | def __str__(self) -> str: 42 | return "an evil ork" 43 | 44 | def action(self) -> str: 45 | return "kills it" 46 | 47 | 48 | class FrogWorld: 49 | def __init__(self, name: str): 50 | print(self) 51 | self.player_name = name 52 | 53 | def __str__(self) -> str: 54 | return "\n\n\t------ Frog World -------" 55 | 56 | def make_character(self) -> Frog: 57 | return Frog(self.player_name) 58 | 59 | def make_obstacle(self) -> Bug: 60 | return Bug() 61 | 62 | class WizardWorld: 63 | def __init__(self, name: str): 64 | print(self) 65 | self.player_name = name 66 | 67 | def __str__(self) -> str: 68 | return "\n\n\t------ Wizard World -------" 69 | 70 | def make_character(self) -> Wizard: 71 | return Wizard(self.player_name) 72 | 73 | def make_obstacle(self) -> Ork: 74 | return Ork() 75 | 76 | 77 | 78 | # Game Environment 79 | class GameEnvironment: 80 | def __init__(self, factory: object): 81 | self.hero = factory.make_character() 82 | self.obstacle = factory.make_obstacle() 83 | 84 | def play(self) -> None: 85 | self.hero.interact_with(self.obstacle) 86 | 87 | 88 | def validate_age(name: str) -> tuple[bool, int]: 89 | age = None 90 | try: 91 | age_input = input(f"Welcome {name}. How old are you? ") 92 | age = int(age_input) 93 | except ValueError: 94 | print(f"Age {age_input} is invalid, please try again...") 95 | return False, age 96 | return True, age 97 | 98 | def main() -> None: 99 | name = input("Hello. What's your name? ") 100 | valid_input = False 101 | while not valid_input: 102 | valid_input, age = validate_age(name) 103 | game = FrogWorld if age < 18 else WizardWorld 104 | environment = GameEnvironment(factory=game(name)) 105 | environment.play() 106 | 107 | if __name__ == "__main__": 108 | main() -------------------------------------------------------------------------------- /05_design_patterns/03_creational_design_patterns/01_factory_design_pattern/factory_method.py: -------------------------------------------------------------------------------- 1 | # ch03/factory/factory_method.py 2 | 3 | from typing import Protocol 4 | from pathlib import Path 5 | import json 6 | import xml.etree.ElementTree as ET 7 | 8 | class DataExtractor(Protocol): 9 | @property 10 | def parsed_data(self): 11 | ... 12 | 13 | class JSONDataExtractor: 14 | def __init__(self, filepath: Path): 15 | self.data = {} 16 | with open(filepath) as f: 17 | self.data = json.load(f) 18 | 19 | @property 20 | def parsed_data(self): 21 | return self.data 22 | 23 | class XMLDataExtractor: 24 | def __init__(self, filepath: Path): 25 | self.tree = ET.parse(filepath) 26 | 27 | @property 28 | def parsed_data(self): 29 | return self.tree 30 | 31 | 32 | -------------------------------------------------------------------------------- /05_design_patterns/03_creational_design_patterns/01_factory_design_pattern/main.py: -------------------------------------------------------------------------------- 1 | from factory_method import JSONDataExtractor, XMLDataExtractor, DataExtractor 2 | from pathlib import Path 3 | 4 | def extract_factory(filepath: Path) -> DataExtractor: 5 | ext = filepath.suffix.lower() 6 | if ext == ".json": 7 | return JSONDataExtractor(filepath) 8 | elif ext == ".xml": 9 | return XMLDataExtractor(filepath) 10 | else: 11 | raise ValueError(f"Unsupported file extension: {ext}") 12 | 13 | def extract(case: str) -> None: 14 | dir_path = Path(__file__).parent 15 | if case == "json": 16 | path = dir_path / "movies.json" 17 | factory = extract_factory(path) 18 | data = factory.parsed_data 19 | for movie in data: 20 | print(f"- {movie['title']}") 21 | director = movie.get("director") 22 | if director: 23 | print(f" Director: {director}") 24 | genre = movie.get("genre") 25 | if genre: 26 | print(f" Genre: {genre}") 27 | elif case == "xml": 28 | path = dir_path / "person.xml" 29 | factory = extract_factory(path) 30 | data = factory.parsed_data 31 | search_xpath = ".//person[lastName='Liar']" 32 | items = data.findall(search_xpath) 33 | for item in items: 34 | first = item.find("firstName").text 35 | last = item.find("lastName").text 36 | print(f"- {first} {last}") 37 | for pn in item.find("phoneNumbers"): 38 | pn_type = pn.attrib.get("type", "unknown") 39 | pn_val = pn.text 40 | phone = f" {pn_type}: {pn_val}" 41 | print(phone) 42 | else: 43 | print(f"Unknown case: {case}") 44 | 45 | if __name__ == "__main__": 46 | print("* JSON case *") 47 | extract(case="json") 48 | print("* XML case *") 49 | extract(case="xml") 50 | 51 | -------------------------------------------------------------------------------- /05_design_patterns/03_creational_design_patterns/01_factory_design_pattern/movies.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "title": "After Dark in Central Park", 4 | "year": 1900, 5 | "director": null, 6 | "cast": null, 7 | "genre": null 8 | }, 9 | { 10 | "title": "Boarding School Girls' Pajama Parade", 11 | "year": 1900, 12 | "director": null, 13 | "cast": null, 14 | "genre": null 15 | }, 16 | { 17 | "title": "Buffalo Bill's Wild West Parad", 18 | "year": 1900, 19 | "director": null, 20 | "cast": null, 21 | "genre": null 22 | }, 23 | { 24 | "title": "Caught", 25 | "year": 1900, 26 | "director": null, 27 | "cast": null, 28 | "genre": null 29 | }, 30 | { 31 | "title": "Clowns Spinning Hats", 32 | "year": 1900, 33 | "director": null, 34 | "cast": null, 35 | "genre": null 36 | }, 37 | { 38 | "title": "Capture of Boer Battery by British", 39 | "year": 1900, 40 | "director": "James H. White", 41 | "cast": null, 42 | "genre": "Short documentary" 43 | }, 44 | { 45 | "title": "The Enchanted Drawing", 46 | "year": 1900, 47 | "director": "J. Stuart Blackton", 48 | "cast": null, 49 | "genre": null 50 | }, 51 | { 52 | "title": "Family Troubles", 53 | "year": 1900, 54 | "director": null, 55 | "cast": null, 56 | "genre": null 57 | }, 58 | { 59 | "title": "Feeding Sea Lions", 60 | "year": 1900, 61 | "director": null, 62 | "cast": "Paul Boyton", 63 | "genre": null 64 | } 65 | ] 66 | 67 | -------------------------------------------------------------------------------- /05_design_patterns/03_creational_design_patterns/01_factory_design_pattern/person.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | John 4 | Smith 5 | 25 6 |
7 | 21 2nd Street 8 | New York 9 | NY 10 | 10021 11 |
12 | 13 | 212 555-1234 14 | 646 555-4567 15 | 16 | 17 | male 18 | 19 |
20 | 21 | Jimy 22 | Liar 23 | 19 24 |
25 | 18 2nd Street 26 | New York 27 | NY 28 | 10021 29 |
30 | 31 | 212 555-1234 32 | 33 | 34 | male 35 | 36 |
37 | 38 | Patty 39 | Liar 40 | 20 41 |
42 | 18 2nd Street 43 | New York 44 | NY 45 | 10021 46 |
47 | 48 | 212 555-1234 49 | 001 452-8819 50 | 51 | 52 | female 53 | 54 |
55 |
56 | -------------------------------------------------------------------------------- /05_design_patterns/03_creational_design_patterns/03_prototype_pattern/main.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | 4 | class Website: 5 | def __init__(self, name: str, domain: str, description: str, **kwargs): 6 | self.name = name 7 | self.domain = domain 8 | self.description = description 9 | # Dynamically assign additional attributes 10 | for key in kwargs: 11 | setattr(self, key, kwargs[key]) 12 | 13 | def __str__(self) -> str: 14 | summary = [f"- {self.name} (ID: {id(self)})\n"] 15 | infos = vars(self).items() 16 | ordered_infos = sorted(infos) 17 | 18 | for attr, val in ordered_infos: 19 | if attr == "name": 20 | continue 21 | summary.append(f"{attr}: {val}\n") 22 | 23 | return "".join(summary) 24 | 25 | 26 | class Prototype: 27 | def __init__(self): 28 | self.registry = {} # Stores objects 29 | 30 | def register(self, identifier: int, obj: object): 31 | self.registry[identifier] = obj # Register object 32 | 33 | def unregister(self, identifier: int): 34 | del self.registry[identifier] # Remove object 35 | 36 | def clone(self, identifier: int, **attrs) -> object: 37 | found = self.registry.get(identifier) 38 | if not found: 39 | raise ValueError(f"Incorrect object identifier: {identifier}") 40 | 41 | obj = copy.deepcopy(found) # Clone the object 42 | for key in attrs: 43 | setattr(obj, key, attrs[key]) # Modify attributes if provided 44 | 45 | return obj 46 | 47 | def main(): 48 | keywords = ("python", "programming", "scripting", "data", "automation") 49 | 50 | site1 = Website( 51 | name="Python", 52 | domain="python.org", 53 | description="Programming language and ecosystem", 54 | category="Open Source Software", 55 | keywords=keywords, 56 | ) 57 | 58 | proto = Prototype() 59 | proto.register("python-001", site1) # Register site1 60 | 61 | # Clone site1 and modify some attributes 62 | site2 = proto.clone( 63 | "python-001", 64 | name="Python Package Index", 65 | domain="pypi.org", 66 | description="Repository for published packages", 67 | ) 68 | 69 | # Print both original and cloned sites 70 | for site in (site1, site2): 71 | print(site) 72 | 73 | 74 | if __name__ == "__main__": 75 | main() -------------------------------------------------------------------------------- /06_python_projects_for_beginners/readme.md: -------------------------------------------------------------------------------- 1 | # Python Projects For Beginners -------------------------------------------------------------------------------- /07_cpython/readme.md: -------------------------------------------------------------------------------- 1 | # Cpython -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Muhammad Hashim 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 | --------------------------------------------------------------------------------