├── better-python ├── README.md └── main.py ├── makefiles ├── README.md └── Makefile ├── pytest ├── todo │ ├── __init__.py │ └── todo_manager.py ├── tests │ ├── __init__.py │ └── test_todo_manager.py └── README.md ├── data-structures └── README.md ├── error-handling ├── README.md ├── 3_exception_chaining.py ├── 5_try_finally.py ├── 1_context_manager.py ├── 4_error_handling_decorators.py └── 2_custom_exceptions.py ├── go-intro ├── loops_examples.go ├── goroutine_example.go ├── variables.go ├── func_example.go ├── loops_conditionals.go └── data_structures_example.go ├── debug-python-in-docker └── README.md ├── python-magic-methods └── main.py ├── string-manipulation └── README.md ├── logging-for-python-devs └── examples.py ├── on-design-patterns └── singleton │ └── README.md ├── useful-python-one-liners └── README.md ├── handle-large-datasets ├── sample_data_generator.py ├── tip_1.py ├── tip_4.py ├── tip_3.py ├── tip_5.py └── tip_2.py ├── name-main-python ├── example-2 │ ├── add.py │ ├── __pycache__ │ │ ├── add.cpython-38.pyc │ │ └── test_add.cpython-38.pyc │ └── test_add.py └── example-1 │ ├── module1.py │ ├── __pycache__ │ └── module1.cpython-38.pyc │ └── module2.py ├── python-for-beginners ├── day-2 │ ├── scores.txt │ ├── people.txt │ ├── emails.txt │ └── day2_examples.py ├── day-1 │ ├── responses.txt │ └── day1_examples.py ├── day-4 │ ├── employees.txt │ └── day4_examples.py ├── day-5 │ ├── employees.txt │ └── day5_examples.py ├── day-6 │ ├── employees.txt │ └── day6_examples.py ├── day-3 │ ├── comments.txt │ └── day3_examples.py └── day-7 │ └── day7_examples.py ├── data-cleaning └── README.md ├── intro-to-docker ├── requirements.txt ├── Dockerfile └── app.py ├── python-sleep ├── simple_example.py ├── delay_times.py ├── countdown.py └── threads.py ├── pandas-df └── students.csv ├── big-o-examples ├── o1_example.py ├── o2n_example.py ├── on_example.py ├── onsq_example.py ├── ologn_example.py ├── onlogn_example.py ├── README.md └── ofactorialn_example.py ├── common-gotchas ├── int_quirk.py ├── tup_assignment.py ├── var_scope.py ├── mutable_defaults.py └── shallow_and_deep_copy.py ├── config-management-basics ├── working-with-yaml │ ├── config.yaml │ └── examples.py ├── parsing-ini-files │ ├── app.ini │ └── parsing_ini_files.py ├── parsing-toml-files │ ├── config.toml │ └── reading-toml.py └── env-vars │ └── examples.py ├── parsing-json ├── README.md └── json-parsing-in-python.py ├── parse-xml ├── README.md ├── products.xml └── xml_parsing_in_python.py ├── enums ├── main1.py ├── main.py └── task.py ├── unit-testing ├── functions │ ├── prime_number.py │ ├── test_prime.py │ ├── prime_number_1.py │ └── test_prime_1.py └── classes │ ├── book.py │ ├── test_book.py │ ├── test_book_1.py │ └── test_book_2.py ├── natural-sorting ├── README.md └── main.py ├── any-all ├── all_examples.py └── any_examples.py ├── partial-functions ├── example4.py ├── example1.py ├── example3.py └── example2.py ├── search ├── linear_search.py └── binary_search.py ├── sql-index ├── create_index.py ├── sample_query.py └── main.py ├── pathlib-examples ├── README.md ├── back_up.py ├── search.py └── organize.py ├── dict-to-json ├── handle_datetime.py └── main.py ├── useful-decorators ├── lru_cache_dec.py ├── contextmanager_dec.py ├── wraps_dec.py ├── property_dec.py ├── cached_property_dec.py ├── README.md ├── singledispatch_dec.py └── total_ordering_dec.py ├── sqlite-tut ├── delete_record.py ├── README.md ├── connect_to_db.py ├── filter.py ├── insert.py └── read_and_update.py ├── tracemalloc-tutorial ├── create_data.py └── main.py ├── merge-dict └── main.py ├── built-in-data-structs ├── set-diff │ └── set-difference.py └── update-python-dicts │ └── update_dict.py ├── write-better-funcs ├── tip5.py ├── tip3.py ├── README.md ├── tip4.py ├── tip2.py └── tip1.py ├── for-else └── main.py ├── logging ├── test_div.py └── main.py ├── generators-101 └── generators.py ├── walrus-operator ├── antipattern.py └── main.py ├── pydantic-basics ├── employees.json └── main.py ├── keyerrors └── main.py ├── working-with-large-datasets └── useful_example.py ├── switch-case ├── main1.py ├── main3.py └── main2.py ├── caching └── main.py ├── python-lists └── basics.py ├── function-args └── main.py ├── timedelta └── examples.py ├── pathlib-tutorial └── main.py ├── bytes2str └── main.py ├── regex ├── main.py └── regex_examples.ipynb ├── sql-tips └── main.py ├── optimize-python-code └── examples.py ├── misused-python-functions └── main.py ├── functools-n-itertools └── examples.py ├── custom-exceptions └── main.py ├── write-efficient-data-classes └── examples.py ├── custom_context_manager └── main.py ├── coding-interview-tips └── tips.py ├── automate-data-cleaning └── automate_5_steps.py ├── clean-python └── clean_python.py ├── efficient-python-for-beginners └── main.py ├── readable-python-functions └── examples.py ├── decorator-patterns └── examples │ └── main.py ├── memory-management └── memory_management_examples.py ├── secure-hashing └── examples.py ├── useful-python-functions └── main.py └── pydantic-for-python-devs └── examples.py /better-python/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /makefiles/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pytest/todo/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data-structures/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /error-handling/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /go-intro/loops_examples.go: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /pytest/tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /debug-python-in-docker/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /go-intro/goroutine_example.go: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /python-magic-methods/main.py: -------------------------------------------------------------------------------- 1 | p 2 | -------------------------------------------------------------------------------- /string-manipulation/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /logging-for-python-devs/examples.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /on-design-patterns/singleton/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /useful-python-one-liners/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /handle-large-datasets/sample_data_generator.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /name-main-python/example-2/add.py: -------------------------------------------------------------------------------- 1 | def add_ab(a,b): 2 | return a + b -------------------------------------------------------------------------------- /python-for-beginners/day-2/scores.txt: -------------------------------------------------------------------------------- 1 | 88.5 2 | 67.0 3 | 92.0 4 | 75.5 5 | -------------------------------------------------------------------------------- /python-for-beginners/day-2/people.txt: -------------------------------------------------------------------------------- 1 | Alice, 34 2 | Bob, 29 3 | Eve, 41 4 | -------------------------------------------------------------------------------- /data-cleaning/README.md: -------------------------------------------------------------------------------- 1 | ✨ Some things interesting on data cleaning w/ Python 2 | -------------------------------------------------------------------------------- /python-for-beginners/day-1/responses.txt: -------------------------------------------------------------------------------- 1 | Yes 2 | No 3 | Yes 4 | Maybe 5 | No 6 | -------------------------------------------------------------------------------- /intro-to-docker/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi==0.116.1 2 | uvicorn[standard]==0.35.0 3 | pydantic==2.11.7 4 | -------------------------------------------------------------------------------- /python-for-beginners/day-2/emails.txt: -------------------------------------------------------------------------------- 1 | person_1@gmail.com 2 | person_2@yahoo.com 3 | person_3@example.org 4 | -------------------------------------------------------------------------------- /name-main-python/example-1/module1.py: -------------------------------------------------------------------------------- 1 | print("This is module1.") 2 | print(f"The __name__ variable of module 1 is: {__name__}.") -------------------------------------------------------------------------------- /python-sleep/simple_example.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | print("Print now") 4 | time.sleep(5) 5 | print("Print after sleeping for 5 seconds") 6 | -------------------------------------------------------------------------------- /pandas-df/students.csv: -------------------------------------------------------------------------------- 1 | Amy,51,92,14,71 2 | Bob,60,20,82,86 3 | Chris,74,74,87,99 4 | Dave,23,2,21,52 5 | Evelyn,1,87,29,37 6 | Fanny,1,63,59,20 7 | -------------------------------------------------------------------------------- /big-o-examples/o1_example.py: -------------------------------------------------------------------------------- 1 | def get_first_element(arr): 2 | return arr[0] if arr else None 3 | 4 | def check_if_even(num): 5 | return num % 2 == 0 6 | -------------------------------------------------------------------------------- /common-gotchas/int_quirk.py: -------------------------------------------------------------------------------- 1 | a = 7 2 | b = 7 3 | print(a == 7) 4 | print(a is b) 5 | 6 | 7 | x = 280 8 | y = 280 9 | print(x == y) 10 | print(x is y) 11 | -------------------------------------------------------------------------------- /big-o-examples/o2n_example.py: -------------------------------------------------------------------------------- 1 | def fibonacci_recursive(n): 2 | if n <= 1: 3 | return n 4 | return fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2) 5 | -------------------------------------------------------------------------------- /big-o-examples/on_example.py: -------------------------------------------------------------------------------- 1 | def find_element(arr, target): 2 | for element in arr: 3 | if element == target: 4 | return True 5 | return False 6 | -------------------------------------------------------------------------------- /name-main-python/example-2/__pycache__/add.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balapriyac/python-basics/HEAD/name-main-python/example-2/__pycache__/add.cpython-38.pyc -------------------------------------------------------------------------------- /config-management-basics/working-with-yaml/config.yaml: -------------------------------------------------------------------------------- 1 | database: 2 | host: localhost 3 | port: 5432 4 | credentials: 5 | username: admin 6 | password: secret 7 | 8 | -------------------------------------------------------------------------------- /name-main-python/example-1/__pycache__/module1.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balapriyac/python-basics/HEAD/name-main-python/example-1/__pycache__/module1.cpython-38.pyc -------------------------------------------------------------------------------- /name-main-python/example-1/module2.py: -------------------------------------------------------------------------------- 1 | # module1 is imported 2 | import module1 3 | 4 | print(f"This is module2") 5 | print(f"The __name__ variable of module2 is: {__name__}.") 6 | -------------------------------------------------------------------------------- /name-main-python/example-2/__pycache__/test_add.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balapriyac/python-basics/HEAD/name-main-python/example-2/__pycache__/test_add.cpython-38.pyc -------------------------------------------------------------------------------- /python-for-beginners/day-4/employees.txt: -------------------------------------------------------------------------------- 1 | Alice,London,Engineer,75000 2 | Bob,Paris,Analyst,62000 3 | Eve,London,Engineer,80000 4 | John,New York,Manager,95000 5 | Dana,Paris,Analyst,64000 6 | -------------------------------------------------------------------------------- /python-for-beginners/day-5/employees.txt: -------------------------------------------------------------------------------- 1 | Alice,London,Engineer,75000 2 | Bob,Paris,Analyst,62000 3 | Eve,London,Engineer,80000 4 | John,New York,Manager,95000 5 | Dana,Paris,Analyst,64000 6 | -------------------------------------------------------------------------------- /python-for-beginners/day-6/employees.txt: -------------------------------------------------------------------------------- 1 | Alice,London,Engineer,75000 2 | Bob,Paris,Analyst,62000 3 | Eve,London,Engineer,80000 4 | John,New York,Manager,95000 5 | Dana,Paris,Analyst,64000 6 | -------------------------------------------------------------------------------- /parsing-json/README.md: -------------------------------------------------------------------------------- 1 | Code examples for my article [How to Parse JSON in Python – A Complete Guide With Examples](https://www.freecodecamp.org/news/how-to-parse-json-in-python-with-examples/) 2 | -------------------------------------------------------------------------------- /common-gotchas/tup_assignment.py: -------------------------------------------------------------------------------- 1 | my_tuple = ([1,2],3,4) 2 | my_tuple[0].append(3) # works fine! 3 | print(my_tuple) 4 | 5 | # now this is where things get interesting... 6 | my_tuple[0] += [4,5] 7 | -------------------------------------------------------------------------------- /parse-xml/README.md: -------------------------------------------------------------------------------- 1 | Code examples for my article [How to Parse XML in Python Without Using External Libraries](https://www.freecodecamp.org/news/how-to-parse-xml-in-python-without-using-external-libraries/) 2 | -------------------------------------------------------------------------------- /python-sleep/delay_times.py: -------------------------------------------------------------------------------- 1 | import time 2 | sleep_times = [3,4,1.5,2,0.75] 3 | string = "How long will this take?" 4 | for sleep_time,word in zip(sleep_times,string.split()): 5 | print(word) 6 | time.sleep(sleep_time) 7 | -------------------------------------------------------------------------------- /enums/main1.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, auto 2 | 3 | class TaskStatus(Enum): 4 | TODO = auto() 5 | IN_PROGRESS = auto() 6 | DONE = auto() 7 | ABANDONED = auto() 8 | 9 | 10 | print(list(TaskStatus)) 11 | 12 | -------------------------------------------------------------------------------- /unit-testing/functions/prime_number.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def is_prime(num): 4 | '''Check if num is prime or not.''' 5 | for i in range(2,int(math.sqrt(num))+1): 6 | if num%i==0: 7 | return False 8 | return True 9 | -------------------------------------------------------------------------------- /python-for-beginners/day-3/comments.txt: -------------------------------------------------------------------------------- 1 | Great course! Loved the pacing. 2 | Not enough Python examples. 3 | Too basic for experienced users. 4 | python is exactly what I needed! 5 | Would like more SQL content. 6 | Excellent – very beginner-friendly. 7 | -------------------------------------------------------------------------------- /python-sleep/countdown.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def count_down(n): 4 | for i in range(n,-1,-1): 5 | if i==0: 6 | print("Ready to go!") 7 | else: 8 | print(i) 9 | time.sleep(1) 10 | 11 | count_down(5) 12 | -------------------------------------------------------------------------------- /natural-sorting/README.md: -------------------------------------------------------------------------------- 1 | ## Natural Sorting in Python with `natsort` 2 | 3 | To get started, you can install the `natsort` library using pip: 4 | 5 | ```sh 6 | $ pip3 install natsort 7 | ``` 8 | 9 | Requires Python 3.7+. So any recent version should suffice. 10 | 11 | -------------------------------------------------------------------------------- /big-o-examples/onsq_example.py: -------------------------------------------------------------------------------- 1 | def find_duplicates(arr): 2 | duplicates = [] 3 | for i in range(len(arr)): 4 | for j in range(i + 1, len(arr)): 5 | if arr[i] == arr[j] and arr[i] not in duplicates: 6 | duplicates.append(arr[i]) 7 | return duplicates 8 | -------------------------------------------------------------------------------- /any-all/all_examples.py: -------------------------------------------------------------------------------- 1 | my_string = "coding**is**cool" 2 | are_all_letters = [char.isalpha() for char in my_string] 3 | print(all(are_all_letters)) 4 | # Output False 5 | 6 | my_string = "56456278" 7 | are_all_digits = [char.isdigit() for char in my_string] 8 | print(all(are_all_digits)) 9 | # Output True 10 | -------------------------------------------------------------------------------- /partial-functions/example4.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | # Use lambda with partial to create a custom scaling function 4 | scale = partial(lambda x, factor: x * factor, factor=3) 5 | 6 | # Apply the partial function to a list of numbers 7 | scaled_values = [scale(i) for i in range(1, 6)] 8 | print(scaled_values) 9 | -------------------------------------------------------------------------------- /config-management-basics/parsing-ini-files/app.ini: -------------------------------------------------------------------------------- 1 | [database] 2 | host = localhost 3 | port = 5432 4 | username = app_user 5 | password = secure_password 6 | pool_size = 10 7 | ssl_enabled = true 8 | 9 | [server] 10 | host = 0.0.0.0 11 | port = 8000 12 | debug = false 13 | 14 | [logging] 15 | level = INFO 16 | file = app.log 17 | -------------------------------------------------------------------------------- /partial-functions/example1.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | # Original function to compute power 4 | def power(base, exponent): 5 | return base ** exponent 6 | 7 | # Create a partial function that always squares the base 8 | square = partial(power, exponent=2) 9 | 10 | print(square(5)) 11 | print(square(9)) 12 | -------------------------------------------------------------------------------- /search/linear_search.py: -------------------------------------------------------------------------------- 1 | def linear_search(nums,target): 2 | for num in nums: 3 | if num == target: 4 | return True 5 | return False 6 | 7 | 8 | nums = [14,21,27,30,36,2,5,7,11] 9 | target = 27 10 | 11 | print(linear_search(nums,target)) 12 | # Output: True 13 | 14 | target = 100 15 | print(linear_search(nums,target)) 16 | # Output: False 17 | -------------------------------------------------------------------------------- /go-intro/variables.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // Explicit variable declaration with type 7 | var name string = "Gopher" 8 | // Type inference with the := operator 9 | age := 5 10 | // Constant declaration 11 | const pi = 3.14159 12 | 13 | fmt.Println(name) 14 | fmt.Println(age) 15 | fmt.Println(pi) 16 | 17 | } 18 | -------------------------------------------------------------------------------- /handle-large-datasets/tip_1.py: -------------------------------------------------------------------------------- 1 | # useful tips that do not focus on a specific dataset 2 | # We’ll use generic filenames like large_dataset.csv in the code examples 3 | 4 | # Use Generators Instead of Lists 5 | 6 | def read_large_file(file_name): 7 | with open(file_name, 'r') as file: 8 | for line in file: 9 | yield line 10 | 11 | 12 | -------------------------------------------------------------------------------- /enums/main.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class TaskStatus(Enum): 4 | TODO = 0 5 | IN_PROGRESS = 1 6 | DONE = 2 7 | ABANDONED = -1 8 | 9 | print(isinstance(TaskStatus.TODO,Enum)) 10 | 11 | print(list(TaskStatus)) 12 | 13 | num_statuses = len(TaskStatus) 14 | print(num_statuses) 15 | 16 | for status in TaskStatus: 17 | print(status.name, status.value) 18 | -------------------------------------------------------------------------------- /big-o-examples/ologn_example.py: -------------------------------------------------------------------------------- 1 | def binary_search(sorted_arr, target): 2 | left, right = 0, len(sorted_arr) - 1 3 | 4 | while left <= right: 5 | mid = (left + right) // 2 6 | if sorted_arr[mid] == target: 7 | return mid 8 | elif sorted_arr[mid] < target: 9 | left = mid + 1 10 | else: 11 | right = mid - 1 12 | return -1 13 | -------------------------------------------------------------------------------- /sql-index/create_index.py: -------------------------------------------------------------------------------- 1 | # create_index.py 2 | 3 | import time 4 | import sqlite3 5 | 6 | db_conn = sqlite3.connect('people_db.db') 7 | 8 | db_cursor =db_conn.cursor() 9 | 10 | t1 = time.perf_counter_ns() 11 | 12 | db_cursor.execute("CREATE INDEX people_job_index ON people (job)") 13 | 14 | t2 = time.perf_counter_ns() 15 | 16 | db_conn.commit() 17 | 18 | print(f"Time to create index: {(t2 - t1)/1000} us") 19 | -------------------------------------------------------------------------------- /pathlib-examples/README.md: -------------------------------------------------------------------------------- 1 | Some file management tasks with Python's pathlib: 2 | 3 | - [Organizing files by extension](https://github.com/balapriyac/python-basics/blob/main/pathlib-examples/organize.py) 4 | - [Searching for specific files](https://github.com/balapriyac/python-basics/blob/main/pathlib-examples/search.py) 5 | - [Backing up important files](https://github.com/balapriyac/python-basics/blob/main/pathlib-examples/back_up.py) 6 | -------------------------------------------------------------------------------- /name-main-python/example-2/test_add.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from add import add_ab 3 | 4 | class TestAdd(unittest.TestCase): 5 | def test_add_23(self): 6 | self.assertEqual(add_ab(2,3), 5) 7 | 8 | def test_add_19(self): 9 | self.assertEqual(add_ab(1,9), 10) 10 | 11 | def test_add_1_minus7(self): 12 | self.assertEqual(add_ab(1,-7), 8) 13 | 14 | if __name__=='__main__': 15 | unittest.main() -------------------------------------------------------------------------------- /pytest/README.md: -------------------------------------------------------------------------------- 1 | ## Unit Testing with PyTest 2 | 3 | Install pytest in a virtual environment: 4 | 5 | ``` 6 | $ python3 -m venv v1 7 | $ source v1/bin/activate 8 | $ pip3 install pytest 9 | ``` 10 | 11 | The code for the TO-DO list app is in the `todo/` folder. And the unit tests are all in the `test_todo_manager.py` file in the `tests/` folder. 12 | 13 | To run the tests, in the root directory, run: 14 | 15 | ``` 16 | $ pytest 17 | ``` 18 | -------------------------------------------------------------------------------- /intro-to-docker/Dockerfile: -------------------------------------------------------------------------------- 1 | # Start with a base Python image: 2 | FROM python:3.11-slim 3 | 4 | # Set up the working directory: 5 | WORKDIR /app 6 | 7 | # Install dependencies (this order is important for caching): 8 | COPY requirements.txt . 9 | RUN pip install --no-cache-dir -r requirements.txt 10 | 11 | # Copy your application code: 12 | COPY . . 13 | 14 | # Expose the port and set the startup command: 15 | EXPOSE 8000 16 | CMD ["python", "app.py"] 17 | -------------------------------------------------------------------------------- /common-gotchas/var_scope.py: -------------------------------------------------------------------------------- 1 | # loops 2 | x = 10 3 | squares = [] 4 | for x in range(5): 5 | squares.append(x ** 2) 6 | 7 | print("Squares list:", squares) 8 | 9 | # x is accessible here and is the last value of the looping var 10 | print("x after for loop:", x) 11 | 12 | # list comp 13 | x = 10 14 | squares = [x ** 2 for x in range(5)] 15 | 16 | print("Squares list:", squares) 17 | 18 | # x is 10 here 19 | print("x after list comprehension:", x) 20 | -------------------------------------------------------------------------------- /unit-testing/classes/book.py: -------------------------------------------------------------------------------- 1 | class Book: 2 | def __init__(self,title,author,pages,price,discount): 3 | self.title = title 4 | self.author = author 5 | self.pages = pages 6 | self.price = price 7 | self.discount = discount 8 | def get_reading_time(self): 9 | return f"{self.pages*1.5} minutes" 10 | def apply_discount(self): 11 | discounted_price = self.price - (self.discount*self.price) 12 | return f"${discounted_price}" 13 | -------------------------------------------------------------------------------- /handle-large-datasets/tip_4.py: -------------------------------------------------------------------------------- 1 | # useful tips that do not focus on a specific dataset 2 | # We’ll use generic filenames like large_dataset.csv in the code examples 3 | 4 | # Use Dask for Parallel Computing 5 | 6 | import dask.dataframe as dd 7 | 8 | # Load data into a Dask DataFrame 9 | df = dd.read_csv('large_sales_data.csv') 10 | 11 | # Group by 'category' and calculate mean sales (executed in parallel) 12 | mean_sales = df.groupby('category')['sales'].mean().compute() 13 | 14 | print(mean_sales) 15 | -------------------------------------------------------------------------------- /pathlib-examples/back_up.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | from pathlib import Path 3 | 4 | def back_up_files(directory, backup_directory): 5 | path = Path(directory) 6 | backup_path = Path(backup_directory) 7 | backup_path.mkdir(parents=True, exist_ok=True) 8 | 9 | for important_file in path.rglob('*.py'): 10 | shutil.copy(important_file, backup_path / important_file.name) 11 | print(f'Backed up {important_file} to {backup_path}') 12 | 13 | back_up_files('new_project', 'backup') 14 | -------------------------------------------------------------------------------- /sql-index/sample_query.py: -------------------------------------------------------------------------------- 1 | # sample_query.py 2 | 3 | import sqlite3 4 | import time 5 | 6 | db_conn = sqlite3.connect("people_db.db") 7 | db_cursor = db_conn.cursor() 8 | 9 | t1 = time.perf_counter_ns() 10 | 11 | db_cursor.execute("SELECT name, email FROM people WHERE job='Product manager' LIMIT 10;") 12 | 13 | res = db_cursor.fetchall() 14 | t2 = time.perf_counter_ns() 15 | 16 | print(res) 17 | print(f"Query time without index: {(t2-t1)/1000} us") # update this line when re-running after creating index 18 | -------------------------------------------------------------------------------- /dict-to-json/handle_datetime.py: -------------------------------------------------------------------------------- 1 | import json 2 | from datetime import datetime 3 | 4 | # Define custom serialization function for datetime objects 5 | def serialize_datetime(obj): 6 | if isinstance(obj, datetime): 7 | return obj.isoformat() 8 | 9 | # Sample dictionary with a datetime object 10 | data = { 11 | "event": "Meeting", 12 | "date": datetime.now() 13 | } 14 | 15 | # Convert dictionary to JSON string 16 | json_string = json.dumps(data, default=serialize_datetime, indent=2) 17 | print(json_string) 18 | -------------------------------------------------------------------------------- /unit-testing/functions/test_prime.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | # import the is_prime function 3 | from prime_number import is_prime 4 | 5 | 6 | class TestPrime(unittest.TestCase): 7 | def test_two(self): 8 | self.assertTrue(is_prime(2)) 9 | def test_five(self): 10 | self.assertTrue(is_prime(5)) 11 | def test_nine(self): 12 | self.assertFalse(is_prime(9)) 13 | def test_eleven(self): 14 | self.assertTrue(is_prime(11)) 15 | 16 | 17 | if __name__=='__main__': 18 | unittest.main() 19 | -------------------------------------------------------------------------------- /useful-decorators/lru_cache_dec.py: -------------------------------------------------------------------------------- 1 | from functools import lru_cache 2 | 3 | @lru_cache(maxsize=128) 4 | def fibonacci(n): 5 | if n < 2: 6 | return n 7 | return fibonacci(n-1) + fibonacci(n-2) 8 | 9 | # Watch how fast this runs compared to non-cached version 10 | import time 11 | start = time.time() 12 | result = fibonacci(35) 13 | end = time.time() 14 | print(f"Fibonacci(35) = {result}, calculated in {end-start:.6f} seconds") 15 | 16 | # Check cache statistics 17 | print(f"Cache info: {fibonacci.cache_info()}") 18 | -------------------------------------------------------------------------------- /common-gotchas/mutable_defaults.py: -------------------------------------------------------------------------------- 1 | def add_to_cart(item, cart=[]): 2 | cart.append(item) 3 | return cart 4 | 5 | # User 1 adds items to their cart 6 | user1_cart = add_to_cart("Apple") 7 | print("User 1 Cart:", user1_cart) 8 | 9 | # User 2 adds items to their cart 10 | user2_cart = add_to_cart("Cookies") 11 | print("User 2 Cart:", user2_cart) 12 | 13 | # workaround 14 | 15 | def add_to_cart(item, cart=None): 16 | if cart is None: 17 | cart = [] 18 | cart.append(item) 19 | return cart 20 | 21 | 22 | -------------------------------------------------------------------------------- /partial-functions/example3.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | # Function to scale a value 4 | def scale_data(value, factor): 5 | return value * factor 6 | 7 | # Create partial functions for specific scaling factors 8 | scale_by_2 = partial(scale_data, factor=2) 9 | scale_by_10 = partial(scale_data, factor=10) 10 | 11 | data = [1, 2, 3, 4, 5] 12 | scaled_by_2 = list(map(scale_by_2, data)) # Scales by 2 13 | scaled_by_10 = list(map(scale_by_10, data)) # Scales by 10 14 | 15 | print(scaled_by_2) 16 | print(scaled_by_10) 17 | -------------------------------------------------------------------------------- /sqlite-tut/delete_record.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | # Specify the customer ID of the customer to delete 4 | cid_to_delete = 3 5 | 6 | with sqlite3.connect('example.db') as conn: 7 | cursor = conn.cursor() 8 | 9 | # Execute DELETE statement to remove the customer with the specified ID 10 | cursor.execute(''' 11 | DELETE FROM customers 12 | WHERE id = ? 13 | ''', (cid_to_delete,)) 14 | 15 | conn.commit() 16 | print(f"Customer with ID {cid_to_delete} deleted successfully.") 17 | cursor.close() 18 | -------------------------------------------------------------------------------- /tracemalloc-tutorial/create_data.py: -------------------------------------------------------------------------------- 1 | # create_data.py 2 | import pandas as pd 3 | import numpy as np 4 | 5 | # Create a sample dataset with order details 6 | num_orders = 100000 7 | data = { 8 | 'OrderID': np.arange(1, num_orders + 1), 9 | 'CustomerID': np.random.randint(1000, 5000, num_orders), 10 | 'OrderAmount': np.random.uniform(10.0, 1000.0, num_orders).round(2), 11 | 'OrderDate': pd.date_range(start='2023-01-01', periods=num_orders, freq='min') 12 | } 13 | 14 | df = pd.DataFrame(data) 15 | df.to_csv('order_data.csv', index=False) 16 | -------------------------------------------------------------------------------- /partial-functions/example2.py: -------------------------------------------------------------------------------- 1 | from functools import partial 2 | 3 | def format_text(text, alignment="left", width=80): 4 | if alignment == "center": 5 | return text.center(width) 6 | elif alignment == "right": 7 | return text.rjust(width) 8 | else: 9 | return text.ljust(width) 10 | 11 | # Create a partial function for center alignment with a specific width 12 | centered_text = partial(format_text, alignment="center", width=50) 13 | 14 | # Use the partial function without passing alignment or width 15 | print(centered_text("Partial Functions")) 16 | -------------------------------------------------------------------------------- /unit-testing/functions/prime_number_1.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def is_prime(num): 4 | '''Check if num is prime or not.''' 5 | # raise TypeError for invalid input type 6 | if type(num) != int: 7 | raise TypeError('num is of invalid type') 8 | # raise ValueError for invalid input value 9 | if num < 0: 10 | raise ValueError('Check the value of num; is num a non-negative integer?') 11 | # for valid input, proceed to check if num is prime 12 | for i in range(2,int(math.sqrt(num))+1): 13 | if num%i==0: 14 | return False 15 | return True 16 | -------------------------------------------------------------------------------- /search/binary_search.py: -------------------------------------------------------------------------------- 1 | def binary_search(nums,target,low,high): 2 | if low > high: 3 | return False 4 | else: 5 | mid = (low + high)//2 6 | if nums[mid] == target: 7 | return True 8 | elif nums[mid] < target: 9 | return binary_search(nums,target,mid+1,high) 10 | else: 11 | return binary_search(nums,target,low,mid-1) 12 | 13 | 14 | nums = [2,5,7,11,14,21,27,30,36] 15 | target = 27 16 | 17 | print(binary_search(nums,target,0,len(nums)-1)) 18 | # Output: True 19 | 20 | target = 38 21 | print(binary_search(nums,target,0,len(nums)-1)) 22 | # Output: False 23 | -------------------------------------------------------------------------------- /handle-large-datasets/tip_3.py: -------------------------------------------------------------------------------- 1 | # useful tips that do not focus on a specific dataset 2 | # We’ll use generic filenames like large_dataset.csv in the code examples 3 | 4 | # Use Pandas' chunksize for Chunked Processing 5 | 6 | import pandas as pd 7 | 8 | total_sales = 0 9 | chunk_size = 100000 # Define chunk size to read data in batches 10 | 11 | # Load data in chunks and process each chunk 12 | for chunk in pd.read_csv('large_sales_data.csv', chunksize=chunk_size): 13 | total_sales += chunk['sales'].sum() # Summing up sales column in each chunk 14 | 15 | print(f"Total Sales: {total_sales}") 16 | -------------------------------------------------------------------------------- /python-sleep/threads.py: -------------------------------------------------------------------------------- 1 | import threading 2 | import time 3 | 4 | def func1(): 5 | for i in range(5): 6 | print(f"Running t1, print {i}.") 7 | time.sleep(2) 8 | 9 | def func2(): 10 | for i in range(5): 11 | print(f"Running t2, print {i}.") 12 | time.sleep(1) 13 | 14 | 15 | def func3(): 16 | for i in range(4): 17 | print(f"Running t3, print {i}.") 18 | time.sleep(0.5) 19 | 20 | 21 | t1 = threading.Thread(target=func1) 22 | t2 = threading.Thread(target=func2) 23 | t3 = threading.Thread(target=func3) 24 | 25 | t1.start() 26 | t2.start() 27 | t3.start() 28 | -------------------------------------------------------------------------------- /sqlite-tut/README.md: -------------------------------------------------------------------------------- 1 | ## Getting Started with SQLite Databases in Python 2 | 3 | To code along to this simple SQLite db interactions with Python, be sure to have a recent version of Python installed. 4 | 5 | Also, create and activate a dedicated venv for your project: 6 | 7 | ```bash 8 | $ python3 -m venv v1 9 | $ source v1/bin/activate 10 | ``` 11 | 12 | Install Faker (for synthetic data generation): 13 | 14 | ```bash 15 | $ pip3 install Faker 16 | ``` 17 | 18 | This folder contains code snippets to: 19 | 20 | - Connect to an SQLite Db and create tables 21 | - CRUD operations 22 | - Perform simple filtering using WHERE clause 23 | -------------------------------------------------------------------------------- /sqlite-tut/connect_to_db.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | # Connect to the db 4 | with sqlite3.connect('example.db') as conn: 5 | cursor = conn.cursor() 6 | 7 | # Create customers table 8 | cursor.execute(''' 9 | CREATE TABLE IF NOT EXISTS customers ( 10 | id INTEGER PRIMARY KEY, 11 | first_name TEXT NOT NULL, 12 | last_name TEXT NOT NULL, 13 | email TEXT UNIQUE NOT NULL, 14 | phone TEXT, 15 | num_orders INTEGER 16 | ); 17 | ''') 18 | conn.commit() 19 | print("Customers table created successfully.") 20 | cursor.close() 21 | 22 | -------------------------------------------------------------------------------- /go-intro/func_example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func calculateStats(values []int) (min, max, sum int) { 6 | if len(values) == 0 { 7 | return 0, 0, 0 8 | } 9 | 10 | min = values[0] 11 | max = values[0] 12 | sum = 0 13 | 14 | for _, v := range values { 15 | if v < min { 16 | min = v 17 | } 18 | if v > max { 19 | max = v 20 | } 21 | sum += v 22 | } 23 | 24 | return // naked return 25 | } 26 | 27 | func main() { 28 | values := []int{5, 8, 2, 10, 3} 29 | min, max, sum := calculateStats(values) 30 | fmt.Println("Min:", min) 31 | fmt.Println("Max:", max) 32 | fmt.Println("Sum:", sum) 33 | } 34 | -------------------------------------------------------------------------------- /useful-decorators/contextmanager_dec.py: -------------------------------------------------------------------------------- 1 | from contextlib import contextmanager 2 | 3 | @contextmanager 4 | def file_manager(filename, mode): 5 | try: 6 | f = open(filename, mode) 7 | yield f 8 | finally: 9 | f.close() 10 | 11 | @contextmanager 12 | def timer(): 13 | import time 14 | start = time.time() 15 | yield 16 | elapsed = time.time() - start 17 | print(f"Elapsed time: {elapsed:.6f} seconds") 18 | 19 | # Usage 20 | with file_manager('test.txt', 'w') as f: 21 | f.write('Hello, context managers!') 22 | 23 | with timer(): 24 | # Code to time 25 | sum(i*i for i in range(1000000)) 26 | -------------------------------------------------------------------------------- /merge-dict/main.py: -------------------------------------------------------------------------------- 1 | # sample dictionaries 2 | config1 = { 3 | 'database': 'Postgres', 4 | 'host': 'localhost', 5 | 'port': 5432 6 | } 7 | 8 | config2 = { 9 | 'username': 'admin', 10 | 'password': 'secret', 11 | 'timeout': 30 12 | } 13 | 14 | # Merge using update() method 15 | final_config = config1.copy() # Create a copy to avoid modifying the original config1 16 | final_config.update(config2) 17 | 18 | print(final_config) 19 | 20 | # Merge using dictionary unpacking 21 | final_config = {**config1, **config2} 22 | 23 | print(final_config) 24 | 25 | # Merge using | operator 26 | final_config = config1 | config2 27 | 28 | print(final_config) 29 | 30 | -------------------------------------------------------------------------------- /built-in-data-structs/set-diff/set-difference.py: -------------------------------------------------------------------------------- 1 | fruits = {"apples","oranges","berries","cherries"} 2 | to_eat = {"apples","cereals","berries","bread"} 3 | 4 | print(fruits.difference(to_eat)) 5 | print(to_eat.difference(fruits)) 6 | 7 | print(fruits - to_eat) 8 | print(to_eat - fruits) 9 | 10 | set1 = {1,2,3,4,5} 11 | list1 = [2,4,6] 12 | list2 = [3,6,9] 13 | list3 = [10,11] 14 | 15 | print(set1.difference(list1,list2,list3)) 16 | 17 | # >>> set1 - (list1 + list2 + list3) 18 | # Traceback (most recent call last): 19 | # File "", line 1, in 20 | # TypeError: unsupported operand type(s) for -: 'set' and 'list' 21 | 22 | print(set1 - set(list1 + list2 + list3)) 23 | -------------------------------------------------------------------------------- /big-o-examples/onlogn_example.py: -------------------------------------------------------------------------------- 1 | def merge_sort(arr): 2 | if len(arr) <= 1: 3 | return arr 4 | 5 | mid = len(arr) // 2 6 | left = merge_sort(arr[:mid]) 7 | right = merge_sort(arr[mid:]) 8 | 9 | return merge(left, right) 10 | 11 | def merge(left, right): 12 | result = [] 13 | i = j = 0 14 | 15 | while i < len(left) and j < len(right): 16 | if left[i] <= right[j]: 17 | result.append(left[i]) 18 | i += 1 19 | else: 20 | result.append(right[j]) 21 | j += 1 22 | 23 | result.extend(left[i:]) 24 | result.extend(right[j:]) 25 | return result 26 | -------------------------------------------------------------------------------- /handle-large-datasets/tip_5.py: -------------------------------------------------------------------------------- 1 | # useful tips that do not focus on a specific dataset 2 | # We’ll use generic filenames like large_dataset.csv in the code examples 3 | 4 | # Consider PySpark for Big Data Processing 5 | 6 | from pyspark.sql import SparkSession 7 | 8 | # Start a Spark session 9 | spark = SparkSession.builder.appName("MovieRatings").getOrCreate() 10 | 11 | # Load the dataset into a Spark DataFrame 12 | df = spark.read.csv('movie_ratings.csv', header=True, inferSchema=True) 13 | 14 | # Perform transformations (e.g., group by genre and calculate average rating) 15 | df_grouped = df.groupBy('genre').mean('rating') 16 | 17 | # Show the result 18 | df_grouped.show() 19 | -------------------------------------------------------------------------------- /parse-xml/products.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Wireless Keyboard 5 | 29.99 6 | 45 7 | 8 | Electronics 9 | Accessories 10 | 11 | 12 | 13 | USB Mouse 14 | 15.99 15 | 120 16 | 17 | Electronics 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /python-for-beginners/day-1/day1_examples.py: -------------------------------------------------------------------------------- 1 | filename = "responses.txt" 2 | survey_name = "Q3 Customer Feedback" 3 | max_entries = 100 4 | 5 | with open("responses.txt", "r") as file: 6 | lines = file.readlines() 7 | 8 | for i, line in enumerate(lines): 9 | cleaned = line.strip() # removes \n and spaces 10 | print(f"{i + 1}: {cleaned}") 11 | 12 | with open("responses.txt", "r") as infile: 13 | lines = infile.readlines() 14 | 15 | yes_responses = [] 16 | 17 | for line in lines: 18 | if line.strip().lower() == "yes": 19 | yes_responses.append(line.strip()) 20 | 21 | with open("yes_only.txt", "w") as outfile: 22 | for item in yes_responses: 23 | outfile.write(item + "\n") 24 | -------------------------------------------------------------------------------- /python-for-beginners/day-5/day5_examples.py: -------------------------------------------------------------------------------- 1 | def clean_text(text): 2 | return text.strip().lower().replace(",", "").replace("$", "") 3 | 4 | def parse_row(line): 5 | parts = line.strip().split(",") 6 | return { 7 | "name": parts[0], 8 | "city": parts[1], 9 | "role": parts[2], 10 | "salary": int(parts[3]) 11 | } 12 | 13 | with open("employees.txt") as file: 14 | rows = [parse_row(line) for line in file] 15 | 16 | def average(values): 17 | return sum(values) / len(values) if values else 0 18 | 19 | def count_by_key(data, key): 20 | counts = {} 21 | for item in data: 22 | k = item[key] 23 | counts[k] = counts.get(k, 0) + 1 24 | return counts 25 | -------------------------------------------------------------------------------- /common-gotchas/shallow_and_deep_copy.py: -------------------------------------------------------------------------------- 1 | import copy 2 | 3 | 4 | # shallow copy 5 | original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 6 | 7 | # Shallow copy of the original list 8 | shallow_copy = original_list 9 | 10 | # Modify the shallow copy 11 | shallow_copy[0][0] = 100 12 | 13 | # Print both the lists 14 | print("Original List:", original_list) 15 | print("Shallow Copy:", shallow_copy) 16 | 17 | 18 | # deep copy 19 | original_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] 20 | 21 | # Deep copy of the original list 22 | deep_copy = copy.deepcopy(original_list) 23 | 24 | # Modify an element of the deep copy 25 | deep_copy[0][0] = 100 26 | 27 | # Print both lists 28 | print("Original List:", original_list) 29 | print("Deep Copy:", deep_copy) 30 | -------------------------------------------------------------------------------- /unit-testing/functions/test_prime_1.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from prime_number_1 import is_prime 3 | 4 | class TestPrime(unittest.TestCase): 5 | def test_prime_not_prime(self): 6 | self.assertTrue(is_prime(2)) 7 | self.assertTrue(is_prime(5)) 8 | self.assertFalse(is_prime(9)) 9 | self.assertTrue(is_prime(11)) 10 | def test_typeerror_1(self): 11 | with self.assertRaises(TypeError): 12 | is_prime(6.5) 13 | def test_typeerror_2(self): 14 | with self.assertRaises(TypeError): 15 | is_prime('five') 16 | def test_valueerror(self): 17 | with self.assertRaises(ValueError): 18 | is_prime(-4) 19 | 20 | if __name__=='__main__': 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /useful-decorators/wraps_dec.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | def log_execution(func): 4 | @functools.wraps(func) # Preserves func's name, docstring, etc. 5 | def wrapper(*args, **kwargs): 6 | print(f"Calling {func.__name__} with args: {args}, kwargs: {kwargs}") 7 | result = func(*args, **kwargs) 8 | print(f"{func.__name__} returned: {result}") 9 | return result 10 | return wrapper 11 | 12 | @log_execution 13 | def add(a, b): 14 | """Add two numbers and return the result.""" 15 | return a + b 16 | 17 | # Without @wraps, help(add) would show wrapper's info 18 | help(add) # Shows the original docstring 19 | print(f"Function name: {add.__name__}") # Shows "add", not "wrapper" 20 | 21 | result = add(5, 3) 22 | -------------------------------------------------------------------------------- /write-better-funcs/tip5.py: -------------------------------------------------------------------------------- 1 | # returns a list of Fibonacci numbers 2 | def generate_fibonacci_numbers_list(limit): 3 | fibonacci_numbers = [0, 1] 4 | while fibonacci_numbers[-1] + fibonacci_numbers[-2] <= limit: 5 | fibonacci_numbers.append(fibonacci_numbers[-1] + fibonacci_numbers[-2]) 6 | return fibonacci_numbers 7 | 8 | 9 | # use generators instead 10 | from typing import Generator 11 | 12 | def generate_fibonacci_numbers(limit: int) -> Generator[int, None, None]: 13 | a, b = 0, 1 14 | while a <= limit: 15 | yield a 16 | a, b = b, a + b 17 | 18 | # Usage 19 | limit = 100 20 | fibonacci_numbers_generator = generate_fibonacci_numbers(limit) 21 | for num in fibonacci_numbers_generator: 22 | print(num) 23 | -------------------------------------------------------------------------------- /for-else/main.py: -------------------------------------------------------------------------------- 1 | # Example 1: Finding a Prime Number 2 | import math 3 | 4 | def is_prime(n): 5 | if n <= 1: 6 | return False 7 | 8 | for i in range(2, int(math.sqrt(n))+ 1): 9 | if n % i == 0: 10 | print(f"{n} is divisible by {i}. Not a prime number.") 11 | break 12 | else: 13 | # This block executes if the loop did not encounter a break statement 14 | print(f"{n} is a prime number.") 15 | return True 16 | 17 | # Example 2: Searching for an Item in a List 18 | 19 | def search_item(lst, item): 20 | for i in lst: 21 | if i == item: 22 | print(f"Found {item} in the list.") 23 | break 24 | else: 25 | print(f"{item} is not in the list.") 26 | -------------------------------------------------------------------------------- /write-better-funcs/tip3.py: -------------------------------------------------------------------------------- 1 | # takes in an arg that's never used! 2 | def process_student_grades(student_id, grades, course_name, instructor): 3 | average_grade = sum(grades) / len(grades) 4 | return f"Student {student_id} achieved an average grade of {average_grade:.2f} in {course_name}." 5 | 6 | 7 | # better version! 8 | def process_student_grades(student_id: int, grades: list, course_name: str) -> str: 9 | average_grade = sum(grades) / len(grades) 10 | return f"Student {student_id} achieved an average grade of {average_grade:.2f} in {course_name}." 11 | 12 | # Usage 13 | student_id = 12345 14 | grades = [85, 90, 75, 88, 92] 15 | course_name = "Mathematics" 16 | result = process_student_grades(student_id, grades, course_name) 17 | print(result) 18 | -------------------------------------------------------------------------------- /logging/test_div.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logger2 = logging.getLogger(__name__) 4 | logger2.setLevel(logging.INFO) 5 | 6 | # configure the handler and formatter for logger2 7 | handler2 = logging.FileHandler(f"{__name__}.log", mode='w') 8 | formatter2 = logging.Formatter("%(name)s %(asctime)s %(levelname)s %(message)s") 9 | 10 | # add formatter to the handler 11 | handler2.setFormatter(formatter2) 12 | # add handler to the logger 13 | logger2.addHandler(handler2) 14 | 15 | logger2.info(f"Testing the custom logger for module {__name__}...") 16 | 17 | def test_division(x,y): 18 | try: 19 | x/y 20 | logger2.info(f"x/y successful with result: {x/y}.") 21 | except ZeroDivisionError as err: 22 | logger2.exception("ZeroDivisionError") 23 | -------------------------------------------------------------------------------- /generators-101/generators.py: -------------------------------------------------------------------------------- 1 | def get_cubes(num): 2 | cubes = [] 3 | for i in range(num): 4 | cubes.append(i**3) 5 | return cubes 6 | 7 | def get_cubes(num): 8 | cubes = [i**3 for i in range(num)] 9 | return cubes 10 | 11 | # get_cubes() function can be rewritten as a generator function get_cubes_gen() 12 | def get_cubes_gen(num): 13 | for i in range(num): 14 | yield i**3 15 | 16 | cubes_gen = get_cubes_gen(6) 17 | print(cubes_gen) 18 | 19 | # >>> from gen_example import get_cubes_gen 20 | # >>> cubes_gen = get_cubes_gen(6) 21 | # >>> next(cubes_gen) 22 | # 0 23 | # >>> next(cubes_gen) 24 | # 1 25 | # >>> next(cubes_gen) 26 | # 8 27 | # >>> next(cubes_gen) 28 | # 27 29 | # >>> next(cubes_gen) 30 | # 64 31 | # >>> next(cubes_gen) 32 | # 125 33 | -------------------------------------------------------------------------------- /config-management-basics/parsing-toml-files/config.toml: -------------------------------------------------------------------------------- 1 | # Application configuration 2 | title = "My Application" 3 | version = "1.0.0" 4 | 5 | [database] 6 | host = "localhost" 7 | port = 5432 8 | username = "app_user" 9 | password = "secure_password" 10 | databases = ["myapp_db", "myapp_cache"] 11 | pool_size = 10 12 | ssl_enabled = true 13 | 14 | [server] 15 | host = "0.0.0.0" 16 | port = 8000 17 | debug = false 18 | allowed_hosts = ["localhost", "127.0.0.1", "example.com"] 19 | 20 | [logging] 21 | level = "INFO" 22 | format = "%(asctime)s - %(levelname)s - %(message)s" 23 | handlers = ["console", "file"] 24 | 25 | [cache] 26 | enabled = true 27 | ttl = 3600 28 | max_size = 1000 29 | 30 | [features] 31 | enable_api = true 32 | enable_webhooks = false 33 | rate_limit = 100 34 | 35 | -------------------------------------------------------------------------------- /useful-decorators/property_dec.py: -------------------------------------------------------------------------------- 1 | class Temperature: 2 | def __init__(self, celsius=0): 3 | self._celsius = celsius 4 | 5 | @property 6 | def celsius(self): 7 | return self._celsius 8 | 9 | @celsius.setter 10 | def celsius(self, value): 11 | if value < -273.15: 12 | raise ValueError("Temperature below absolute zero!") 13 | self._celsius = value 14 | 15 | @property 16 | def fahrenheit(self): 17 | return self._celsius * 9/5 + 32 18 | 19 | @fahrenheit.setter 20 | def fahrenheit(self, value): 21 | self.celsius = (value - 32) * 5/9 22 | 23 | # Usage 24 | temp = Temperature() 25 | temp.celsius = 25 # Clean attribute-like access with validation 26 | print(f"{temp.celsius}°C = {temp.fahrenheit}°F") 27 | -------------------------------------------------------------------------------- /error-handling/3_exception_chaining.py: -------------------------------------------------------------------------------- 1 | class ConfigError(Exception): 2 | """Configuration-related errors""" 3 | pass 4 | 5 | def load_database_config(): 6 | try: 7 | with open('config/database.yaml') as f: 8 | # Imagine we're using PyYAML here 9 | return yaml.safe_load(f) 10 | except FileNotFoundError as e: 11 | raise ConfigError( 12 | "Database configuration file not found" 13 | ) from e 14 | except yaml.YAMLError as e: 15 | raise ConfigError( 16 | "Invalid database configuration format" 17 | ) from e 18 | 19 | # Usage 20 | try: 21 | config = load_database_config() 22 | except ConfigError as e: 23 | print(f"Configuration error: {e}") 24 | print(f"Original error: {e.__cause__}") 25 | 26 | -------------------------------------------------------------------------------- /pathlib-examples/search.py: -------------------------------------------------------------------------------- 1 | # search.py 2 | from pathlib import Path 3 | 4 | # globbing 5 | def search_and_process_text_files(directory): 6 | path = Path(directory) 7 | path = path.resolve() 8 | for text_file in path.glob('*.txt'): 9 | # process text files as needed 10 | print(f'Processing {text_file}...') 11 | print(text_file.read_text()) 12 | 13 | search_and_process_text_files('new_project') 14 | 15 | # recursive globbing 16 | def search_and_process_text_files(directory): 17 | path = Path(directory) 18 | path = path.resolve() 19 | for text_file in path.rglob('*.txt'): 20 | # process text files as needed 21 | print(f'Processing {text_file}...') 22 | print(text_file.read_text()) 23 | 24 | search_and_process_text_files('new_project') 25 | 26 | -------------------------------------------------------------------------------- /unit-testing/classes/test_book.py: -------------------------------------------------------------------------------- 1 | from book import Book 2 | import unittest 3 | 4 | class TestBook(unittest.TestCase): 5 | def test_reading_time(self): 6 | book_1 = Book('Deep Work','Cal Newport',304,15,0.05) 7 | book_2 = Book('Grit','Angela Duckworth',447,16,0.15) 8 | self.assertEqual(book_1.get_reading_time(), f"{304*1.5} minutes") 9 | self.assertEqual(book_2.get_reading_time(), f"{447*1.5} minutes") 10 | def test_discount(self): 11 | book_1 = Book('Deep Work','Cal Newport',304,15,0.05) 12 | book_2 = Book('Grit','Angela Duckworth',447,16,0.15) 13 | self.assertEqual(book_1.apply_discount(),f"${15 - 15*0.05}") 14 | self.assertEqual(book_2.apply_discount(),f"${16 - 16*0.15}" ) 15 | 16 | if __name__=='__main__': 17 | unittest.main() 18 | -------------------------------------------------------------------------------- /write-better-funcs/README.md: -------------------------------------------------------------------------------- 1 | ## Write Better Python Functions 2 | 3 | Simple but effective tips to write better Python fucntions: 4 | 5 | - [Write Functions That Do Only One Thing](https://github.com/balapriyac/python-basics/blob/main/write-better-funcs/tip1.py) 6 | - [Add Type Hints to Improve Maintainability](https://github.com/balapriyac/python-basics/blob/main/write-better-funcs/tip2.py) 7 | - [Accept Only the Arguments You Actually Need](https://github.com/balapriyac/python-basics/blob/main/write-better-funcs/tip3.py) 8 | - [Enforce Keyword-Only Arguments to Minimize Errors](https://github.com/balapriyac/python-basics/blob/main/write-better-funcs/tip4.py) 9 | - [Don't Return Lists From Functions; Use Generators Instead](https://github.com/balapriyac/python-basics/blob/main/write-better-funcs/tip5.py) 10 | -------------------------------------------------------------------------------- /walrus-operator/antipattern.py: -------------------------------------------------------------------------------- 1 | # Function to compute profit 2 | def compute_profit(sales, cost): 3 | return sales - cost 4 | 5 | # Messy list comprehension with nested walrus operator 6 | sales_data = [(100, 70), (200, 150), (150, 100), (300, 200)] 7 | results = [ 8 | (sales, cost, profit, sales_ratio) 9 | for sales, cost in sales_data 10 | if (profit := compute_profit(sales, cost)) > 50 11 | if (sales_ratio := sales / cost) > 1.5 12 | if (profit_margin := (profit / sales)) > 0.2 13 | ] 14 | 15 | # Example of nested walrus operators 16 | values = [5, 15, 25, 35, 45] 17 | threshold = 20 18 | results = [] 19 | 20 | for value in values: 21 | if (above_threshold := value > threshold) and (incremented := (new_value := value + 10) > 30): 22 | results.append(new_value) 23 | 24 | print(results) 25 | -------------------------------------------------------------------------------- /built-in-data-structs/update-python-dicts/update_dict.py: -------------------------------------------------------------------------------- 1 | books = {'Fluent Python':50, 2 | 'Learning Python':58} 3 | 4 | more_books = {'Effective Python':40, 5 | 'Think Python':29} 6 | 7 | for book in more_books.keys(): 8 | books[book] = more_books[book] 9 | print(books) 10 | 11 | for book, price in more_books.items(): 12 | books[book] = price 13 | print(books) 14 | 15 | # Update a Python Dictionary Using update() 16 | books.update(more_books) 17 | print(books) 18 | 19 | some_more_books = [('Python Cookbook',33),('Python Crash Course',41)] 20 | books.update(some_more_books) 21 | print(books) 22 | 23 | # Updating a Dictionary in the Presence of Repeating Keys 24 | and_some_more = [('Fluent Python',45),('Python for Everybody',30)] 25 | books.update(and_some_more) 26 | print(books) 27 | -------------------------------------------------------------------------------- /useful-decorators/cached_property_dec.py: -------------------------------------------------------------------------------- 1 | from functools import cached_property 2 | import time 3 | 4 | class DataAnalyzer: 5 | def __init__(self, dataset): 6 | self.dataset = dataset 7 | 8 | @cached_property 9 | def complex_analysis(self): 10 | print("Running expensive analysis...") 11 | time.sleep(2) # Simulating heavy computation 12 | return sum(x**2 for x in self.dataset) 13 | 14 | # Usage 15 | analyzer = DataAnalyzer(range(1000000)) 16 | print("First access:") 17 | t1 = time.time() 18 | result1 = analyzer.complex_analysis 19 | t2 = time.time() 20 | print(f"Result: {result1}, Time: {t2-t1:.2f}s") 21 | 22 | print("\nSecond access:") 23 | t1 = time.time() 24 | result2 = analyzer.complex_analysis 25 | t2 = time.time() 26 | print(f"Result: {result2}, Time: {t2-t1:.2f}s") 27 | -------------------------------------------------------------------------------- /pydantic-basics/employees.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "John Doe", 4 | "age": 30, 5 | "email": "john.doe@example.com", 6 | "department": "Engineering", 7 | "employee_id": "EMP001" 8 | }, 9 | { 10 | "name": "Jane Smith", 11 | "age": 25, 12 | "email": "jane.smith@example.com", 13 | "department": "Marketing", 14 | "employee_id": "EMP002" 15 | }, 16 | { 17 | "name": "Alice Brown", 18 | "age": 35, 19 | "email": "invalid-email", 20 | "department": "Finance", 21 | "employee_id": "EMP0034" 22 | }, 23 | { 24 | "name": "Dave West", 25 | "age": 40, 26 | "email": "dave.west@example.com", 27 | "department": "HR", 28 | "employee_id": "EMP005" 29 | } 30 | ] 31 | -------------------------------------------------------------------------------- /sqlite-tut/filter.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | # Define the threshold for the number of orders 4 | order_threshold = 10 5 | 6 | with sqlite3.connect('example.db') as conn: 7 | cursor = conn.cursor() 8 | 9 | # Fetch customers with less than 10 orders 10 | cursor.execute(''' 11 | SELECT id, first_name, last_name, email, num_orders 12 | FROM customers 13 | WHERE num_orders < ? 14 | ''', (order_threshold,)) 15 | 16 | # Fetch all matching customers 17 | filtered_customers = cursor.fetchall() 18 | 19 | # Display filtered customers 20 | if filtered_customers: 21 | print("Customers with less than 10 orders:") 22 | for customer in filtered_customers: 23 | print(customer) 24 | else: 25 | print("No customers found with less than 10 orders.") 26 | -------------------------------------------------------------------------------- /go-intro/loops_conditionals.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | func main() { 6 | // If statement 7 | score := 85 8 | if score >= 90 { 9 | fmt.Println("A grade") 10 | } else if score >= 80 { 11 | fmt.Println("B grade") 12 | } else { 13 | fmt.Println("Lower grade") 14 | } 15 | 16 | // Traditional for loop (similar to C-style for loops) 17 | for i := 0; i < 4; i++ { 18 | fmt.Println("Count:", i) 19 | } 20 | 21 | // For as a while loop 22 | sum := 1 23 | for sum < 10 { 24 | sum += sum 25 | fmt.Println("Sum is now:", sum) 26 | } 27 | 28 | // Iterating with range 29 | fruits := []string{"apple", "banana", "cherry"} 30 | for index, fruit := range fruits { 31 | fmt.Println(index, fruit) 32 | } 33 | // Using _ to ignore the index 34 | for _, fruit := range fruits { 35 | fmt.Println(fruit) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /keyerrors/main.py: -------------------------------------------------------------------------------- 1 | books = { 2 | "1984": "George Orwell", 3 | "To Kill a Mockingbird": "Harper Lee", 4 | "The Great Gatsby": "F. Scott Fitzgerald" 5 | } 6 | 7 | 8 | # handle KeyError explicitly 9 | try: 10 | # Try to access the key "Brave New World" 11 | author = books["Brave New World"] 12 | # Catch the KeyError if the key does not exist 13 | except KeyError: 14 | author = "Book not found" 15 | 16 | print(author) 17 | 18 | # Try to get the value for "Brave New World" 19 | author = books.get("Brave New World", "Book not found") 20 | print(author) 21 | 22 | 23 | # use defaultdict to handle missing keys natively 24 | from collections import defaultdict 25 | 26 | books_default = defaultdict(lambda: "Book not found",books) 27 | # Access the key "Brave New World" 28 | author = books_default["Brave New World"] 29 | print(author) 30 | -------------------------------------------------------------------------------- /useful-decorators/README.md: -------------------------------------------------------------------------------- 1 | Some useful Python decorators explained with examples: 2 | 3 | - [@property](https://github.com/balapriyac/python-basics/blob/main/useful-decorators/property_dec.py) 4 | - [@cached_property](https://github.com/balapriyac/python-basics/blob/main/useful-decorators/cached_property_dec.py) 5 | - [@lru_cache](https://github.com/balapriyac/python-basics/blob/main/useful-decorators/lru_cache_dec.py) 6 | - [@contextmanager](https://github.com/balapriyac/python-basics/blob/main/useful-decorators/contextmanager_dec.py) 7 | - [@singledispatch](https://github.com/balapriyac/python-basics/blob/main/useful-decorators/singledispatch_dec.py) 8 | - [@total_ordering](https://github.com/balapriyac/python-basics/blob/main/useful-decorators/total_ordering_dec.py) 9 | - [@wraps](https://github.com/balapriyac/python-basics/blob/main/useful-decorators/wraps_dec.py) 10 | -------------------------------------------------------------------------------- /useful-decorators/singledispatch_dec.py: -------------------------------------------------------------------------------- 1 | from functools import singledispatch 2 | from datetime import date, datetime 3 | 4 | @singledispatch 5 | def format_output(obj): 6 | return str(obj) 7 | 8 | @format_output.register 9 | def _(obj: int): 10 | return f"INTEGER: {obj:+d}" 11 | 12 | @format_output.register 13 | def _(obj: float): 14 | return f"FLOAT: {obj:.2f}" 15 | 16 | @format_output.register 17 | def _(obj: date): 18 | return f"DATE: {obj.strftime('%Y-%m-%d')}" 19 | 20 | @format_output.register(list) 21 | def _(obj): 22 | return f"LIST: {', '.join(format_output(x) for x in obj)}" 23 | 24 | # Usage 25 | results = [ 26 | format_output("Hello"), 27 | format_output(42), 28 | format_output(-3.14159), 29 | format_output(date(2025, 2, 21)), 30 | format_output([1, 2.5, "three"]) 31 | ] 32 | 33 | for r in results: 34 | print(r) 35 | -------------------------------------------------------------------------------- /python-for-beginners/day-6/day6_examples.py: -------------------------------------------------------------------------------- 1 | try: 2 | with open("employees.txt") as file: 3 | lines = file.readlines() 4 | except FileNotFoundError: 5 | print("Error: File not found.") 6 | lines = [] 7 | 8 | records = [] 9 | 10 | for line in lines: 11 | try: 12 | parts = line.strip().split(",") 13 | if len(parts) != 4: 14 | raise ValueError("Incorrect number of fields") 15 | record = { 16 | "name": parts[0], 17 | "city": parts[1], 18 | "role": parts[2], 19 | "salary": int(parts[3]) 20 | } 21 | records.append(record) 22 | except Exception as e: 23 | print(f"Skipping bad line: {line.strip()} ({e})") 24 | 25 | with open("cleaned_employees.txt", "w") as out: 26 | for r in records: 27 | out.write(f"{r['name']},{r['city']},{r['role']},{r['salary']}\n") 28 | -------------------------------------------------------------------------------- /error-handling/5_try_finally.py: -------------------------------------------------------------------------------- 1 | class ImageProcessor: 2 | def __init__(self): 3 | self.temp_files = [] 4 | 5 | def process_image(self, image_path): 6 | temp_output = f"temp_{image_path}" 7 | self.temp_files.append(temp_output) 8 | 9 | try: 10 | # Process the image 11 | raw_data = self.load_image(image_path) 12 | processed = self.apply_filters(raw_data) 13 | self.save_image(processed, temp_output) 14 | return self.upload_to_cloud(temp_output) 15 | finally: 16 | # Clean up temporary files 17 | for temp_file in self.temp_files: 18 | try: 19 | os.remove(temp_file) 20 | except OSError: 21 | logging.error(f"Failed to remove temp file: {temp_file}") 22 | self.temp_files = [] 23 | 24 | -------------------------------------------------------------------------------- /big-o-examples/README.md: -------------------------------------------------------------------------------- 1 | ## Big O Complexity 2 | ![image](https://github.com/user-attachments/assets/f65d0a0a-f8b6-4ea8-97fc-704631b0363f) 3 | 4 | Examples: 5 | - [Constant time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/o1_example.py) 6 | - [Logarithmic time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/ologn_example.py) 7 | - [Linear time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/on_example.py) 8 | - [Linearithmic time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/onlogn_example.py) 9 | - [Quadratic time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/onsq_example.py) 10 | - [Exponential time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/o2n_example.py) 11 | - [Factorial time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/ofactorialn_example.py) 12 | -------------------------------------------------------------------------------- /sqlite-tut/insert.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | import random 3 | from faker import Faker 4 | 5 | # Initialize Faker object 6 | fake = Faker() 7 | Faker.seed(24) 8 | 9 | # Connect to the db 10 | with sqlite3.connect('example.db') as conn: 11 | cursor = conn.cursor() 12 | 13 | # Insert customer records 14 | num_records = 10 15 | for _ in range(num_records): 16 | first_name = fake.first_name() 17 | last_name = fake.last_name() 18 | email = fake.email() 19 | phone = fake.phone_number() 20 | num_orders = random.randint(0,100) 21 | 22 | cursor.execute(''' 23 | INSERT INTO customers (first_name, last_name, email, phone, num_orders) 24 | VALUES (?, ?, ?, ?, ?) 25 | ''', (first_name, last_name, email, phone, num_orders)) 26 | print(f"{num_records} customer records inserted successfully.") 27 | conn.commit() 28 | cursor.close() 29 | -------------------------------------------------------------------------------- /pydantic-basics/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | from pydantic import BaseModel, EmailStr, ValidationError, validator 3 | 4 | 5 | class Employee(BaseModel): 6 | name: str 7 | age: int 8 | email: EmailStr 9 | department: str 10 | employee_id: str 11 | 12 | @validator("employee_id") 13 | def validate_employee_id(cls, v): 14 | if not v.isalnum() or len(v) != 6: 15 | raise ValueError("Employee ID must be exactly 6 alphanumeric characters") 16 | return v 17 | 18 | 19 | # Load and parse the JSON data 20 | with open("employees.json", "r") as f: 21 | data = json.load(f) 22 | 23 | # Validate each employee record 24 | for record in data: 25 | try: 26 | employee = Employee(**record) 27 | print(f"Valid employee record: {employee.name}") 28 | except ValidationError as e: 29 | print(f"Invalid employee record: {record['name']}") 30 | print(f"Errors: {e.errors()}") 31 | -------------------------------------------------------------------------------- /unit-testing/classes/test_book_1.py: -------------------------------------------------------------------------------- 1 | from book import Book 2 | import unittest 3 | 4 | class TestBook(unittest.TestCase): 5 | def setUp(self): 6 | print("\nRunning setUp method...") 7 | self.book_1 = Book('Deep Work','Cal Newport',304,15,0.05) 8 | self.book_2 = Book('Grit','Angela Duckworth',447,16,0.15) 9 | def tearDown(self): 10 | print("Running tearDown method...") 11 | def test_reading_time(self): 12 | print("Running test_reading_time...") 13 | self.assertEqual(self.book_1.get_reading_time(), f"{304*1.5} minutes") 14 | self.assertEqual(self.book_2.get_reading_time(), f"{447*1.5} minutes") 15 | def test_discount(self): 16 | print("Running test_discount...") 17 | self.assertEqual(self.book_1.apply_discount(),f"${15 - 15*0.05}") 18 | self.assertEqual(self.book_2.apply_discount(),f"${16 - 16*0.15}" ) 19 | 20 | if __name__=='__main__': 21 | unittest.main() 22 | -------------------------------------------------------------------------------- /any-all/any_examples.py: -------------------------------------------------------------------------------- 1 | list_1 = [0,0,0,1,0,0,0,0] 2 | # any(a list with at least one non-zero entry) returns True 3 | print(any(list_1)) 4 | # Output True 5 | 6 | list_2 = [0j,0,0,0.0,0,0,0.0,0] 7 | # any(a list of zeros) returns False 8 | print(any(list_2)) 9 | # Output False 10 | 11 | list_3 = [True, False, False] 12 | # any(a list with at least one True value) returns True 13 | print(any(list_3)) 14 | # Output True 15 | 16 | list_4 = ["","","code more"] 17 | # any(a list with at least one non-empty string) returns True 18 | print(any(list_4)) 19 | # Output True 20 | 21 | list_5 = ["","",""] 22 | # any(a list of empty strings) returns False 23 | print(any(list_5)) 24 | # Output False 25 | 26 | my_string = "coding**is**cool**345" 27 | are_there_digits = [char.isdigit() for char in my_string] 28 | print(any(are_there_digits)) 29 | 30 | # Output True 31 | 32 | my_string = "***456278)))" 33 | num = [char.isalpha() for char in my_string] 34 | print(any(num)) 35 | 36 | # Output False 37 | -------------------------------------------------------------------------------- /working-with-large-datasets/useful_example.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | chunk_size = 200000 4 | product_revenue = {} 5 | 6 | # Read in chunks, using only needed columns 7 | for chunk in pd.read_csv('orders.csv', 8 | chunksize=chunk_size, 9 | usecols=['product_id', 'quantity', 'price']): 10 | 11 | # Calculate revenue for each row 12 | chunk['revenue'] = chunk['quantity'] * chunk['price'] 13 | 14 | # Aggregate by product 15 | chunk_summary = chunk.groupby('product_id')['revenue'].sum() 16 | 17 | # Add to our running totals 18 | for product_id, revenue in chunk_summary.items(): 19 | product_revenue[product_id] = product_revenue.get(product_id, 0) + revenue 20 | 21 | # Convert to dataframe and get top 10 22 | result = pd.DataFrame(list(product_revenue.items()), 23 | columns=['product_id', 'revenue']) 24 | top_10 = result.nlargest(10, 'revenue') 25 | 26 | print(top_10) 27 | -------------------------------------------------------------------------------- /switch-case/main1.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | # List of words to choose from 4 | word_list = ["Python", "programming", "Hello", "world", "context", "Switch"] 5 | 6 | # Randomly select a word from the list 7 | word = random.choice(word_list) 8 | 9 | # Provide context and available options to the user 10 | print("Welcome! You have a randomly selected word.") 11 | print("Choose an option to manipulate the word:") 12 | print("1. Lowercase") 13 | print("2. Uppercase") 14 | print("3. Titlecase") 15 | print("4. Swapcase") 16 | print("5. Default behavior") 17 | 18 | # Get user option 19 | option = int(input("Enter your option: ")) 20 | 21 | # if-elif-else 22 | # if-elif-else 23 | 24 | if option == 1: 25 | result = word.lower() 26 | elif option == 2: 27 | result = word.upper() 28 | elif option == 3: 29 | result = word.title() 30 | elif option == 4: 31 | result = word.swapcase() 32 | else: 33 | result = word 34 | 35 | print(f"Your random word is {word} and the result is {result}") 36 | -------------------------------------------------------------------------------- /sql-index/main.py: -------------------------------------------------------------------------------- 1 | # imports 2 | import sqlite3 3 | from faker import Faker 4 | 5 | # connect to the db 6 | db_conn = sqlite3.connect('people_db.db') 7 | db_cursor = db_conn.cursor() 8 | 9 | # create table 10 | db_cursor.execute('''CREATE TABLE people ( 11 | id INTEGER PRIMARY KEY, 12 | name TEXT, 13 | email TEXT, 14 | job TEXT)''') 15 | 16 | 17 | # create and insert records 18 | fake = Faker() 19 | Faker.seed(42) 20 | 21 | num_records = 100000 22 | 23 | for _ in range(num_records): 24 | first = fake.first_name() 25 | last = fake.last_name() 26 | name = f"{first} {last}" 27 | domain = fake.domain_name() 28 | email = f"{first}.{last}@{domain}" 29 | job = fake.job() 30 | db_cursor.execute('INSERT INTO people (name, email, job) VALUES (?,?,?)', (name,email,job)) 31 | 32 | # commit the transaction and close the cursor and db connection 33 | db_conn.commit() 34 | db_cursor.close() 35 | db_conn.close() 36 | -------------------------------------------------------------------------------- /caching/main.py: -------------------------------------------------------------------------------- 1 | from functools import cache, lru_cache 2 | import timeit 3 | 4 | # without caching 5 | def fibonacci_no_cache(n): 6 | if n <= 1: 7 | return n 8 | return fibonacci_no_cache(n-1) + fibonacci_no_cache(n-2) 9 | 10 | # with cache 11 | @cache 12 | def fibonacci_cache(n): 13 | if n <= 1: 14 | return n 15 | return fibonacci_cache(n-1) + fibonacci_cache(n-2) 16 | 17 | # with LRU cache 18 | @lru_cache(maxsize=None) 19 | def fibonacci_lru_cache(n): 20 | if n <= 1: 21 | return n 22 | return fibonacci_lru_cache(n-1) + fibonacci_lru_cache(n-2) 23 | 24 | 25 | n = 35 26 | 27 | no_cache_time = timeit.timeit(lambda: fibonacci_no_cache(n), number=1) 28 | cache_time = timeit.timeit(lambda: fibonacci_cache(n), number=1) 29 | lru_cache_time = timeit.timeit(lambda: fibonacci_lru_cache(n), number=1) 30 | 31 | print(f"Time without cache: {no_cache_time:.6f} seconds") 32 | print(f"Time with cache: {cache_time:.6f} seconds") 33 | print(f"Time with LRU cache: {lru_cache_time:.6f} seconds") 34 | -------------------------------------------------------------------------------- /pytest/todo/todo_manager.py: -------------------------------------------------------------------------------- 1 | # todo/todo_manager.py 2 | class ToDoManager: 3 | def __init__(self): 4 | self.tasks = [] 5 | 6 | def add_task(self, task): 7 | if not task: 8 | raise ValueError("Task cannot be empty.") 9 | self.tasks.append({"task": task, "completed": False}) 10 | return task 11 | 12 | def remove_task(self, task): 13 | for t in self.tasks: 14 | if t["task"] == task: 15 | self.tasks.remove(t) 16 | return task 17 | raise ValueError("Task not found.") 18 | 19 | def mark_completed(self, task): 20 | for t in self.tasks: 21 | if t["task"] == task: 22 | t["completed"] = True 23 | return task 24 | raise ValueError("Task not found.") 25 | 26 | def get_tasks(self, completed=None): 27 | if completed is None: 28 | return self.tasks 29 | return [t for t in self.tasks if t["completed"] == completed] 30 | -------------------------------------------------------------------------------- /sqlite-tut/read_and_update.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | # Connect to the db 4 | with sqlite3.connect('example.db') as conn: 5 | cursor = conn.cursor() 6 | 7 | # Fetch and display all customers 8 | cursor.execute('SELECT id, first_name, last_name, email, num_orders FROM customers') 9 | all_customers = cursor.fetchall() 10 | print("All Customers:") 11 | for customer in all_customers: 12 | print(customer) 13 | 14 | # Update num_orders for a specific customer 15 | if all_customers: 16 | customer_id = all_customers[0][0] # Take the ID of the first customer 17 | new_num_orders = all_customers[0][4] + 1 # Increment num_orders by 1 18 | cursor.execute(''' 19 | UPDATE customers 20 | SET num_orders = ? 21 | WHERE id = ? 22 | ''', (new_num_orders, customer_id)) 23 | print(f"Orders updated for customer ID {customer_id}: now has {new_num_orders} orders.") 24 | 25 | conn.commit() 26 | cursor.close() 27 | -------------------------------------------------------------------------------- /switch-case/main3.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | # List of words to choose from 4 | word_list = ["Python", "programming", "Hello", "world", "context", "Switch"] 5 | 6 | # Randomly select a word from the list 7 | word = random.choice(word_list) 8 | 9 | # Provide context and available options to the user 10 | print("Welcome! You have a randomly selected word.") 11 | print("Choose an option to manipulate the word:") 12 | print("1. Lowercase") 13 | print("2. Uppercase") 14 | print("3. Titlecase") 15 | print("4. Swapcase") 16 | print("5. Default behavior") 17 | 18 | # Get user option 19 | option = int(input("Enter your option: ")) 20 | 21 | match option: 22 | case 1: 23 | result = word.lower() 24 | case 2: 25 | result = word.upper() 26 | case 3: 27 | result = word.title() 28 | case 4: 29 | result = word.swapcase() 30 | case _: 31 | result = word # Default behavior, return the string as is 32 | 33 | print(f"Your random word is {word} and the result is {result}.") 34 | -------------------------------------------------------------------------------- /logging/main.py: -------------------------------------------------------------------------------- 1 | import sentry_sdk 2 | 3 | sentry_sdk.init( 4 | dsn="", 5 | traces_sample_rate=0.85, 6 | ) 7 | 8 | import logging 9 | from test_div import test_division 10 | 11 | # get a custom logger & set the logging level 12 | py_logger = logging.getLogger(__name__) 13 | py_logger.setLevel(logging.INFO) 14 | 15 | # configure the handler and formatter as needed 16 | py_handler = logging.FileHandler(f"{__name__}.log", mode='w') 17 | py_formatter = logging.Formatter("%(name)s %(asctime)s %(levelname)s %(message)s") 18 | 19 | # add formatter to the handler 20 | py_handler.setFormatter(py_formatter) 21 | # add handler to the logger 22 | py_logger.addHandler(py_handler) 23 | 24 | py_logger.info(f"Testing the custom logger for module {__name__}...") 25 | 26 | x_vals = [2,3,6,4,10] 27 | y_vals = [5,7,12,0,1] 28 | 29 | for x_val,y_val in zip(x_vals,y_vals): 30 | x,y = x_val, y_val 31 | # call test_division 32 | test_division(x,y) 33 | py_logger.info(f"Call test_division with args {x} and {y}") 34 | -------------------------------------------------------------------------------- /write-better-funcs/tip4.py: -------------------------------------------------------------------------------- 1 | # example fn. for processing transaction 2 | def process_payment(transaction_id: int, amount: float, currency: str, description: str = None): 3 | print(f"Processing transaction {transaction_id}...") 4 | print(f"Amount: {amount} {currency}") 5 | if description: 6 | print(f"Description: {description}") 7 | 8 | # Usage 9 | process_payment(1234, 100.0, 'USD', 'Payment for services') 10 | 11 | # enforce keyword-only arguments to minimize errors 12 | # make the optional `description` arg keyword-only 13 | def process_payment(transaction_id: int, amount: float, currency: str, *, description: str = None): 14 | print(f"Processing transaction {transaction_id}:") 15 | print(f"Amount: {amount} {currency}") 16 | if description: 17 | print(f"Description: {description}") 18 | 19 | # Usage 20 | process_payment(1234, 100.0, 'USD', description='Payment for services') 21 | process_payment(5678, 150.0, 'EUR', 'Invoice payment') # throws error as we try to pass in more positional args than allowed! 22 | -------------------------------------------------------------------------------- /error-handling/1_context_manager.py: -------------------------------------------------------------------------------- 1 | class DatabaseConnection: 2 | def __init__(self, connection_string): 3 | self.connection_string = connection_string 4 | self.conn = None 5 | 6 | def __enter__(self): 7 | try: 8 | print(f"Connecting to database: {self.connection_string}") 9 | # In reality, you'd use something like psycopg2 or SQLAlchemy here 10 | self.conn = "database_connection" 11 | return self 12 | except Exception as e: 13 | raise ConnectionError(f"Failed to connect: {str(e)}") 14 | 15 | def __exit__(self, exc_type, exc_val, exc_tb): 16 | print("Closing database connection") 17 | if self.conn: 18 | # Close connection here 19 | self.conn = None 20 | return False # Don't suppress exceptions 21 | 22 | # Usage 23 | with DatabaseConnection("postgresql://localhost:5432/mydb") as db: 24 | # Do database operations 25 | # Connection is automatically closed after this block 26 | pass 27 | 28 | -------------------------------------------------------------------------------- /python-lists/basics.py: -------------------------------------------------------------------------------- 1 | shopping_list = ['apples','pens','oatmeal cookies','notepad','brushes','paint'] 2 | 3 | shopping_list[2] = 'candy' 4 | print(shopping_list) 5 | 6 | for item in shopping_list: 7 | print(item) 8 | 9 | print(shopping_list[2:]) 10 | # Output: ['candy', 'notepad', 'brushes', 'paint'] 11 | 12 | print(shopping_list[:2]) 13 | # Output: ['apples', 'pens'] 14 | 15 | print(shopping_list[:]) 16 | # Output: ['apples', 'pens', 'candy', 'notepad', 'brushes', 'paint'] 17 | 18 | print(len(shopping_list)) 19 | # 6 20 | 21 | print(max(shopping_list)) 22 | # pens 23 | 24 | print(min(shopping_list)) 25 | # apples 26 | 27 | # list methods 28 | shopping_list.append('grapes') 29 | print(shopping_list) 30 | 31 | shopping_list.extend(['protein bars','cheese']) 32 | print(shopping_list) 33 | 34 | last_element = shopping_list.pop() 35 | print(shopping_list) 36 | print(last_element) 37 | 38 | not_needed = shopping_list.pop(2) 39 | print(not_needed) 40 | 41 | del shopping_list[1] 42 | print(shopping_list) 43 | 44 | shopping_list.sort() 45 | print(shopping_list) 46 | 47 | list_2 = shopping_list + ['noodles','almonds'] 48 | print(list_2) 49 | -------------------------------------------------------------------------------- /error-handling/4_error_handling_decorators.py: -------------------------------------------------------------------------------- 1 | from functools import wraps 2 | import logging 3 | 4 | def handle_api_errors(retries=3, fallback_value=None): 5 | def decorator(func): 6 | @wraps(func) 7 | def wrapper(*args, **kwargs): 8 | for attempt in range(retries): 9 | try: 10 | return func(*args, **kwargs) 11 | except (ConnectionError, TimeoutError) as e: 12 | logging.error( 13 | f"API call failed (attempt {attempt + 1}/{retries}): {str(e)}" 14 | ) 15 | if attempt == retries - 1: 16 | if fallback_value is not None: 17 | return fallback_value 18 | raise 19 | except Exception as e: 20 | logging.error(f"Unexpected error: {str(e)}") 21 | raise 22 | return wrapper 23 | return decorator 24 | 25 | # Usage 26 | @handle_api_errors(retries=3, fallback_value=[]) 27 | def fetch_user_data(user_id): 28 | # Make API call here 29 | pass 30 | 31 | -------------------------------------------------------------------------------- /big-o-examples/ofactorialn_example.py: -------------------------------------------------------------------------------- 1 | def generate_permutations(arr): 2 | if len(arr) <= 1: 3 | return [arr] 4 | 5 | perms = [] 6 | for i in range(len(arr)): 7 | # Remove current element 8 | current = arr[i] 9 | remaining = arr[:i] + arr[i+1:] 10 | 11 | # Generate permutations of remaining elements 12 | for p in generate_permutations(remaining): 13 | perms.append([current] + p) 14 | 15 | return perms 16 | 17 | def traveling_salesman_bruteforce(graph): 18 | nodes = list(graph.keys()) 19 | shortest_path = float('inf') 20 | 21 | for path in generate_permutations(nodes[1:]): # Start from node 0 22 | current_path_length = graph[nodes[0]][path[0]] # First edge 23 | 24 | # Calculate path length 25 | for i in range(len(path) - 1): 26 | current_path_length += graph[path[i]][path[i + 1]] 27 | 28 | # Add return to start 29 | current_path_length += graph[path[-1]][nodes[0]] 30 | 31 | shortest_path = min(shortest_path, current_path_length) 32 | 33 | return shortest_path 34 | -------------------------------------------------------------------------------- /walrus-operator/main.py: -------------------------------------------------------------------------------- 1 | # Example 1 2 | # Without walrus operator 3 | data = input("Enter your data: ") 4 | while len(data) > 0: 5 | print("You entered:", data) 6 | data = input("Enter your data: ") 7 | 8 | # With walrus operator 9 | while (data := input("Enter your data: ")) != "": 10 | print("You entered:", data) 11 | 12 | # Example 2 13 | # Without Walrus Operator 14 | # Function to compute profit 15 | def compute_profit(sales, cost): 16 | return sales - cost 17 | 18 | sales_data = [(100, 70), (200, 150), (150, 100), (300, 200)] 19 | profits = [] 20 | for sales, cost in sales_data: 21 | profit = compute_profit(sales, cost) 22 | if profit > 50: 23 | profits.append(profit) 24 | 25 | # list comp. version without walrus operator 26 | # Without Walrus Operator 27 | sales_data = [(100, 70), (200, 150), (150, 100), (300, 200)] 28 | profits = [compute_profit(sales, cost) for sales, cost in sales_data if compute_profit(sales, cost) > 50] 29 | 30 | # List comp. with Walrus Operator 31 | sales_data = [(100, 70), (200, 150), (150, 100), (300, 200)] 32 | profits = [profit for sales, cost in sales_data if (profit := compute_profit(sales, cost)) > 50] 33 | -------------------------------------------------------------------------------- /unit-testing/classes/test_book_2.py: -------------------------------------------------------------------------------- 1 | from book import Book 2 | import unittest 3 | 4 | class TestBook(unittest.TestCase): 5 | @classmethod 6 | def setUpClass(cls): 7 | print("\nsetUpClass method: Runs before all tests...") 8 | def setUp(self): 9 | print("\nRunning setUp method...") 10 | self.book_1 = Book('Deep Work','Cal Newport',304,15,0.05) 11 | self.book_2 = Book('Grit','Angela Duckworth',447,16,0.15) 12 | def tearDown(self): 13 | print("Running tearDown method...") 14 | def test_reading_time(self): 15 | print("Running test_reading_time...") 16 | self.assertEqual(self.book_1.get_reading_time(), f"{304*1.5} minutes") 17 | self.assertEqual(self.book_2.get_reading_time(), f"{447*1.5} minutes") 18 | def test_discount(self): 19 | print("Running test_discount...") 20 | self.assertEqual(self.book_1.apply_discount(),f"${15 - 15*0.05}") 21 | self.assertEqual(self.book_2.apply_discount(),f"${16 - 16*0.15}" ) 22 | @classmethod 23 | def tearDownClass(cls): 24 | print("\ntearDownClass method: Runs after all tests...") 25 | 26 | if __name__=='__main__': 27 | unittest.main() 28 | -------------------------------------------------------------------------------- /python-for-beginners/day-3/day3_examples.py: -------------------------------------------------------------------------------- 1 | titles = [ 2 | " Data Scientist\n", 3 | "data scientist", 4 | "Senior Data Scientist ", 5 | "DATA scientist", 6 | "Data engineer", 7 | "Data Scientist" 8 | ] 9 | 10 | cleaned = [title.strip().lower() for title in titles] 11 | print(cleaned) 12 | 13 | standardized = [] 14 | 15 | for title in cleaned: 16 | if "data scientist" in title: 17 | standardized.append("data scientist") 18 | else: 19 | standardized.append(title) 20 | 21 | text = " The price is $5,000! " 22 | 23 | # Clean up 24 | clean = text.strip().lower().replace("$", "").replace(",", "").replace("!", "") 25 | print(clean) 26 | 27 | email = " Alice.Johnson@Example.com " 28 | email = email.strip().lower() 29 | 30 | username, domain = email.split("@") 31 | 32 | print(f"User: {username}, Domain: {domain}") 33 | 34 | comment = "I'm learning Python and SQL for data jobs." 35 | 36 | if "python" in comment.lower(): 37 | print("Mentioned Python") 38 | 39 | # Word count 40 | word_count = len(clean.split()) 41 | 42 | # Contains digit 43 | has_number = any(char.isdigit() for char in clean) 44 | 45 | print(word_count) 46 | print(has_number) 47 | -------------------------------------------------------------------------------- /useful-decorators/total_ordering_dec.py: -------------------------------------------------------------------------------- 1 | from functools import total_ordering 2 | 3 | @total_ordering 4 | class Version: 5 | def __init__(self, major, minor, patch): 6 | self.major = major 7 | self.minor = minor 8 | self.patch = patch 9 | 10 | def __eq__(self, other): 11 | if not isinstance(other, Version): 12 | return NotImplemented 13 | return (self.major, self.minor, self.patch) == (other.major, other.minor, other.patch) 14 | 15 | def __lt__(self, other): 16 | if not isinstance(other, Version): 17 | return NotImplemented 18 | return (self.major, self.minor, self.patch) < (other.major, other.minor, other.patch) 19 | 20 | def __repr__(self): 21 | return f"v{self.major}.{self.minor}.{self.patch}" 22 | 23 | # Usage 24 | versions = [ 25 | Version(2, 0, 0), 26 | Version(1, 9, 5), 27 | Version(1, 11, 0), 28 | Version(2, 0, 1) 29 | ] 30 | 31 | print(f"Sorted versions: {sorted(versions)}") 32 | print(f"v1.9.5 > v1.11.0: {Version(1, 9, 5) > Version(1, 11, 0)}") 33 | print(f"v2.0.0 >= v2.0.0: {Version(2, 0, 0) >= Version(2, 0, 0)}") 34 | print(f"v2.0.1 <= v2.0.0: {Version(2, 0, 1) <= Version(2, 0, 0)}") 35 | -------------------------------------------------------------------------------- /natural-sorting/main.py: -------------------------------------------------------------------------------- 1 | import natsort 2 | 3 | # List of filenames 4 | filenames = ["file10.txt", "file2.txt", "file1.txt"] 5 | 6 | # Sort filenames naturally 7 | sorted_filenames = natsort.natsorted(filenames) 8 | 9 | print(sorted_filenames) 10 | 11 | # List of version numbers 12 | versions = ["v-1.10", "v-1.2", "v-1.5"] 13 | 14 | # Sort versions naturally 15 | sorted_versions = natsort.natsorted(versions) 16 | 17 | print(sorted_versions) 18 | 19 | # customize sorting w/ a key 20 | # List of tuples containing filename and size 21 | file_data = [ 22 | ("data_20230101_080000.csv", 100), 23 | ("data_20221231_235959.csv", 150), 24 | ("data_20230201_120000.csv", 120), 25 | ("data_20230115_093000.csv", 80) 26 | ] 27 | 28 | # Sort file data based on file size 29 | sorted_file_data = natsort.natsorted(file_data, key=lambda x:x[1]) 30 | 31 | # Print sorted file data 32 | for filename, size in sorted_file_data: 33 | print(filename, size) 34 | 35 | # Case-insensitive sorting 36 | # List of strings with mixed case 37 | words = ["apple", "Banana", "cat", "Dog", "Elephant"] 38 | 39 | # Sort words naturally with case-insensitivity 40 | sorted_words = natsort.natsorted(words, alg=natsort.ns.IGNORECASE) 41 | 42 | print(sorted_words) 43 | -------------------------------------------------------------------------------- /write-better-funcs/tip2.py: -------------------------------------------------------------------------------- 1 | # fn. to process orders 2 | def process_orders(orders): 3 | total_quantity = sum(order['quantity'] for order in orders) 4 | total_value = sum(order['quantity'] * order['price'] for order in orders) 5 | return { 6 | 'total_quantity': total_quantity, 7 | 'total_value': total_value 8 | } 9 | 10 | # Sample data 11 | orders = [ 12 | {'price': 100.0, 'quantity': 2}, 13 | {'price': 50.0, 'quantity': 5}, 14 | {'price': 150.0, 'quantity': 1} 15 | ] 16 | 17 | # Usage 18 | result = process_orders(orders) 19 | print(result) 20 | 21 | # modified with type hints 22 | from typing import List, Dict 23 | 24 | def process_orders(orders: List[Dict[str, float | int]]) -> Dict[str, float | int]: 25 | total_quantity = sum(order['quantity'] for order in orders) 26 | total_value = sum(order['quantity'] * order['price'] for order in orders) 27 | return { 28 | 'total_quantity': total_quantity, 29 | 'total_value': total_value 30 | } 31 | 32 | # Sample data 33 | orders = [ 34 | {'price': 100.0, 'quantity': 2}, 35 | {'price': 50.0, 'quantity': 5}, 36 | {'price': 150.0, 'quantity': 1} 37 | ] 38 | 39 | # Usage 40 | result = process_orders(orders) 41 | print(result) 42 | -------------------------------------------------------------------------------- /pathlib-examples/organize.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | def organize_files_by_extension(path_to_dir): 4 | path = Path(path_to_dir).resolve() 5 | print(f"Resolved path: {path}") 6 | 7 | if path.exists() and path.is_dir(): 8 | print(f"The directory {path} exists. Proceeding with file organization...") 9 | 10 | # List all items in the directory 11 | for item in path.iterdir(): 12 | print(f"Found item: {item}") 13 | if item.is_file(): 14 | extension = item.suffix.lower() 15 | target_dir = path / extension[1:] # Remove the leading dot 16 | 17 | # Ensure the target directory exists 18 | target_dir.mkdir(exist_ok=True) 19 | new_path = target_dir / item.name 20 | 21 | # Move the file 22 | item.rename(new_path) 23 | 24 | # Confirm the file has been moved 25 | if new_path.exists(): 26 | print(f"Successfully moved {item} to {new_path}") 27 | else: 28 | print(f"Failed to move {item} to {new_path}") 29 | 30 | else: 31 | print(f"Error: The directory {path} does not exist or is not a directory.") 32 | 33 | organize_files_by_extension('new_project') 34 | -------------------------------------------------------------------------------- /function-args/main.py: -------------------------------------------------------------------------------- 1 | # Positional Arguments 2 | def meet(name,job_title,city): 3 | return f"Meet {name}, a {job_title} from {city}." 4 | 5 | meet1 = meet('James','Writer','Nashville') 6 | print(meet1) 7 | 8 | # Keyword Arguments 9 | meet3 = meet(city='Madison',name='Ashley',job_title='Developer') 10 | print(meet3) 11 | 12 | # Variable Number of Positional Arguments 13 | def reverse_strings(*strings): 14 | reversed_strs = [] 15 | for string in strings: 16 | reversed_strs.append(string[::-1]) 17 | return reversed_strs 18 | 19 | rev_strs2 = reverse_strings('Coding','Is','Fun') 20 | print(rev_strs2) # ['gnidoC', 'sI', 'nuF'] 21 | 22 | # Variable Number of Keyword Arguments 23 | def running_sum(**nums): 24 | sum = 0 25 | for key,val in nums.items(): 26 | sum+=val 27 | return sum 28 | 29 | sum1 = running_sum(a=1,b=5,c=10) 30 | print(sum1) 31 | 32 | sum2 = running_sum(num1=7,num2=20) 33 | print(sum2) 34 | 35 | # Using Default Values for Arguments 36 | def greet(name='there'): 37 | print(f"Hello {name}!") 38 | 39 | # Mutable Default Arguments - The Curious Case 40 | def append_to_list(elt,py_list=[]): 41 | py_list.append(elt) 42 | return py_list 43 | 44 | greet('Jane') 45 | 46 | greet() # Hello there! 47 | 48 | -------------------------------------------------------------------------------- /go-intro/data_structures_example.go: -------------------------------------------------------------------------------- 1 | // You can edit this code! 2 | // Click here and start typing. 3 | package main 4 | 5 | import "fmt" 6 | 7 | func main() { 8 | // Declaring and initializing an array 9 | var colors [3]string 10 | colors[0] = "Red" 11 | colors[1] = "Green" 12 | colors[2] = "Blue" 13 | 14 | fmt.Println(colors) 15 | 16 | // Array literal 17 | numbers := [5]int{1, 2, 3, 4, 5} 18 | fmt.Println(numbers) 19 | 20 | // Creating a slice 21 | fruits := []string{"Apple", "Banana", "Cherry"} 22 | // Adding elements to a slice 23 | fruits = append(fruits, "Date") 24 | 25 | // Slicing operations 26 | //firstTwo := fruits[:2] // ["Apple", "Banana"] 27 | //lastTwo := fruits[1:3] // ["Banana", "Cherry"] 28 | 29 | // Creating a map 30 | ages := map[string]int{ 31 | "Alice": 25, 32 | "Bob": 30, 33 | } 34 | 35 | // Working with maps 36 | fmt.Println(ages["Alice"]) // 25 37 | ages["Charlie"] = 22 // Add a new entry 38 | ages["Alice"] = 26 // Update an existing entry 39 | 40 | // Check if a key exists 41 | age, exists := ages["Dave"] 42 | if exists { 43 | fmt.Println("Dave's age:", age) 44 | } else { 45 | fmt.Println("Dave not in map") 46 | } 47 | 48 | // Delete an entry 49 | delete(ages, "Bob") 50 | 51 | } 52 | -------------------------------------------------------------------------------- /python-for-beginners/day-2/day2_examples.py: -------------------------------------------------------------------------------- 1 | with open("scores.txt", "r") as file: 2 | scores = [float(line.strip()) for line in file] 3 | 4 | print(scores) 5 | 6 | average = sum(scores) / len(scores) 7 | print(f"Average score: {average:.2f}") 8 | 9 | 10 | with open("people.txt", "r") as file: 11 | records = [] 12 | for line in file: 13 | name, age = line.strip().split(",") 14 | records.append((name.strip(), int(age.strip()))) 15 | 16 | for person in records: 17 | name, age = person 18 | if age > 30: 19 | print(f"{name} is over 30.") 20 | 21 | people = [] 22 | 23 | with open("people.txt", "r") as file: 24 | for line in file: 25 | name, age = line.strip().split(",") 26 | person = { 27 | "name": name.strip(), 28 | "age": int(age.strip()) 29 | } 30 | people.append(person) 31 | 32 | for person in people: 33 | if person["age"] < 60: 34 | print(f"{person['name']} is perhaps a working professional.") 35 | 36 | domains = set() 37 | 38 | with open("emails.txt", "r") as file: 39 | for line in file: 40 | email = line.strip().lower() 41 | if "@" in email: 42 | domain = email.split("@")[1] 43 | domains.add(domain) 44 | 45 | print(domains) 46 | 47 | -------------------------------------------------------------------------------- /switch-case/main2.py: -------------------------------------------------------------------------------- 1 | import random 2 | 3 | # List of words to choose from 4 | word_list = ["Python", "programming", "Hello", "world", "context", "Switch"] 5 | 6 | # Randomly select a word from the list 7 | word = random.choice(word_list) 8 | 9 | # Provide context and available options to the user 10 | print("Welcome! You have a randomly selected word.") 11 | print("Choose an option to manipulate the word:") 12 | print("1. Lowercase") 13 | print("2. Uppercase") 14 | print("3. Titlecase") 15 | print("4. Swapcase") 16 | print("5. Default behavior") 17 | 18 | # Get user option 19 | option = int(input("Enter your option: ")) 20 | 21 | # functions & dictionary mapping 22 | # Define functions for each option 23 | def lower_case(word): 24 | return word.lower() 25 | 26 | def upper_case(word): 27 | return word.upper() 28 | 29 | def title_case(word): 30 | return word.title() 31 | 32 | def swap_case(word): 33 | return word.swapcase() 34 | 35 | # Store functions in a dictionary 36 | options = { 37 | 1: lower_case, 38 | 2: upper_case, 39 | 3: title_case, 40 | 4: swap_case, 41 | } 42 | 43 | # Use the dictionary to select and call the appropriate function 44 | result = options.get(option, lambda x: x)(word) 45 | 46 | print(f"Your random word is {word} and the result is {result}") 47 | -------------------------------------------------------------------------------- /error-handling/2_custom_exceptions.py: -------------------------------------------------------------------------------- 1 | class OrderError(Exception): 2 | """Base exception for order-related errors""" 3 | pass 4 | 5 | class PaymentError(OrderError): 6 | """Raised when payment processing fails""" 7 | def __init__(self, message, transaction_id=None): 8 | self.transaction_id = transaction_id 9 | super().__init__(f"Payment failed: {message}") 10 | 11 | class InventoryError(OrderError): 12 | """Raised when inventory is insufficient""" 13 | def __init__(self, product_id, requested, available): 14 | self.product_id = product_id 15 | self.requested = requested 16 | self.available = available 17 | super().__init__( 18 | f"Insufficient inventory for product {product_id}: " 19 | f"requested {requested}, available {available}" 20 | ) 21 | 22 | # Usage example 23 | def process_order(order): 24 | try: 25 | check_inventory(order) 26 | process_payment(order) 27 | except InventoryError as e: 28 | # Handle inventory issues 29 | notify_inventory_team(e.product_id) 30 | raise 31 | except PaymentError as e: 32 | # Handle payment issues 33 | if e.transaction_id: 34 | reverse_transaction(e.transaction_id) 35 | raise 36 | 37 | -------------------------------------------------------------------------------- /write-better-funcs/tip1.py: -------------------------------------------------------------------------------- 1 | # fn. to analyze sales data, calculate sales metrics, and write it to a file 2 | def analyze_and_report_sales(data, report_filename): 3 | total_sales = sum(item['price'] * item['quantity'] for item in data) 4 | average_sales = total_sales / len(data) 5 | 6 | with open(report_filename, 'w') as report_file: 7 | report_file.write(f"Total Sales: {total_sales}\n") 8 | report_file.write(f"Average Sales: {average_sales}\n") 9 | 10 | return total_sales, average_sales 11 | 12 | 13 | # refactored into two funcs: one to calculate metrics and another to write sales report 14 | def calculate_sales_metrics(data): 15 | total_sales = sum(item['price'] * item['quantity'] for item in data) 16 | average_sales = total_sales / len(data) 17 | return total_sales, average_sales 18 | 19 | def write_sales_report(report_filename, total_sales, average_sales): 20 | with open(report_filename, 'w') as report_file: 21 | report_file.write(f"Total Sales: {total_sales}\n") 22 | report_file.write(f"Average Sales: {average_sales}\n") 23 | 24 | # Usage 25 | data = [{'price': 100, 'quantity': 2}, {'price': 200, 'quantity': 1}] 26 | total_sales, average_sales = calculate_sales_metrics(data) 27 | write_sales_report('sales_report.txt', total_sales, average_sales) 28 | -------------------------------------------------------------------------------- /handle-large-datasets/tip_2.py: -------------------------------------------------------------------------------- 1 | # useful tips that do not focus on a specific dataset 2 | # We’ll use generic filenames like large_dataset.csv in the code examples 3 | 4 | # Go Parallel with Multiprocessing 5 | 6 | import pandas as pd 7 | import numpy as np 8 | from multiprocessing import Pool 9 | 10 | # Function to clean and normalize data for each chunk 11 | def clean_and_normalize(df_chunk): 12 | # Remove top 5% as outliers in the 'price' column 13 | df_chunk = df_chunk[df_chunk['price'] < df_chunk['price'].quantile(0.95)] 14 | 15 | # Normalize the 'price' column 16 | df_chunk['price'] = (df_chunk['price'] - df_chunk['price'].min()) / (df_chunk['price'].max() - df_chunk['price'].min()) 17 | return df_chunk 18 | 19 | # Function to read data in chunks and process it in parallel 20 | def process_in_chunks(file_name, chunk_size): 21 | chunks = pd.read_csv(file_name, chunksize=chunk_size) 22 | 23 | with Pool(4) as pool: # Adjust the number of processes for your CPU 24 | # Process each chunk in parallel and combine results 25 | cleaned_data = pd.concat(pool.map(clean_and_normalize, chunks)) 26 | 27 | return cleaned_data 28 | 29 | if __name__ == "__main__": 30 | cleaned_df = process_in_chunks('large_house_data.csv', chunk_size=100000) 31 | print(cleaned_df.head()) 32 | -------------------------------------------------------------------------------- /timedelta/examples.py: -------------------------------------------------------------------------------- 1 | # Import Necessary Modules 2 | from datetime import date 3 | from datetime import time 4 | from datetime import datetime 5 | from datetime import timedelta 6 | 7 | time_delt1 = timedelta(days= 270, hours = 9, minutes = 18) 8 | print(time_delt1) 9 | 10 | # Sample Output: 270 days, 9:18:00 11 | 12 | # to create reference, use current date and time 13 | time_now = datetime.now() 14 | print(time_now) 15 | 16 | # check what date it'll be after time_delt1 17 | future_date1 = time_now + time_delt1 18 | print(future_date1) 19 | 20 | # What day will it be after 189 days 21 | future_date2 = time_now + timedelta(days=189) 22 | print(future_date2) 23 | 24 | # What day would it have been 189 days ago 25 | past_date1 = time_now - timedelta(days=189) 26 | print(past_date1) 27 | 28 | # create reference objects for today, and teachers' day 29 | teachers_day = date(time_now.year, 9, 5) 30 | today = date.today() 31 | 32 | # calculate number of days to teachers' day. 33 | time_to_td = teachers_day - today 34 | print(f"Teachers' day is {time_to_td.days} days away") 35 | 36 | # check if teachers' day has passed 37 | if teachers_day < today: 38 | print(f"This year's Teachers' Day was {(today-teachers_day).days} days ago") 39 | time_to_td = teachers_day - today 40 | print(f"Next year's Teachers' day is {time_to_td.days} days away") 41 | -------------------------------------------------------------------------------- /tracemalloc-tutorial/main.py: -------------------------------------------------------------------------------- 1 | # main.py 2 | import pandas as pd 3 | import tracemalloc 4 | 5 | def load_data(file_path): 6 | print("Loading data...") 7 | df = pd.read_csv(file_path) 8 | return df 9 | 10 | def process_data(df): 11 | print("Processing data...") 12 | df['DiscountedAmount'] = df['OrderAmount'] * 0.9 # Apply a 10% discount 13 | df['OrderYear'] = pd.to_datetime(df['OrderDate']).dt.year # Extract the order year 14 | return df 15 | 16 | def main(): 17 | # Start tracing memory allocations 18 | tracemalloc.start() 19 | 20 | # Load data 21 | df = load_data('order_data.csv') 22 | 23 | # Take a snapshot 24 | snapshot1 = tracemalloc.take_snapshot() 25 | 26 | # Process data 27 | df = process_data(df) 28 | 29 | # Take another snapshot 30 | snapshot2 = tracemalloc.take_snapshot() 31 | 32 | # Compare snapshots 33 | top_stats = snapshot2.compare_to(snapshot1, 'lineno') 34 | 35 | print("[ Top memory-consuming lines ]") 36 | for stat in top_stats[:10]: 37 | print(stat) 38 | 39 | # Current and peak memory usage 40 | current, peak = tracemalloc.get_traced_memory() 41 | print(f"Current memory usage: {current / 1024 / 1024:.1f} MB") 42 | print(f"Peak usage: {peak / 1024 / 1024:.1f} MB") 43 | 44 | tracemalloc.stop() 45 | 46 | if __name__ == "__main__": 47 | main() 48 | -------------------------------------------------------------------------------- /intro-to-docker/app.py: -------------------------------------------------------------------------------- 1 | # app.py 2 | from fastapi import FastAPI 3 | from pydantic import BaseModel 4 | from typing import List 5 | import os 6 | 7 | app = FastAPI(title="Todo API") 8 | todos = [] 9 | next_id = 1 10 | 11 | class TodoCreate(BaseModel): 12 | title: str 13 | completed: bool = False 14 | 15 | class Todo(BaseModel): 16 | id: int 17 | title: str 18 | completed: bool 19 | 20 | @app.get("/") 21 | def health_check(): 22 | return { 23 | "status": "healthy", 24 | "environment": os.getenv("ENVIRONMENT", "development"), 25 | "python_version": os.getenv("PYTHON_VERSION", "unknown") 26 | } 27 | 28 | @app.get("/todos", response_model=List[Todo]) 29 | def list_todos(): 30 | return todos 31 | 32 | @app.post("/todos", response_model=Todo) 33 | def create_todo(todo_data: TodoCreate): 34 | global next_id 35 | new_todo = Todo( 36 | id=next_id, 37 | title=todo_data.title, 38 | completed=todo_data.completed 39 | ) 40 | todos.append(new_todo) 41 | next_id += 1 42 | return new_todo 43 | 44 | @app.delete("/todos/{todo_id}") 45 | def delete_todo(todo_id: int): 46 | global todos 47 | todos = [t for t in todos if t.id != todo_id] 48 | return {"message": "Todo deleted"} 49 | 50 | if __name__ == "__main__": 51 | import uvicorn 52 | uvicorn.run(app, host="0.0.0.0", port=8000) 53 | -------------------------------------------------------------------------------- /python-for-beginners/day-4/day4_examples.py: -------------------------------------------------------------------------------- 1 | data = [ 2 | {"name": "Alice", "city": "London"}, 3 | {"name": "Bob", "city": "Paris"}, 4 | {"name": "Eve", "city": "London"}, 5 | {"name": "John", "city": "New York"}, 6 | {"name": "Dana", "city": "Paris"}, 7 | ] 8 | 9 | city_counts = {} 10 | 11 | for person in data: 12 | city = person["city"] 13 | if city not in city_counts: 14 | city_counts[city] = 1 15 | else: 16 | city_counts[city] += 1 17 | 18 | print(city_counts) 19 | 20 | salaries = [ 21 | {"role": "Engineer", "salary": 75000}, 22 | {"role": "Analyst", "salary": 62000}, 23 | {"role": "Engineer", "salary": 80000}, 24 | {"role": "Manager", "salary": 95000}, 25 | {"role": "Analyst", "salary": 64000}, 26 | ] 27 | 28 | totals = {} 29 | counts = {} 30 | 31 | for person in salaries: 32 | role = person["role"] 33 | salary = person["salary"] 34 | 35 | totals[role] = totals.get(role, 0) + salary 36 | counts[role] = counts.get(role, 0) + 1 37 | 38 | averages = {role: totals[role] / counts[role] for role in totals} 39 | 40 | print(averages) 41 | 42 | ages = [29, 34, 29, 41, 34, 29] 43 | 44 | freq = {} 45 | 46 | for age in ages: 47 | freq[age] = freq.get(age, 0) + 1 48 | 49 | most_common = max(freq.items(), key=lambda x: x[1]) 50 | 51 | print(f"Most common age: {most_common[0]} (appears {most_common[1]} times)") 52 | -------------------------------------------------------------------------------- /pathlib-tutorial/main.py: -------------------------------------------------------------------------------- 1 | from pathlib import Path 2 | 3 | # import Path from pathlib 4 | from pathlib import Path 5 | 6 | # create a base path 7 | # replace user w/ a valid user :) 8 | base_path = Path("/home/user/Documents") 9 | 10 | # create new paths from the base path 11 | subdirectory_path = base_path / "projects" / "project1" 12 | file_path = subdirectory_path / "report.txt" 13 | 14 | # Print out the paths 15 | print("Base path:", base_path) 16 | print("Subdirectory path:", subdirectory_path) 17 | print("File path:", file_path) 18 | 19 | 20 | path = Path("/home/user/Downloads") 21 | # check if a path exists 22 | print(path.exists()) 23 | 24 | # check if a path is a file or a directory 25 | print(path.is_file()) 26 | print(path.is_dir()) 27 | 28 | # iterating over directory contents 29 | for item in path.iterdir(): 30 | print(item) 31 | 32 | # rename files 33 | path = Path('old_path') 34 | path.rename('new_path') 35 | 36 | # delete files 37 | path.unlink() 38 | 39 | # delete empty directories 40 | path.rmdir() 41 | 42 | # resolve paths 43 | relative_path = Path('new_project/README.md') 44 | absolute_path = relative_path.resolve() 45 | print(absolute_path) 46 | 47 | path = Path('/home/user/projectA') 48 | 49 | # simple globbing 50 | text_files = list(path.glob('*.txt')) 51 | print(text_files) 52 | 53 | # recursive globbing 54 | text_files = list(path.rglob('*.txt')) 55 | print(text_files) 56 | 57 | -------------------------------------------------------------------------------- /bytes2str/main.py: -------------------------------------------------------------------------------- 1 | # Sample byte object 2 | byte_data = b'Hello, World!' 3 | 4 | # Converting bytes to string using UTF-8 encoding 5 | string_data = byte_data.decode('utf-8') 6 | 7 | print(string_data) 8 | 9 | # Sample byte object in UTF-16 encoding 10 | byte_data_utf16 = b'\xff\xfeH\x00e\x00l\x00l\x00o\x00,\x00 \x00W\x00o\x00r\x00l\x00d\x00!\x00' 11 | 12 | # Converting bytes to string using UTF-16 encoding 13 | string_data_utf16 = byte_data_utf16.decode('utf-16') 14 | 15 | print(string_data_utf16) 16 | 17 | # using chardet to detect encoding 18 | import chardet 19 | 20 | # Sample byte object with unknown encoding 21 | byte_data_unknown = b'\xe4\xbd\xa0\xe5\xa5\xbd' 22 | 23 | # Detecting the encoding 24 | detected_encoding = chardet.detect(byte_data_unknown) 25 | encoding = detected_encoding['encoding'] 26 | print(encoding) 27 | 28 | # Converting bytes to string using detected encoding 29 | string_data_unknown = byte_data_unknown.decode(encoding) 30 | 31 | print(string_data_unknown) 32 | 33 | # Sample byte object with invalid sequence for UTF-8 34 | byte_data_invalid = b'Hello, World!\xff' 35 | 36 | # Converting bytes to string while ignoring errors 37 | string_data = byte_data_invalid.decode('utf-8', errors='ignore') 38 | 39 | print(string_data) 40 | 41 | # Converting bytes to string while replacing errors with a placeholder 42 | string_data_replace = byte_data_invalid.decode('utf-8', errors='replace') 43 | 44 | print(string_data_replace) 45 | 46 | -------------------------------------------------------------------------------- /python-for-beginners/day-7/day7_examples.py: -------------------------------------------------------------------------------- 1 | def load_csv(filename): 2 | with open(filename) as f: 3 | lines = [line.strip() for line in f if line.strip()] 4 | header = lines[0].split(",") 5 | rows = [line.split(",") for line in lines[1:]] 6 | return header, rows 7 | 8 | def detect_type(value): 9 | try: 10 | float(value) 11 | return "numeric" 12 | except: 13 | return "text" 14 | 15 | def profile_columns(header, rows): 16 | summary = {} 17 | for i, col in enumerate(header): 18 | values = [row[i].strip() for row in rows if len(row) == len(header)] 19 | col_type = detect_type(values[0]) 20 | unique = set(values) 21 | summary[col] = { 22 | "type": col_type, 23 | "unique_count": len(unique), 24 | "most_common": max(set(values), key=values.count) 25 | } 26 | if col_type == "numeric": 27 | nums = [float(v) for v in values if v.replace('.', '', 1).isdigit()] 28 | summary[col]["average"] = sum(nums) / len(nums) if nums else 0 29 | return summary 30 | 31 | def write_summary(summary, out_file): 32 | with open(out_file, "w") as f: 33 | for col, stats in summary.items(): 34 | f.write(f"Column: {col}\n") 35 | for k, v in stats.items(): 36 | f.write(f" {k}: {v}\n") 37 | f.write("\n") 38 | 39 | header, rows = load_csv("employees.csv") 40 | summary = profile_columns(header, rows) 41 | write_summary(summary, "profile_report.txt") 42 | 43 | -------------------------------------------------------------------------------- /regex/main.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | text = "Contact info: (123)-456-7890 and 987-654-3210." 4 | cleaned_text = re.sub(r'[()-]', '', text) 5 | print(cleaned_text) 6 | 7 | # Output: Contact info: 1234567890 or 9876543210 8 | 9 | text = "Please reach out to us at support@example.org or help@example.org." 10 | emails = re.findall(r'\b[\w.-]+?@\w+?\.\w+?\b', text) 11 | print(emails) 12 | 13 | # Output: ['support@example.com', 'sales@example.org'] 14 | 15 | text = "Using regular expressions." 16 | cleaned_text = re.sub(r'\s+', ' ', text) 17 | print(cleaned_text) 18 | 19 | # Output: Using regular expressions. 20 | 21 | email = "test@example.com" 22 | if re.match(r'^\b[\w.-]+?@\w+?\.\w+?\b$', email): 23 | print("Valid email") 24 | else: 25 | print("Invalid email") 26 | 27 | # Output: Valid email 28 | 29 | text = "This is sentence one. And this is sentence two! Is this sentence three?" 30 | sentences = re.split(r'[.!?]', text) 31 | print(sentences) 32 | 33 | # Output: ['This is sentence one', ' And this is sentence two', ' Is this sentence three', ''] 34 | 35 | # Using regex w/ pandas dataframes 36 | import pandas as pd 37 | 38 | data = { 39 | 'names': ['Alice123', 'Bob!@#', 'Charlie$$$'], 40 | 'emails': ['alice@example.com', 'bob_at_example.com', 'charlie@example.com'] 41 | } 42 | df = pd.DataFrame(data) 43 | 44 | # Remove non-alphabetic characters from names 45 | df['names'] = df['names'].str.replace(r'[^a-zA-Z]', '', regex=True) 46 | 47 | # Validate email addresses 48 | df['valid_email'] = df['emails'].apply(lambda x: bool(re.match(r'^\b[\w.-]+?@\w+?\.\w+?\b$', x))) 49 | 50 | print(df) 51 | -------------------------------------------------------------------------------- /enums/task.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class TaskState(Enum): 4 | TODO = 0 5 | IN_PROGRESS = 1 6 | DONE = 2 7 | ABANDONED = -1 8 | 9 | 10 | class Task: 11 | def __init__(self, name, state): 12 | self.name = name 13 | self.state = state 14 | 15 | def update_state(self, new_state): 16 | # Define valid state transitions based on the current state 17 | valid_transitions = { 18 | TaskState.TODO: [TaskState.IN_PROGRESS, TaskState.ABANDONED], 19 | TaskState.IN_PROGRESS: [TaskState.DONE, TaskState.ABANDONED], 20 | TaskState.DONE: [], 21 | TaskState.ABANDONED: [] 22 | } 23 | 24 | # Check if the new state is a valid transition from the current state 25 | if new_state in valid_transitions[self.state]: 26 | self.state = new_state 27 | else: 28 | raise ValueError(f"Invalid state transition from {self.state.name} to {new_state.name}") 29 | 30 | 31 | # Create a new task with the initial state "To Do" 32 | task = Task("Write Report", TaskState.TODO) 33 | 34 | # Print the task details 35 | print(f"Task Name: {task.name}") 36 | print(f"Current State: {task.state.name}") 37 | 38 | # Update the task state to "In Progress" 39 | task.update_state(TaskState.IN_PROGRESS) 40 | print(f"Updated State: {task.state.name}") 41 | 42 | # Attempt to update the task state to an invalid state 43 | # task.update_state(TaskState.TODO) # uncomment to see if exception handling works! 44 | 45 | 46 | # Update the task state to "Completed" 47 | task.update_state(TaskState.DONE) 48 | print(f"Updated State: {task.state.name}") 49 | -------------------------------------------------------------------------------- /pytest/tests/test_todo_manager.py: -------------------------------------------------------------------------------- 1 | # tests/test_todo_manager.py 2 | import pytest 3 | from todo.todo_manager import ToDoManager 4 | 5 | @pytest.fixture 6 | def todo_manager(): 7 | return ToDoManager() 8 | 9 | def test_add_task(todo_manager): 10 | todo_manager.add_task("Buy groceries") 11 | assert todo_manager.tasks == [{"task": "Buy groceries", "completed": False}] 12 | 13 | def test_add_empty_task(todo_manager): 14 | with pytest.raises(ValueError, match="Task cannot be empty."): 15 | todo_manager.add_task("") 16 | 17 | def test_remove_task(todo_manager): 18 | todo_manager.add_task("Buy groceries") 19 | todo_manager.remove_task("Buy groceries") 20 | assert todo_manager.tasks == [] 21 | 22 | def test_remove_nonexistent_task(todo_manager): 23 | with pytest.raises(ValueError, match="Task not found."): 24 | todo_manager.remove_task("Do laundry") 25 | 26 | def test_mark_completed(todo_manager): 27 | todo_manager.add_task("Go for a walk") 28 | todo_manager.mark_completed("Go for a walk") 29 | assert todo_manager.tasks == [{"task": "Go for a walk", "completed": True}] 30 | 31 | def test_get_tasks(todo_manager): 32 | todo_manager.add_task("Task 1") 33 | todo_manager.add_task("Task 2") 34 | todo_manager.mark_completed("Task 1") 35 | 36 | all_tasks = todo_manager.get_tasks() 37 | completed_tasks = todo_manager.get_tasks(completed=True) 38 | pending_tasks = todo_manager.get_tasks(completed=False) 39 | 40 | assert len(all_tasks) == 2 41 | assert completed_tasks == [{"task": "Task 1", "completed": True}] 42 | assert pending_tasks == [{"task": "Task 2", "completed": False}] 43 | -------------------------------------------------------------------------------- /better-python/main.py: -------------------------------------------------------------------------------- 1 | # Prefer list comprehensions over loops 2 | data = [{'name': 'Alice', 'age': 25, 'score': 90}, 3 | {'name': 'Bob', 'age': 30, 'score': 85}, 4 | {'name': 'Charlie', 'age': 22, 'score': 95}] 5 | 6 | # Using a loop 7 | result = [] 8 | for row in data: 9 | if row['score'] > 85: 10 | result.append(row['name']) 11 | 12 | print(result) 13 | 14 | # Using a list comprehension 15 | result = [row['name'] for row in data if row['score'] > 85] 16 | 17 | # Generator function to read and process a CSV file 18 | import csv 19 | from typing import Generator, Dict 20 | 21 | def read_large_csv_with_generator(file_path: str) -> Generator[Dict[str, str], None, None]: 22 | with open(file_path, 'r') as file: 23 | reader = csv.DictReader(file) 24 | for row in reader: 25 | yield row 26 | 27 | # Path to a sample CSV file 28 | file_path = 'large_data.csv' 29 | 30 | for row in read_large_csv_with_generator(file_path): 31 | print(row) 32 | 33 | # Caching 34 | from functools import cache 35 | from typing import Tuple 36 | import numpy as np 37 | 38 | @cache 39 | def euclidean_distance(pt1: Tuple[float, float], pt2: Tuple[float, float]) -> float: 40 | return np.sqrt((pt1[0] - pt2[0]) ** 2 + (pt1[1] - pt2[1]) ** 2) 41 | 42 | def assign_clusters(data: np.ndarray, centroids: np.ndarray) -> np.ndarray: 43 | clusters = np.zeros(data.shape[0]) 44 | for i, point in enumerate(data): 45 | distances = [euclidean_distance(tuple(point), tuple(centroid)) for centroid in centroids] 46 | clusters[i] = np.argmin(distances) 47 | return clusters 48 | 49 | # Context managers 50 | import sqlite3 51 | 52 | def query_db(db_path): 53 | with sqlite3.connect(db_path) as conn: 54 | cursor = conn.cursor() 55 | cursor.execute(query) 56 | for row in cursor.fetchall(): 57 | yield row 58 | -------------------------------------------------------------------------------- /sql-tips/main.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from faker import Faker 3 | import random 4 | 5 | # Initialize Faker 6 | fake = Faker() 7 | 8 | # Connect to SQLite database (or create it) 9 | conn = sqlite3.connect('employees.db') 10 | cursor = conn.cursor() 11 | 12 | # Create employees table 13 | cursor.execute(''' 14 | CREATE TABLE IF NOT EXISTS employees ( 15 | employee_id INTEGER PRIMARY KEY, 16 | first_name TEXT NOT NULL, 17 | last_name TEXT NOT NULL, 18 | email TEXT NOT NULL UNIQUE, 19 | phone_number TEXT, 20 | hire_date TEXT, 21 | job_id TEXT, 22 | salary REAL, 23 | department TEXT 24 | ) 25 | ''') 26 | 27 | # Function to generate fake employee data 28 | def generate_employee_data(num_records): 29 | employees = [] 30 | for _ in range(num_records): 31 | first_name = fake.first_name() 32 | last_name = fake.last_name() 33 | email = fake.email() 34 | phone_number = fake.phone_number() 35 | hire_date = fake.date_between(start_date='-10y', end_date='today').isoformat() 36 | job_id = random.choice(['IT_PROG', 'HR_REP', 'FIN_ANALYST', 'SALES_REP']) 37 | salary = round(random.uniform(30000, 120000), 2) 38 | department = random.choice(['IT', 'HR', 'Finance', 'Sales']) 39 | 40 | employees.append((first_name, last_name, email, phone_number, hire_date, job_id, salary, department)) 41 | return employees 42 | 43 | # Insert fake data into employees table 44 | num_records = 1000 45 | employee_data = generate_employee_data(num_records) 46 | cursor.executemany(''' 47 | INSERT INTO employees (first_name, last_name, email, phone_number, hire_date, job_id, salary, department) 48 | VALUES (?, ?, ?, ?, ?, ?, ?, ?) 49 | ''', employee_data) 50 | 51 | # Commit changes and close connection 52 | conn.commit() 53 | conn.close() 54 | 55 | print(f"Inserted {num_records} records into the employees table.") 56 | -------------------------------------------------------------------------------- /optimize-python-code/examples.py: -------------------------------------------------------------------------------- 1 | # before 2 | import time 3 | 4 | def square_numbers_loop(numbers): 5 | result = [] 6 | for num in numbers: 7 | result.append(num ** 2) 8 | return result 9 | 10 | # Let's test this with 100,000 numbers to see the performance 11 | test_numbers = list(range(1000000)) 12 | 13 | start_time = time.time() 14 | squared_loop = square_numbers_loop(test_numbers) 15 | loop_time = time.time() - start_time 16 | print(f"Loop time: {loop_time:.4f} seconds") 17 | 18 | # after 19 | def square_numbers_comprehension(numbers): 20 | return [num ** 2 for num in numbers] # Create the entire list in one line 21 | 22 | start_time = time.time() 23 | squared_comprehension = square_numbers_comprehension(test_numbers) 24 | comprehension_time = time.time() - start_time 25 | print(f"Comprehension time: {comprehension_time:.4f} seconds") 26 | print(f"Improvement: {loop_time / comprehension_time:.2f}x faster") 27 | 28 | # before 29 | def find_common_elements_list(list1, list2): 30 | common = [] 31 | for item in list1: # Go through each item in the first list 32 | if item in list2: # Check if it exists in the second list 33 | common.append(item) # If yes, add it to our common list 34 | return common 35 | 36 | # Test with reasonably large lists 37 | large_list1 = list(range(10000)) 38 | large_list2 = list(range(5000, 15000)) 39 | 40 | start_time = time.time() 41 | common_list = find_common_elements_list(large_list1, large_list2) 42 | list_time = time.time() - start_time 43 | print(f"List approach time: {list_time:.4f} seconds") 44 | 45 | # after 46 | def find_common_elements_set(list1, list2): 47 | set2 = set(list2) # Convert list to a set (one-time cost) 48 | return [item for item in list1 if item in set2] # Check membership in set 49 | 50 | start_time = time.time() 51 | common_set = find_common_elements_set(large_list1, large_list2) 52 | set_time = time.time() - start_time 53 | print(f"Set approach time: {set_time:.4f} seconds") 54 | print(f"Improvement: {list_time / set_time:.2f}x faster") 55 | -------------------------------------------------------------------------------- /misused-python-functions/main.py: -------------------------------------------------------------------------------- 1 | # instead of this 2 | def generate_leaderboard(players): 3 | leaderboard = [] 4 | for i, player in enumerate(players): 5 | # Manually adding 1 because leaderboards start at 1, not 0 6 | leaderboard.append(f"{i+1}. {player['name']}: {player['score']} pts") 7 | return leaderboard 8 | 9 | top_players = [ 10 | {'name': 'MasterBlaster', 'score': 157}, 11 | {'name': 'QuickShot', 'score': 145}, 12 | {'name': 'StealthNinja', 'score': 132} 13 | ] 14 | print('\n'.join(generate_leaderboard(top_players))) 15 | 16 | # use it like this 17 | def generate_leaderboard(players): 18 | # The start parameter makes our intention crystal clear 19 | leaderboard = [] 20 | for rank, player in enumerate(players, start=1): 21 | leaderboard.append(f"{rank}. {player['name']}: {player['score']} pts") 22 | return leaderboard 23 | 24 | # instead of this 25 | def organize_inventory(products): 26 | # First sort by quantity (least to most) 27 | by_quantity = sorted(products, key=lambda x: x['quantity']) 28 | 29 | # Then sort by category 30 | by_category = sorted(by_quantity, key=lambda x: x['category']) 31 | 32 | # Finally sort by priority 33 | final_sorted = sorted(by_category, key=lambda x: x['priority']) 34 | 35 | return final_sorted 36 | 37 | inventory = [ 38 | {'name': 'Laptop', 'category': 'Electronics', 'quantity': 5, 'priority': 1}, 39 | {'name': 'Headphones', 'category': 'Electronics', 'quantity': 8, 'priority': 2}, 40 | {'name': 'Notebook', 'category': 'Office', 'quantity': 15, 'priority': 3}, 41 | {'name': 'Pen', 'category': 'Office', 'quantity': 50, 'priority': 3} 42 | ] 43 | 44 | 45 | # use it like this 46 | def organize_inventory(products): 47 | return sorted(products, key=lambda x: ( 48 | x['priority'], # Sort by priority first 49 | x['category'], # Then by category 50 | x['quantity'] # Then by quantity 51 | )) 52 | 53 | 54 | # instead of this 55 | # use it like this 56 | 57 | # instead of this 58 | 59 | # use it like this 60 | # instead of this 61 | # use it like this 62 | # instead of this 63 | # use it like this 64 | # instead of this 65 | # use it like this 66 | -------------------------------------------------------------------------------- /makefiles/Makefile: -------------------------------------------------------------------------------- 1 | # Variables 2 | PYTHON := python3 3 | APP_NAME := myapp 4 | TEST_PATH := tests/ 5 | 6 | .PHONY: help 7 | help: ## Show this help message 8 | @echo "Available commands for $(APP_NAME):" 9 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[0m %s\n", $$1, $$2}' 10 | 11 | .DEFAULT_GOAL := help 12 | 13 | # Environment setup 14 | .PHONY: install 15 | install: ## Install all dependencies 16 | $(PYTHON) -m pip install --upgrade pip 17 | pip install -r requirements.txt 18 | pip install -r requirements-dev.txt 19 | 20 | .PHONY: dev-setup 21 | dev-setup: install ## Complete development setup 22 | pre-commit install 23 | @echo "✅ Development environment ready!" 24 | 25 | # Code quality 26 | .PHONY: format 27 | format: ## Format code 28 | black $(APP_NAME) $(TEST_PATH) 29 | isort $(APP_NAME) $(TEST_PATH) 30 | 31 | .PHONY: lint 32 | lint: ## Run linting 33 | flake8 $(APP_NAME) $(TEST_PATH) 34 | black --check $(APP_NAME) $(TEST_PATH) 35 | isort --check-only $(APP_NAME) $(TEST_PATH) 36 | 37 | # Testing 38 | .PHONY: test 39 | test: ## Run tests 40 | $(PYTHON) -m pytest $(TEST_PATH) -v 41 | 42 | .PHONY: test-watch 43 | test-watch: ## Run tests in watch mode 44 | $(PYTHON) -m pytest $(TEST_PATH) -v --looponfail 45 | 46 | .PHONY: coverage 47 | coverage: ## Run tests with coverage 48 | $(PYTHON) -m pytest $(TEST_PATH) --cov=$(APP_NAME) --cov-report=html --cov-report=term 49 | 50 | # Development 51 | .PHONY: serve 52 | serve: ## Start development server 53 | flask --app $(APP_NAME) run --debug --reload 54 | 55 | .PHONY: shell 56 | shell: ## Start application shell 57 | flask --app $(APP_NAME) shell 58 | 59 | # Database 60 | .PHONY: db-migrate 61 | db-migrate: ## Create database migration 62 | flask --app $(APP_NAME) db migrate 63 | 64 | .PHONY: db-upgrade 65 | db-upgrade: ## Apply database migrations 66 | flask --app $(APP_NAME) db upgrade 67 | 68 | # Cleanup 69 | .PHONY: clean 70 | clean: ## Clean up generated files 71 | find . -type f -name "*.pyc" -delete 72 | find . -type d -name "__pycache__" -delete 73 | rm -rf build/ dist/ *.egg-info/ htmlcov/ .coverage .pytest_cache/ 74 | 75 | # All-in-one commands 76 | .PHONY: check 77 | check: lint test ## Run all checks 78 | 79 | .PHONY: fresh-start 80 | fresh-start: clean install ## Clean everything and reinstall 81 | -------------------------------------------------------------------------------- /dict-to-json/main.py: -------------------------------------------------------------------------------- 1 | # Python dict to JSON 2 | import json 3 | 4 | books = [ 5 | { 6 | "title": "The Great Gatsby", 7 | "author": "F. Scott Fitzgerald", 8 | "publication_year": 1925, 9 | "genre": "Fiction" 10 | }, 11 | { 12 | "title": "To Kill a Mockingbird", 13 | "author": "Harper Lee", 14 | "publication_year": 1960, 15 | "genre": "Fiction" 16 | }, 17 | { 18 | "title": "1984", 19 | "author": "George Orwell", 20 | "publication_year": 1949, 21 | "genre": "Fiction" 22 | } 23 | ] 24 | 25 | # Convert dictionary to JSON string 26 | json_string = json.dumps(books, indent=4) 27 | print(json_string) 28 | 29 | 30 | # Nested dict to JSON 31 | books = [ 32 | { 33 | "title": "The Great Gatsby", 34 | "author": "F. Scott Fitzgerald", 35 | "publication_year": 1925, 36 | "genre": "Fiction", 37 | "reviews": [ 38 | {"user": "Alice", "rating": 4, "comment": "Captivating story"}, 39 | {"user": "Bob", "rating": 5, "comment": "Enjoyed it!"} 40 | ] 41 | }, 42 | { 43 | "title": "To Kill a Mockingbird", 44 | "author": "Harper Lee", 45 | "publication_year": 1960, 46 | "genre": "Fiction", 47 | "reviews": [ 48 | {"user": "Charlie", "rating": 5, "comment": "A great read!"}, 49 | {"user": "David", "rating": 4, "comment": "Engaging narrative"} 50 | ] 51 | }, 52 | { 53 | "title": "1984", 54 | "author": "George Orwell", 55 | "publication_year": 1949, 56 | "genre": "Fiction", 57 | "reviews": [ 58 | {"user": "Emma", "rating": 5, "comment": "Orwell pulls it off well!"}, 59 | {"user": "Frank", "rating": 4, "comment": "Dystopian masterpiece"} 60 | ] 61 | } 62 | ] 63 | 64 | # Convert dictionary to JSON string 65 | json_string = json.dumps(books, indent=4) 66 | print(json_string) 67 | 68 | 69 | # Sort keys 70 | person = { 71 | "name": "John Doe", 72 | "age": 30, 73 | "email": "john@example.com", 74 | "address": { 75 | "city": "New York", 76 | "zipcode": "10001", 77 | "street": "123 Main Street" 78 | } 79 | } 80 | 81 | # Convert dictionary to JSON string with sorted keys 82 | json_string = json.dumps(person, sort_keys=True, indent=4) 83 | print(json_string) 84 | -------------------------------------------------------------------------------- /config-management-basics/parsing-ini-files/parsing_ini_files.py: -------------------------------------------------------------------------------- 1 | 2 | import configparser 3 | from pathlib import Path 4 | 5 | 6 | 7 | config = configparser.ConfigParser() 8 | config.read('/content/app.ini') 9 | 10 | # Access values from sections 11 | db_host = config['database']['host'] 12 | db_port = config['database']['port'] 13 | 14 | print(f"Database: {db_host}:{db_port}") 15 | print(f"Sections: {config.sections()}") 16 | 17 | 18 | config = configparser.ConfigParser() 19 | config.read('app.ini') 20 | 21 | # Automatic type conversion 22 | db_port = config.getint('database', 'port') 23 | ssl_enabled = config.getboolean('database', 'ssl_enabled') 24 | 25 | # With fallback defaults 26 | max_retries = config.getint('database', 'max_retries', fallback=3) 27 | timeout = config.getfloat('database', 'timeout', fallback=30.0) 28 | 29 | print(f"Port: {db_port}, SSL: {ssl_enabled}") 30 | 31 | class ConfigManager: 32 | def __init__(self, config_file='app.ini'): 33 | self.config = configparser.ConfigParser() 34 | 35 | if not Path(config_file).exists(): 36 | raise FileNotFoundError(f"Config file not found: {config_file}") 37 | 38 | self.config.read(config_file) 39 | 40 | def get_database_config(self): 41 | db = self.config['database'] 42 | return { 43 | 'host': db.get('host'), 44 | 'port': db.getint('port'), 45 | 'username': db.get('username'), 46 | 'password': db.get('password'), 47 | 'pool_size': db.getint('pool_size', fallback=5) 48 | } 49 | 50 | config = ConfigManager('app.ini') 51 | db_config = config.get_database_config() 52 | print(db_config) 53 | 54 | 55 | config = configparser.ConfigParser() 56 | config.read('app.ini') 57 | 58 | # Get all options in a section as a dictionary 59 | db_settings = dict(config['database']) 60 | server_settings = dict(config['server']) 61 | 62 | # Check if a section exists 63 | if config.has_section('cache'): 64 | cache_enabled = config.getboolean('cache', 'enabled') 65 | else: 66 | cache_enabled = False 67 | 68 | print(f"Database settings: {db_settings}") 69 | print(f"Caching enabled: {cache_enabled}") 70 | 71 | 72 | config = configparser.ConfigParser() 73 | 74 | # Add sections and values 75 | config['database'] = { 76 | 'host': 'localhost', 77 | 'port': '5432', 78 | 'username': 'myapp' 79 | } 80 | 81 | config['server'] = { 82 | 'host': '0.0.0.0', 83 | 'port': '8000', 84 | 'debug': 'false' 85 | } 86 | 87 | # Write to file 88 | with open('generated.ini', 'w') as configfile: 89 | config.write(configfile) 90 | 91 | print("Configuration file created!") 92 | -------------------------------------------------------------------------------- /functools-n-itertools/examples.py: -------------------------------------------------------------------------------- 1 | # ex 1 2 | from functools import lru_cache 3 | 4 | @lru_cache(maxsize=128) 5 | def fetch_user_data(user_id): 6 | # Expensive database call 7 | return database.get_user(user_id) 8 | 9 | # First call hits database, subsequent calls use cache 10 | user = fetch_user_data(123) # Database call 11 | user = fetch_user_data(123) # Returns cached result 12 | 13 | # ex 2 14 | from itertools import chain 15 | 16 | # Process multiple log files as one stream 17 | error_logs = ['app.log', 'db.log', 'api.log'] 18 | all_lines = chain.from_iterable(open(f) for f in error_logs) 19 | 20 | error_count = sum(1 for line in all_lines if 'ERROR' in line) 21 | 22 | # ex 3 23 | from functools import partial 24 | import logging 25 | 26 | def log_event(level, component, message): 27 | logging.log(level, f"[{component}] {message}") 28 | 29 | # Create specialized loggers 30 | auth_error = partial(log_event, logging.ERROR, 'AUTH') 31 | db_info = partial(log_event, logging.INFO, 'DATABASE') 32 | 33 | # Clean usage 34 | auth_error("Login failed for user") 35 | db_info("Connection established") 36 | 37 | # ex 4 38 | from itertools import combinations 39 | 40 | features = ['cache', 'compression', 'cdn'] 41 | 42 | # Test all pairs of features 43 | for combo in combinations(features, 2): 44 | performance = test_feature_combo(combo) 45 | print(f"{combo}: {performance}ms") 46 | 47 | # ex 5 48 | from functools import singledispatch 49 | from datetime import datetime 50 | 51 | @singledispatch 52 | def format_data(value): 53 | return str(value) # Default 54 | 55 | @format_data.register(datetime) 56 | def _(value): 57 | return value.strftime("%Y-%m-%d") 58 | 59 | @format_data.register(list) 60 | def _(value): 61 | return ", ".join(str(item) for item in value) 62 | 63 | # Automatically picks the right formatter 64 | print(format_data(datetime.now())) # "2025-06-27" 65 | print(format_data([1, 2, 3])) # "1, 2, 3" 66 | 67 | # ex 6 68 | from itertools import groupby 69 | 70 | transactions = [ 71 | {'type': 'credit', 'amount': 100}, 72 | {'type': 'credit', 'amount': 50}, 73 | {'type': 'debit', 'amount': 75}, 74 | {'type': 'debit', 'amount': 25} 75 | ] 76 | 77 | # Group by transaction type 78 | for trans_type, group in groupby(transactions, key=lambda x: x['type']): 79 | total = sum(item['amount'] for item in group) 80 | print(f"{trans_type}: ${total}") 81 | 82 | from functools import reduce 83 | 84 | # Calculate compound interest 85 | monthly_rates = [1.01, 1.02, 0.99, 1.015] # Monthly growth rates 86 | 87 | final_amount = reduce(lambda total, rate: total * rate, monthly_rates, 1000) 88 | print(f"Final amount: ${final_amount:.2f}") 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /custom-exceptions/main.py: -------------------------------------------------------------------------------- 1 | class OutOfStockError(Exception): 2 | """Exception raised when the product is out of stock""" 3 | 4 | def __init__(self, product_name): 5 | self.product_name = product_name 6 | super().__init__(f"'{self.product_name}' is out of stock") 7 | 8 | 9 | class InvalidProductIDError(Exception): 10 | """Exception raised for invalid product IDs""" 11 | 12 | def __init__(self, product_id): 13 | self.product_id = product_id 14 | super().__init__(f"Product ID '{self.product_id}' is not valid") 15 | 16 | 17 | class PurchaseLimitExceededError(Exception): 18 | """Exception raised when purchase limit is exceeded""" 19 | 20 | def __init__(self, product_name, limit): 21 | self.product_name = product_name 22 | self.limit = limit 23 | super().__init__( 24 | f"Cannot purchase more than {self.limit} units of '{self.product_name}'" 25 | ) 26 | 27 | class Inventory: 28 | def __init__(self): 29 | self.products = { 30 | 'P001': {'name': 'Laptop', 'stock': 5, 'max_purchase_limit': 2}, 31 | 'P002': {'name': 'Smartphone', 'stock': 0, 'max_purchase_limit': 5}, 32 | } 33 | 34 | 35 | def purchase(self, product_id, quantity): 36 | if product_id not in self.products: 37 | raise InvalidProductIDError(product_id) 38 | 39 | product = self.products[product_id] 40 | 41 | if product['stock'] == 0: 42 | raise OutOfStockError(product['name']) 43 | 44 | # Check if the quantity exceeds the max purchase limit 45 | if quantity > product['max_purchase_limit']: 46 | raise PurchaseLimitExceededError(product['name'], product['max_purchase_limit']) 47 | 48 | # Process purchase 49 | if quantity <= product['stock']: 50 | product['stock'] -= quantity 51 | print(f"Successfully purchased {quantity} unit(s) of {product['name']}.") 52 | else: 53 | raise OutOfStockError(product['name']) 54 | 55 | # Testing the system 56 | inventory = Inventory() 57 | 58 | try: 59 | inventory.purchase('P001', 1) # Successful purchase 60 | inventory.purchase('P002', 1) # OutOfStockError 61 | except OutOfStockError as e: 62 | print(f"Error: {e}") 63 | except InvalidProductIDError as e: 64 | print(f"Error: {e}") 65 | except PurchaseLimitExceededError as e: 66 | print(f"Error: {e}") 67 | 68 | try: 69 | inventory.purchase('P001', 3) # PurchaseLimitExceededError 70 | except OutOfStockError as e: 71 | print(f"Error: {e}") 72 | except InvalidProductIDError as e: 73 | print(f"Error: {e}") 74 | except PurchaseLimitExceededError as e: 75 | print(f"Error: {e}") 76 | 77 | -------------------------------------------------------------------------------- /config-management-basics/env-vars/examples.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | # Get an environment variable (raises KeyError if not found) 4 | api_key = os.environ['API_KEY'] 5 | 6 | # Safer approach: get with a default value 7 | database_host = os.environ.get('DATABASE_HOST', 'localhost') 8 | database_port = os.environ.get('DATABASE_PORT', '5432') 9 | 10 | print(f"Connecting to {database_host}:{database_port}") 11 | 12 | database_port = int(os.environ.get('DATABASE_PORT', '5432')) 13 | max_connections = int(os.environ.get('MAX_CONNECTIONS', '10')) 14 | 15 | total_capacity = database_port + max_connections # Now this works with integers 16 | 17 | debug_mode = os.environ.get('DEBUG', 'False').lower() in ('true', '1', 'yes') 18 | 19 | # Set an environment variable 20 | os.environ['APP_ENV'] = 'development' 21 | os.environ['MAX_CONNECTIONS'] = '100' 22 | 23 | # Verify it was set 24 | print(f"Environment: {os.environ['APP_ENV']}") 25 | 26 | # Delete an environment variable 27 | if 'TEMP_VAR' in os.environ: 28 | del os.environ['TEMP_VAR'] 29 | 30 | class AppConfig: 31 | """Application configuration loaded from environment variables""" 32 | 33 | def __init__(self): 34 | # Required settings (will fail fast if missing) 35 | self.api_key = self._get_required('API_KEY') 36 | self.database_url = self._get_required('DATABASE_URL') 37 | 38 | # Optional settings with defaults 39 | self.debug = self._get_bool('DEBUG', False) 40 | self.port = self._get_int('PORT', 8000) 41 | self.log_level = os.environ.get('LOG_LEVEL', 'INFO') 42 | self.max_workers = self._get_int('MAX_WORKERS', 4) 43 | 44 | def _get_required(self, key): 45 | """Get a required environment variable or raise an error""" 46 | value = os.environ.get(key) 47 | if value is None: 48 | raise ValueError(f"Required environment variable '{key}' is not set") 49 | return value 50 | 51 | def _get_bool(self, key, default): 52 | """Convert environment variable to boolean""" 53 | value = os.environ.get(key) 54 | if value is None: 55 | return default 56 | return value.lower() in ('true', '1', 'yes', 'on') 57 | 58 | def _get_int(self, key, default): 59 | """Convert environment variable to integer""" 60 | value = os.environ.get(key) 61 | if value is None: 62 | return default 63 | try: 64 | return int(value) 65 | except ValueError: 66 | raise ValueError(f"Environment variable '{key}' must be an integer, got '{value}'") 67 | 68 | def __repr__(self): 69 | """Safe string representation (masks sensitive data)""" 70 | return (f"AppConfig(debug={self.debug}, port={self.port}, " 71 | f"log_level={self.log_level}, api_key={'*' * 8})") 72 | 73 | config = AppConfig() 74 | print(config) 75 | print(f"Running on port {config.port}") 76 | -------------------------------------------------------------------------------- /write-efficient-data-classes/examples.py: -------------------------------------------------------------------------------- 1 | from dataclasses import dataclass 2 | 3 | @dataclass(frozen=True) 4 | class CacheKey: 5 | user_id: int 6 | resource_type: str 7 | timestamp: int 8 | 9 | cache = {} 10 | key = CacheKey(user_id=42, resource_type="profile", timestamp=1698345600) 11 | cache[key] = {"data": "expensive_computation_result"} 12 | 13 | from dataclasses import dataclass 14 | 15 | @dataclass(slots=True) 16 | class Measurement: 17 | sensor_id: int 18 | temperature: float 19 | humidity: float 20 | timestamp: int 21 | 22 | from dataclasses import dataclass, field 23 | from datetime import datetime 24 | 25 | @dataclass 26 | class User: 27 | user_id: int 28 | email: str 29 | last_login: datetime = field(compare=False) 30 | login_count: int = field(compare=False, default=0) 31 | 32 | user1 = User(1, "alice@example.com", datetime.now(), 5) 33 | user2 = User(1, "alice@example.com", datetime.now(), 10) 34 | print(user1 == user2) # True 35 | 36 | from dataclasses import dataclass, field 37 | 38 | @dataclass 39 | class ShoppingCart: 40 | user_id: int 41 | items: list[str] = field(default_factory=list) 42 | metadata: dict = field(default_factory=dict) 43 | 44 | cart1 = ShoppingCart(user_id=1) 45 | cart2 = ShoppingCart(user_id=2) 46 | cart1.items.append("laptop") 47 | print(cart2.items) # [] - separate list 48 | 49 | from dataclasses import dataclass, field 50 | 51 | @dataclass 52 | class Rectangle: 53 | width: float 54 | height: float 55 | area: float = field(init=False) 56 | 57 | def __post_init__(self): 58 | self.area = self.width * self.height 59 | if self.width <= 0 or self.height <= 0: 60 | raise ValueError("Dimensions must be positive") 61 | 62 | rect = Rectangle(5.0, 3.0) 63 | print(rect.area) # 15.0 64 | 65 | from dataclasses import dataclass 66 | 67 | @dataclass(order=True) 68 | class Task: 69 | priority: int 70 | name: str 71 | 72 | tasks = [ 73 | Task(priority=3, name="Low priority task"), 74 | Task(priority=1, name="Critical bug fix"), 75 | Task(priority=2, name="Feature request") 76 | ] 77 | 78 | sorted_tasks = sorted(tasks) 79 | for task in sorted_tasks: 80 | print(f"{task.priority}: {task.name}") 81 | # Output: 1: Critical bug fix, 2: Feature request, 3: Low priority task 82 | 83 | from dataclasses import dataclass, field, InitVar 84 | 85 | @dataclass 86 | class DatabaseConnection: 87 | host: str 88 | port: int 89 | ssl: InitVar[bool] = True 90 | connection_string: str = field(init=False) 91 | 92 | def __post_init__(self, ssl: bool): 93 | protocol = "https" if ssl else "http" 94 | self.connection_string = f"{protocol}://{self.host}:{self.port}" 95 | 96 | conn = DatabaseConnection("localhost", 5432, ssl=True) 97 | print(conn.connection_string) # https://localhost:5432 98 | print(hasattr(conn, 'ssl')) # False 99 | 100 | 101 | -------------------------------------------------------------------------------- /custom_context_manager/main.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from typing import Optional 3 | 4 | # Writing a context manager class 5 | class ConnectionManager: 6 | def __init__(self, db_name: str): 7 | self.db_name = db_name 8 | self.conn: Optional[sqlite3.Connection] = None 9 | 10 | def __enter__(self): 11 | self.conn = sqlite3.connect(self.db_name) 12 | return self.conn 13 | 14 | def __exit__(self, exc_type, exc_value, traceback): 15 | if self.conn: 16 | self.conn.close() 17 | 18 | # Example usage 19 | db_name = "library.db" 20 | 21 | # Using ConnectionManager context manager directly 22 | with ConnectionManager(db_name) as conn: 23 | cursor = conn.cursor() 24 | 25 | # Create a books table if it doesn't exist 26 | cursor.execute(""" 27 | CREATE TABLE IF NOT EXISTS books ( 28 | id INTEGER PRIMARY KEY, 29 | title TEXT, 30 | author TEXT, 31 | publication_year INTEGER 32 | ) 33 | """) 34 | 35 | # Insert sample book records 36 | books_data = [ 37 | ("The Great Gatsby", "F. Scott Fitzgerald", 1925), 38 | ("To Kill a Mockingbird", "Harper Lee", 1960), 39 | ("1984", "George Orwell", 1949), 40 | ("Pride and Prejudice", "Jane Austen", 1813) 41 | ] 42 | cursor.executemany("INSERT INTO books (title, author, publication_year) VALUES (?, ?, ?)", books_data) 43 | conn.commit() 44 | 45 | # Retrieve and print all book records 46 | cursor.execute("SELECT * FROM books") 47 | records = cursor.fetchall() 48 | print("Library Catalog:") 49 | for record in records: 50 | book_id, title, author, publication_year = record 51 | print(f"Book ID: {book_id}, Title: {title}, Author: {author}, Year: {publication_year}") 52 | cursor.close() 53 | 54 | # Writing a generator function with the `@contextmanager` decorator 55 | from contextlib import contextmanager 56 | 57 | @contextmanager 58 | def database_connection(db_name: str): 59 | conn = sqlite3.connect(db_name) 60 | try: 61 | yield conn # Provide the connection to the 'with' block 62 | finally: 63 | conn.close() # Close the connection upon exiting the 'with' block 64 | 65 | # Example usage 66 | db_name = "library.db" 67 | 68 | # Using database_connection context manager directly 69 | with database_connection(db_name) as conn: 70 | cursor = conn.cursor() 71 | 72 | # Insert a set of book records 73 | more_books_data = [ 74 | ("The Catcher in the Rye", "J.D. Salinger", 1951), 75 | ("To the Lighthouse", "Virginia Woolf", 1927), 76 | ("Dune", "Frank Herbert", 1965), 77 | ("Slaughterhouse-Five", "Kurt Vonnegut", 1969) 78 | ] 79 | cursor.executemany("INSERT INTO books (title, author, publication_year) VALUES (?, ?, ?)", more_books_data) 80 | conn.commit() 81 | 82 | # Retrieve and print all book records 83 | cursor.execute("SELECT * FROM books") 84 | records = cursor.fetchall() 85 | print("Updated Library Catalog:") 86 | for record in records: 87 | book_id, title, author, publication_year = record 88 | print(f"Book ID: {book_id}, Title: {title}, Author: {author}, Year: {publication_year}") 89 | cursor.close() 90 | -------------------------------------------------------------------------------- /coding-interview-tips/tips.py: -------------------------------------------------------------------------------- 1 | # 1. Reverse an Array in Place 2 | nums = [90,23,19,45,33,54] 3 | nums.reverse() 4 | print(nums) 5 | 6 | # Output >> [54, 33, 45, 19, 23, 90] 7 | 8 | # 2. Sort Arrays and Customize Sorts 9 | nums = [23,67,12,78,94,113,47] 10 | nums.sort() 11 | print(nums) 12 | 13 | # Output >> [12, 23, 47, 67, 78, 94, 113] 14 | 15 | nums.sort(reverse=True) 16 | print(nums) 17 | 18 | # Output >> [113, 94, 78, 67, 47, 23, 12] 19 | 20 | nums.sort(key=lambda num:num%7) 21 | print(nums) 22 | 23 | # Output >> [113, 78, 23, 94, 67, 47, 12] 24 | 25 | rem_list = [num%7 for num in nums] 26 | print(rem_list) 27 | 28 | # Output >> [1, 1, 2, 3, 4, 5, 5] 29 | 30 | str_list = ["puppet","trumpet","carpet","reset"] 31 | str_list.sort(key=lambda x:x.count('p')) 32 | print(str_list) 33 | 34 | # Output >> ['reset', 'trumpet', 'carpet', 'puppet'] 35 | 36 | # 3. List and Dictionary Comprehensions 37 | nums = [15,12,90,27,10,34,26,77] 38 | div_by_3 = [num for num in nums if num%3==0] 39 | print(div_by_3) 40 | 41 | # Output >> [15, 12, 90, 27] 42 | 43 | squares_dict = {i:i**2 for i in range(1,11)} 44 | print(squares_dict) 45 | 46 | # Output >> 47 | # {1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81, 10: 100} 48 | 49 | strings = ["hello","coding","blue","work"] 50 | str_len = {string:len(string) for string in strings} 51 | print(str_len) 52 | 53 | # Output >> {'hello': 5, 'coding': 6, 'blue': 4, 'work': 4} 54 | 55 | # 4. Unpacking Iterables 56 | list1 = [i*2 for i in range(4)] 57 | num1, num2, num3, num4 = list1 58 | num1, *num2 = list1 59 | num1, *num2, num3 = list1 60 | 61 | fruits = ["apples","grapes","berries","oranges","melons"] 62 | 63 | print('--'.join(fruits)) 64 | # Output >> 'apples--grapes--berries--oranges--melons' 65 | 66 | print(''.join(fruits)) 67 | # Output >> 'applesgrapesberriesorangesmelons' 68 | 69 | print(' '.join(fruits)) 70 | # Output >> 'apples grapes berries oranges melons' 71 | 72 | # 6. Loop Using enumerate() 73 | fruits = ["apples","grapes","berries","oranges","melons"] 74 | 75 | for idx,fruit in enumerate(fruits): 76 | print(f"At index {idx}: {fruit}") 77 | 78 | 79 | # Output >> 80 | # At index 0: apples 81 | # At index 1: grapes 82 | # At index 2: berries 83 | # At index 3: oranges 84 | # At index 4: melons 85 | 86 | for idx,fruit in enumerate(fruits,1): 87 | print(f"At index {idx}: {fruit}") 88 | 89 | # Output >> 90 | # At index 1: apples 91 | # At index 2: grapes 92 | # At index 3: berries 93 | # At index 4: oranges 94 | # At index 5: melons 95 | 96 | idx_dict = {idx:fruit for idx,fruit in enumerate(fruits)} 97 | print(idx_dict) 98 | 99 | # Output >> {0: 'apples', 1: 'grapes', 2: 'berries', 3: 'oranges', 4: 'melons'} 100 | 101 | # 7. Useful Math Functions to Know 102 | import math 103 | num1 = 2.47 104 | print(math.ceil(num1)) 105 | # Output >> 3 106 | 107 | num2 = 3.97 108 | print(math.floor(num2)) 109 | # Output >> 3 110 | 111 | sqrt_nums = [math.sqrt(num) for num in range(1,11)] 112 | print(sqrt_nums) 113 | 114 | # Output >> [1.0, 1.4142135623730951, 1.7320508075688772, 2.0, 2.23606797749979, 2.449489742783178, 2.6457513110645907, 2.8284271247461903, 3.0, 3.1622776601683795] 115 | 116 | sqrt_nums = [round(math.sqrt(num),2) for num in range(1,11)] 117 | print(sqrt_nums) 118 | 119 | # Output >> [1.0, 1.41, 1.73, 2.0, 2.24, 2.45, 2.65, 2.83, 3.0, 3.16] 120 | -------------------------------------------------------------------------------- /config-management-basics/parsing-toml-files/reading-toml.py: -------------------------------------------------------------------------------- 1 | import tomllib 2 | 3 | with open('config.toml', 'rb') as f: 4 | config = tomllib.load(f) 5 | 6 | # Access values 7 | app_title = config['title'] 8 | db_host = config['database']['host'] 9 | db_port = config['database']['port'] 10 | 11 | print(f"Application: {app_title}") 12 | print(f"Database: {db_host}:{db_port}") 13 | print(f"Config keys: {config.keys()}") 14 | 15 | with open('config.toml', 'rb') as f: 16 | config = tomllib.load(f) 17 | 18 | # Strings 19 | app_title = config['title'] 20 | 21 | # Integers 22 | db_port = config['database']['port'] 23 | cache_ttl = config['cache']['ttl'] 24 | 25 | # Booleans 26 | debug_mode = config['server']['debug'] 27 | cache_enabled = config['cache']['enabled'] 28 | 29 | # Arrays (become Python lists) 30 | databases = config['database']['databases'] 31 | allowed_hosts = config['server']['allowed_hosts'] 32 | 33 | print(f"Databases: {databases}") 34 | print(f"Type of databases: {type(databases)}") 35 | print(f"Debug mode: {debug_mode}, type: {type(debug_mode)}") 36 | 37 | from pathlib import Path 38 | 39 | class TOMLConfig: 40 | def __init__(self, config_file='config.toml'): 41 | self.config_file = Path(config_file) 42 | 43 | if not self.config_file.exists(): 44 | raise FileNotFoundError(f"Config file not found: {config_file}") 45 | 46 | with open(self.config_file, 'rb') as f: 47 | self.config = tomllib.load(f) 48 | 49 | def get(self, key, default=None): 50 | """Get a top-level configuration value""" 51 | return self.config.get(key, default) 52 | 53 | def get_section(self, section): 54 | """Get an entire configuration section""" 55 | if section not in self.config: 56 | raise ValueError(f"Section '{section}' not found") 57 | return self.config[section] 58 | 59 | config = TOMLConfig('config.toml') 60 | 61 | # Get top-level values 62 | app_title = config.get('title') 63 | version = config.get('version') 64 | 65 | # Get entire sections 66 | db_config = config.get_section('database') 67 | server_config = config.get_section('server') 68 | 69 | print(f"{app_title} v{version}") 70 | print(f"Database config: {db_config}") 71 | 72 | # Sample nested TOML structure 73 | toml_content = """ 74 | [database.primary] 75 | host = "db1.example.com" 76 | port = 5432 77 | 78 | [database.replica] 79 | host = "db2.example.com" 80 | port = 5432 81 | 82 | [api.v1] 83 | enabled = true 84 | rate_limit = 100 85 | 86 | [api.v2] 87 | enabled = false 88 | rate_limit = 200 89 | """ 90 | 91 | config = tomllib.loads(toml_content) 92 | 93 | # Access nested values 94 | primary_host = config['database']['primary']['host'] 95 | v1_rate_limit = config['api']['v1']['rate_limit'] 96 | 97 | print(f"Primary DB: {primary_host}") 98 | print(f"API v1 rate limit: {v1_rate_limit}") 99 | 100 | def load_config_safe(config_file='config.toml'): 101 | try: 102 | with open(config_file, 'rb') as f: 103 | return tomllib.load(f) 104 | except FileNotFoundError: 105 | print(f"Config file {config_file} not found, using defaults") 106 | return {} 107 | except tomllib.TOMLDecodeError as e: 108 | print(f"Error parsing TOML: {e}") 109 | raise 110 | 111 | config = load_config_safe('config.toml') 112 | 113 | # Get with defaults 114 | db_host = config.get('database', {}).get('host', 'localhost') 115 | db_port = config.get('database', {}).get('port', 5432) 116 | debug = config.get('server', {}).get('debug', False) 117 | 118 | print(f"Database: {db_host}:{db_port}") 119 | print(f"Debug: {debug}") 120 | 121 | 122 | -------------------------------------------------------------------------------- /config-management-basics/working-with-yaml/examples.py: -------------------------------------------------------------------------------- 1 | import yaml 2 | 3 | # Open and read the YAML file 4 | with open('config.yaml', 'r') as file: 5 | config = yaml.safe_load(file) 6 | 7 | # Access the data 8 | print(config['database']['host']) 9 | 10 | # Your configuration data as Python dictionaries 11 | config = { 12 | 'database': { 13 | 'host': 'localhost', 14 | 'port': 5432, 15 | 'name': 'myapp_db', 16 | 'credentials': { 17 | 'username': 'admin', 18 | 'password': 'secret123' 19 | } 20 | }, 21 | 'server': { 22 | 'host': '0.0.0.0', 23 | 'port': 8000, 24 | 'debug': True 25 | }, 26 | 'features': { 27 | 'enable_cache': True, 28 | 'cache_ttl': 3600 29 | } 30 | } 31 | 32 | # Write to a YAML file 33 | with open('generated_config.yaml', 'w') as file: 34 | yaml.dump(config, file, default_flow_style=False) 35 | 36 | # Configuration with lists 37 | services_config = { 38 | 'services': [ 39 | { 40 | 'name': 'auth-service', 41 | 'url': 'http://auth.example.com', 42 | 'timeout': 30 43 | }, 44 | { 45 | 'name': 'payment-service', 46 | 'url': 'http://payment.example.com', 47 | 'timeout': 60 48 | }, 49 | { 50 | 'name': 'notification-service', 51 | 'url': 'http://notification.example.com', 52 | 'timeout': 15 53 | } 54 | ], 55 | 'retry_policy': { 56 | 'max_attempts': 3, 57 | 'backoff_seconds': 5 58 | } 59 | } 60 | 61 | # Write to file 62 | with open('services.yaml', 'w') as file: 63 | yaml.dump(services_config, file, default_flow_style=False, sort_keys=False) 64 | 65 | # Read it back 66 | with open('services.yaml', 'r') as file: 67 | loaded_services = yaml.safe_load(file) 68 | 69 | # Access list items 70 | for service in loaded_services['services']: 71 | print(f"Service: {service['name']}, URL: {service['url']}") 72 | 73 | import os 74 | 75 | class ConfigManager: 76 | def __init__(self, config_dir='configs'): 77 | self.config_dir = config_dir 78 | self.config = {} 79 | 80 | def load_config(self, environment='development'): 81 | """Load configuration for a specific environment""" 82 | config_file = os.path.join(self.config_dir, f'{environment}.yaml') 83 | 84 | try: 85 | with open(config_file, 'r') as file: 86 | self.config = yaml.safe_load(file) 87 | print(f"✓ Loaded configuration for {environment}") 88 | return self.config 89 | except FileNotFoundError: 90 | print(f"✗ Configuration file not found: {config_file}") 91 | return None 92 | except yaml.YAMLError as e: 93 | print(f"✗ Error parsing YAML: {e}") 94 | return None 95 | 96 | def get(self, key_path, default=None): 97 | """Get a configuration value using dot notation""" 98 | keys = key_path.split('.') 99 | value = self.config 100 | 101 | for key in keys: 102 | if isinstance(value, dict) and key in value: 103 | value = value[key] 104 | else: 105 | return default 106 | 107 | return value 108 | 109 | def save_config(self, environment, config_data): 110 | """Save configuration to a file""" 111 | config_file = os.path.join(self.config_dir, f'{environment}.yaml') 112 | 113 | os.makedirs(self.config_dir, exist_ok=True) 114 | 115 | with open(config_file, 'w') as file: 116 | yaml.dump(config_data, file, default_flow_style=False) 117 | 118 | print(f"✓ Saved configuration for {environment}") 119 | 120 | 121 | -------------------------------------------------------------------------------- /automate-data-cleaning/automate_5_steps.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | 3 | # Step 1: Run Basic Data Quality Checks 4 | 5 | def check_data_quality(df): 6 | # Store initial data quality metrics 7 | quality_report = { 8 | 'missing_values': df.isnull().sum().to_dict(), 9 | 'duplicates': df.duplicated().sum(), 10 | 'total_rows': len(df), 11 | 'memory_usage': df.memory_usage().sum() / 1024**2 # in MB 12 | } 13 | return quality_report 14 | 15 | # Step 2: Standardize Data Types 16 | 17 | def standardize_datatypes(df): 18 | for column in df.columns: 19 | # Try converting string dates to datetime 20 | if df[column].dtype == 'object': 21 | try: 22 | df[column] = pd.to_datetime(df[column]) 23 | print(f"Converted {column} to datetime") 24 | except ValueError: 25 | # Try converting to numeric if datetime fails 26 | try: 27 | df[column] = pd.to_numeric(df[column].str.replace('$', '').str.replace(',', '')) 28 | print(f"Converted {column} to numeric") 29 | except: 30 | pass 31 | return df 32 | 33 | # Step 3: Handle Missing Values 34 | 35 | from sklearn.impute import SimpleImputer 36 | 37 | def handle_missing_values(df): 38 | # Handle numeric columns 39 | numeric_columns = df.select_dtypes(include=['int64', 'float64']).columns 40 | if len(numeric_columns) > 0: 41 | num_imputer = SimpleImputer(strategy='median') 42 | df[numeric_columns] = num_imputer.fit_transform(df[numeric_columns]) 43 | 44 | # Handle categorical columns 45 | categorical_columns = df.select_dtypes(include=['object']).columns 46 | if len(categorical_columns) > 0: 47 | cat_imputer = SimpleImputer(strategy='most_frequent') 48 | df[categorical_columns] = cat_imputer.fit_transform(df[categorical_columns]) 49 | 50 | return df 51 | 52 | # Step 4: Detect and Handle Outliers 53 | 54 | def remove_outliers(df): 55 | numeric_columns = df.select_dtypes(include=['int64', 'float64']).columns 56 | outliers_removed = {} 57 | 58 | for column in numeric_columns: 59 | Q1 = df[column].quantile(0.25) 60 | Q3 = df[column].quantile(0.75) 61 | IQR = Q3 - Q1 62 | lower_bound = Q1 - 1.5 * IQR 63 | upper_bound = Q3 + 1.5 * IQR 64 | 65 | # Count outliers before removing 66 | outliers = df[(df[column] < lower_bound) | (df[column] > upper_bound)].shape[0] 67 | 68 | # Cap the values instead of removing them 69 | df[column] = df[column].clip(lower=lower_bound, upper=upper_bound) 70 | 71 | if outliers > 0: 72 | outliers_removed[column] = outliers 73 | 74 | return df, outliers_removed 75 | 76 | # Step 5: Validate the Results 77 | 78 | def validate_cleaning(df, original_shape, cleaning_report): 79 | validation_results = { 80 | 'rows_remaining': len(df), 81 | 'missing_values_remaining': df.isnull().sum().sum(), 82 | 'duplicates_remaining': df.duplicated().sum(), 83 | 'data_loss_percentage': (1 - len(df)/original_shape[0]) * 100 84 | } 85 | 86 | # Add validation results to the cleaning report 87 | cleaning_report['validation'] = validation_results 88 | return cleaning_report 89 | 90 | 91 | def automated_cleaning_pipeline(df): 92 | # Store original shape for reporting 93 | original_shape = df.shape 94 | 95 | # Initialize cleaning report 96 | cleaning_report = {} 97 | 98 | # Execute each step and collect metrics 99 | cleaning_report['initial_quality'] = check_data_quality(df) 100 | 101 | df = standardize_datatypes(df) 102 | df = handle_missing_values(df) 103 | df, outliers = remove_outliers(df) 104 | cleaning_report['outliers_removed'] = outliers 105 | 106 | # Validate and finalize report 107 | cleaning_report = validate_cleaning(df, original_shape, cleaning_report) 108 | 109 | return df, cleaning_report 110 | -------------------------------------------------------------------------------- /parsing-json/json-parsing-in-python.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | json_string = '{"name": "Sarah Chen", "age": 28, "city": "Portland"}' 4 | person = json.loads(json_string) 5 | 6 | print(person["name"]) # Sarah Chen 7 | print(person["age"]) # 28 8 | print(type(person)) # 9 | 10 | weather_data = ''' 11 | { 12 | "location": { 13 | "city": "Seattle", 14 | "state": "WA", 15 | "coordinates": { 16 | "latitude": 47.6062, 17 | "longitude": -122.3321 18 | } 19 | }, 20 | "current": { 21 | "temperature_f": 58, 22 | "conditions": "Partly Cloudy", 23 | "humidity": 72, 24 | "wind": { 25 | "speed_mph": 8, 26 | "direction": "NW" 27 | } 28 | } 29 | } 30 | ''' 31 | 32 | weather = json.loads(weather_data) 33 | 34 | city = weather["location"]["city"] 35 | temp = weather["current"]["temperature_f"] 36 | wind_speed = weather["current"]["wind"]["speed_mph"] 37 | 38 | print(f"{city}: {temp}°F, Wind {wind_speed} mph") 39 | 40 | 41 | products_json = ''' 42 | [ 43 | { 44 | "id": "PROD-001", 45 | "name": "Wireless Mouse", 46 | "price": 24.99, 47 | "in_stock": true 48 | }, 49 | { 50 | "id": "PROD-002", 51 | "name": "Mechanical Keyboard", 52 | "price": 89.99, 53 | "in_stock": false 54 | }, 55 | { 56 | "id": "PROD-003", 57 | "name": "USB-C Hub", 58 | "price": 34.99, 59 | "in_stock": true 60 | } 61 | ] 62 | ''' 63 | 64 | products = json.loads(products_json) 65 | 66 | print(f"Total products: {len(products)}") 67 | 68 | for product in products: 69 | status = "Available" if product["in_stock"] else "Out of stock" 70 | print(f"{product['name']}: ${product['price']} - {status}") 71 | 72 | first_product = products[0] 73 | print(f"First product ID: {first_product['id']}") 74 | 75 | available_products = [p for p in products if p["in_stock"]] 76 | print(f"Available: {len(available_products)} products") 77 | 78 | # First, let's create a sample config 79 | config_data = { 80 | "api_url": "https://api.example.com/v2", 81 | "timeout": 30, 82 | "retry_attempts": 3, 83 | "enable_logging": True 84 | } 85 | 86 | with open('config.json', 'w') as f: 87 | json.dump(config_data, f, indent=2) 88 | 89 | with open('config.json', 'r') as f: 90 | config = json.load(f) 91 | 92 | print(f"API URL: {config['api_url']}") 93 | print(f"Timeout: {config['timeout']} seconds") 94 | print(f"Logging: {'Enabled' if config['enable_logging'] else 'Disabled'}") 95 | 96 | 97 | def parse_json_safely(json_string): 98 | try: 99 | return json.loads(json_string) 100 | except json.JSONDecodeError as e: 101 | print(f"JSON parsing failed: {e.msg}") 102 | print(f"Error at line {e.lineno}, column {e.colno}") 103 | return None 104 | 105 | # Missing closing quote 106 | bad_json1 = '{"name": "Sarah, "age": 28}' 107 | result1 = parse_json_safely(bad_json1) 108 | print(f"Result 1: {result1}\n") 109 | 110 | # Missing closing brace 111 | bad_json2 = '{"name": "Sarah", "age": 28' 112 | result2 = parse_json_safely(bad_json2) 113 | print(f"Result 2: {result2}\n") 114 | 115 | # Extra comma 116 | bad_json3 = '{"name": "Sarah", "age": 28,}' 117 | result3 = parse_json_safely(bad_json3) 118 | print(f"Result 3: {result3}\n") 119 | 120 | # Valid JSON for comparison 121 | good_json = '{"name": "Sarah", "age": 28}' 122 | result4 = parse_json_safely(good_json) 123 | print(f"Result 4: {result4}") 124 | 125 | def load_json_file_safely(filepath): 126 | try: 127 | with open(filepath, 'r') as f: 128 | return json.load(f) 129 | except FileNotFoundError: 130 | print(f"Error: File '{filepath}' not found") 131 | return None 132 | except PermissionError: 133 | print(f"Error: Permission denied reading '{filepath}'") 134 | return None 135 | except json.JSONDecodeError as e: 136 | print(f"Error: Invalid JSON in '{filepath}'") 137 | print(f" {e.msg} at line {e.lineno}") 138 | return None 139 | 140 | data = load_json_file_safely('missing_file.json') 141 | if data is None: 142 | print("Using default configuration") 143 | data = {"timeout": 30, "retries": 3} 144 | -------------------------------------------------------------------------------- /clean-python/clean_python.py: -------------------------------------------------------------------------------- 1 | def process_user(user_dict): 2 | if user_dict['status'] == 'active': # What if 'status' is missing? 3 | send_email(user_dict['email']) # What if it's 'mail' in some places? 4 | 5 | # Is it 'name', 'full_name', or 'username'? Who knows! 6 | log_activity(f"Processed {user_dict['name']}") 7 | 8 | 9 | from dataclasses import dataclass 10 | from typing import Optional 11 | 12 | @dataclass 13 | class User: 14 | id: int 15 | email: str 16 | full_name: str 17 | status: str 18 | last_login: Optional[datetime] = None 19 | 20 | def process_user(user: User): 21 | if user.status == 'active': 22 | send_email(user.email) 23 | log_activity(f"Processed {user.full_name}") 24 | 25 | 26 | def process_order(order, status): 27 | if status == 'pending': 28 | # process logic 29 | elif status == 'shipped': 30 | # different logic 31 | elif status == 'delivered': 32 | # more logic 33 | else: 34 | raise ValueError(f"Invalid status: {status}") 35 | 36 | # Later in your code... 37 | process_order(order, 'shiped') # Typo! But no IDE warning 38 | 39 | from enum import Enum, auto 40 | 41 | class OrderStatus(Enum): 42 | PENDING = 'pending' 43 | SHIPPED = 'shipped' 44 | DELIVERED = 'delivered' 45 | 46 | def process_order(order, status: OrderStatus): 47 | if status == OrderStatus.PENDING: 48 | # process logic 49 | elif status == OrderStatus.SHIPPED: 50 | # different logic 51 | elif status == OrderStatus.DELIVERED: 52 | # more logic 53 | 54 | # Later in your code... 55 | process_order(order, OrderStatus.SHIPPED) # IDE autocomplete helps! 56 | 57 | def create_user(name, email, admin=False, notify=True, temporary=False): 58 | # Implementation 59 | 60 | # Later in code... 61 | create_user("John Smith", "john@example.com", True, False) 62 | 63 | def create_user(name, email, *, admin=False, notify=True, temporary=False): 64 | # Implementation 65 | 66 | # Now you must use keywords for optional args 67 | create_user("John Smith", "john@example.com", admin=True, notify=False) 68 | 69 | import os 70 | 71 | data_dir = os.path.join('data', 'processed') 72 | if not os.path.exists(data_dir): 73 | os.makedirs(data_dir) 74 | 75 | filepath = os.path.join(data_dir, 'output.csv') 76 | with open(filepath, 'w') as f: 77 | f.write('results\n') 78 | 79 | # Check if we have a JSON file with the same name 80 | json_path = os.path.splitext(filepath)[0] + '.json' 81 | if os.path.exists(json_path): 82 | with open(json_path) as f: 83 | data = json.load(f) 84 | 85 | from pathlib import Path 86 | 87 | data_dir = Path('data') / 'processed' 88 | data_dir.mkdir(parents=True, exist_ok=True) 89 | 90 | filepath = data_dir / 'output.csv' 91 | filepath.write_text('results\n') 92 | 93 | # Check if we have a JSON file with the same name 94 | json_path = filepath.with_suffix('.json') 95 | if json_path.exists(): 96 | data = json.loads(json_path.read_text()) 97 | 98 | def process_payment(order, user): 99 | if order.is_valid: 100 | if user.has_payment_method: 101 | payment_method = user.get_payment_method() 102 | if payment_method.has_sufficient_funds(order.total): 103 | try: 104 | payment_method.charge(order.total) 105 | order.mark_as_paid() 106 | send_receipt(user, order) 107 | return True 108 | except PaymentError as e: 109 | log_error(e) 110 | return False 111 | else: 112 | log_error("Insufficient funds") 113 | return False 114 | else: 115 | log_error("No payment method") 116 | return False 117 | else: 118 | log_error("Invalid order") 119 | return False 120 | 121 | def process_payment(order, user): 122 | # Guard clauses: check preconditions first 123 | if not order.is_valid: 124 | log_error("Invalid order") 125 | return False 126 | 127 | if not user.has_payment_method: 128 | log_error("No payment method") 129 | return False 130 | 131 | payment_method = user.get_payment_method() 132 | if not payment_method.has_sufficient_funds(order.total): 133 | log_error("Insufficient funds") 134 | return False 135 | 136 | # Main logic comes after all validations 137 | try: 138 | payment_method.charge(order.total) 139 | order.mark_as_paid() 140 | send_receipt(user, order) 141 | return True 142 | except PaymentError as e: 143 | log_error(e) 144 | return False 145 | 146 | -------------------------------------------------------------------------------- /efficient-python-for-beginners/main.py: -------------------------------------------------------------------------------- 1 | # instead of this 2 | def process_sales_data(sales): 3 | highest_sale = sales[0] 4 | for sale in sales: 5 | if sale > highest_sale: 6 | highest_sale = sale 7 | 8 | total_sales = 0 9 | for sale in sales: 10 | total_sales += sale 11 | 12 | return highest_sale, total_sales, total_sales / len(sales) 13 | 14 | # do this 15 | def process_sales_data(sales): 16 | return max(sales), sum(sales), sum(sales) / len(sales) 17 | 18 | # Instead of this 19 | def get_premium_customer_emails(customers): 20 | premium_emails = [] 21 | for customer in customers: 22 | if customer['membership_level'] == 'premium' and customer['active']: 23 | email = customer['email'].lower().strip() 24 | premium_emails.append(email) 25 | return premium_emails 26 | 27 | # Do this 28 | def get_premium_customer_emails(customers): 29 | return [ 30 | customer['email'].lower().strip() 31 | for customer in customers 32 | if customer['membership_level'] == 'premium' and customer['active'] 33 | ] 34 | 35 | # Instead of this 36 | def has_permission(user_id, permitted_users): 37 | # permitted_users is a list of user IDs 38 | for p_user in permitted_users: 39 | if p_user == user_id: 40 | return True 41 | return False 42 | 43 | # Usage: 44 | permitted_users = [1001, 1023, 1052, 1076, 1088, 1095, 1102, 1109] 45 | print(has_permission(1088, permitted_users)) # True 46 | 47 | # Do this 48 | def has_permission(user_id, permitted_users): 49 | # permitted_users is now a set of user IDs 50 | return user_id in permitted_users 51 | 52 | # Usage: 53 | permitted_users = {1001, 1023, 1052, 1076, 1088, 1095, 1102, 1109} 54 | print(has_permission(1088, permitted_users)) # True 55 | 56 | # Instead of this 57 | def find_errors(log_file): 58 | with open(log_file, 'r') as file: 59 | lines = file.readlines() 60 | 61 | error_messages = [] 62 | for line in lines: 63 | if '[ERROR]' in line: 64 | timestamp = line.split('[ERROR]')[0].strip() 65 | message = line.split('[ERROR]')[1].strip() 66 | error_messages.append((timestamp, message)) 67 | 68 | return error_messages 69 | 70 | # Do this 71 | def find_errors(log_file): 72 | with open(log_file, 'r') as file: 73 | for line in file: 74 | if '[ERROR]' in line: 75 | timestamp = line.split('[ERROR]')[0].strip() 76 | message = line.split('[ERROR]')[1].strip() 77 | yield (timestamp, message) 78 | 79 | # Usage: 80 | for timestamp, message in find_errors('application.log'): 81 | print(f"Error at {timestamp}: {message}") 82 | 83 | # Instead of this 84 | import re 85 | from datetime import datetime 86 | 87 | def find_recent_errors(logs): 88 | recent_errors = [] 89 | 90 | for log in logs: 91 | # This regex compilation happens on every iteration 92 | timestamp_pattern = re.compile(r'\[(.*?)\]') 93 | timestamp_match = timestamp_pattern.search(log) 94 | 95 | if timestamp_match and '[ERROR]' in log: 96 | # The datetime parsing happens on every iteration 97 | log_time = datetime.strptime(timestamp_match.group(1), '%Y-%m-%d %H:%M:%S') 98 | current_time = datetime.now() 99 | 100 | # Check if the log is from the last 24 hours 101 | time_diff = (current_time - log_time).total_seconds() / 3600 102 | if time_diff <= 24: 103 | recent_errors.append(log) 104 | 105 | return recent_errors 106 | 107 | # do this 108 | import re 109 | from datetime import datetime 110 | 111 | def find_recent_errors(logs): 112 | recent_errors = [] 113 | 114 | # Compile the regex once 115 | timestamp_pattern = re.compile(r'\[(.*?)\]') 116 | # Get the current time once 117 | current_time = datetime.now() 118 | 119 | for log in logs: 120 | timestamp_match = timestamp_pattern.search(log) 121 | 122 | if timestamp_match and '[ERROR]' in log: 123 | log_time = datetime.strptime(timestamp_match.group(1), '%Y-%m-%d %H:%M:%S') 124 | 125 | # Check if the log is recent (last 24 hours) 126 | time_diff = (current_time - log_time).total_seconds() / 3600 127 | if time_diff <= 24: 128 | recent_errors.append(log) 129 | 130 | return recent_errors 131 | 132 | # instead of this 133 | def generate_html_report(data_points): 134 | html = "

Data Report

" 141 | return html 142 | 143 | # do this 144 | def generate_html_report(data_points): 145 | parts = ["

Data Report

") 151 | return "".join(parts) 152 | 153 | 154 | -------------------------------------------------------------------------------- /readable-python-functions/examples.py: -------------------------------------------------------------------------------- 1 | # Bad example 2 | def process(d, t): 3 | return d * (1 + t/100) 4 | 5 | # Good example 6 | def apply_tax_to_price(price, tax_rate): 7 | return price * (1 + tax_rate/100) 8 | 9 | # Bad example 10 | def send_notification(user_id, email, phone, message, subject, 11 | priority, send_email, send_sms, attachment): 12 | # code goes here... 13 | 14 | # Good example 15 | def send_notification(user, notification_config, message_content): 16 | """ 17 | Send a notification to a user based on configuration settings. 18 | 19 | Parameters: 20 | - user: User object with contact information 21 | - notification_config: NotificationConfig with delivery preferences 22 | - message_content: MessageContent with subject, body, and attachments 23 | """ 24 | # code goes here... 25 | 26 | # Bad example 27 | def process_order(order): 28 | # Validate order 29 | # Update inventory 30 | # Charge customer 31 | # Send confirmation email 32 | # Update analytics 33 | 34 | # Good example 35 | def process_order(order): 36 | """Process a customer order from validation through confirmation.""" 37 | validated_order = validate_order(order) 38 | update_inventory(validated_order) 39 | payment_result = charge_customer(validated_order) 40 | if payment_result.is_successful: 41 | send_confirmation_email(validated_order, payment_result) 42 | update_order_analytics(validated_order) 43 | return OrderResult(validated_order, payment_result) 44 | 45 | 46 | # Bad example 47 | def validate_email(email): 48 | """This function validates email.""" 49 | # code goes here... 50 | 51 | # Good example 52 | def validate_email(email: str) -> bool: 53 | """ 54 | Check if an email address has valid format. 55 | 56 | Parameters: 57 | - email: String containing the email address to validate 58 | 59 | Returns: 60 | - True if the email is valid, else False 61 | 62 | Note: 63 | - This validation checks format only, not if the address actually exists 64 | """ 65 | # code goes here... 66 | 67 | 68 | # Bad example 69 | def calculate_final_price(price, discount): 70 | return price * (1 - discount / 100) 71 | 72 | # Good example 73 | def calculate_final_price(price: float, discount_percentage: float) -> float: 74 | """ 75 | Calculate final price after applying the discount. 76 | 77 | Parameters: 78 | - price: Original price of the item 79 | - discount_percentage: Percentage discount to apply (0-100) 80 | 81 | Returns: 82 | - Discounted price 83 | """ 84 | return price * (1 - discount_percentage / 100) 85 | 86 | # Bad example 87 | def create_report(data, include_charts=True, format='pdf', output_path='report.pdf'): 88 | # code goes here... 89 | 90 | # Good example 91 | def create_report( 92 | data: List[Dict[str, Any]], 93 | *, # Force keyword arguments for clarity 94 | include_charts: bool = True, 95 | format_type: Literal['pdf', 'html', 'xlsx'] = 'pdf', 96 | output_path: Optional[str] = None 97 | ) -> str: 98 | """ 99 | Generate a report from the provided data. 100 | 101 | Parameters: 102 | - data: List of records to include in the report 103 | - include_charts: Whether to generate charts from the data 104 | - format_type: Output format of the report 105 | - output_path: Where to save the report (if None, uses a default location) 106 | 107 | Returns: 108 | - Path to the generated report 109 | """ 110 | if output_path is None: 111 | timestamp = datetime.now().strftime('%Y%m%d_%H%M%S') 112 | output_path = f"reports/report_{timestamp}.{format_type}" 113 | 114 | # code goes here... 115 | 116 | return output_path 117 | 118 | # Bad example 119 | def process_payment(payment): 120 | if payment.is_valid: 121 | if payment.amount > 0: 122 | if not payment.is_duplicate: 123 | # Actual payment processing logic (buried in conditionals) 124 | return success_result 125 | else: 126 | return DuplicatePaymentError() 127 | else: 128 | return InvalidAmountError() 129 | else: 130 | return InvalidPaymentError() 131 | 132 | # Goood example 133 | def process_payment(payment: Payment) -> PaymentResult: 134 | """ 135 | Process a payment transaction. 136 | 137 | Returns a PaymentResult or raises appropriate exceptions. 138 | """ 139 | # Guard clauses for validation 140 | if not payment.is_valid: 141 | raise InvalidPaymentError("Payment validation failed") 142 | 143 | if payment.amount <= 0: 144 | raise InvalidAmountError(f"Invalid payment amount: {payment.amount}") 145 | 146 | if payment.is_duplicate: 147 | raise DuplicatePaymentError(f"Duplicate payment ID: {payment.id}") 148 | 149 | # Main logic - now at the top level with no nesting 150 | transaction_id = submit_to_payment_processor(payment) 151 | update_payment_records(payment, transaction_id) 152 | notify_payment_success(payment) 153 | 154 | return PaymentResult( 155 | success=True, 156 | transaction_id=transaction_id, 157 | processed_at=datetime.now() 158 | ) 159 | -------------------------------------------------------------------------------- /decorator-patterns/examples/main.py: -------------------------------------------------------------------------------- 1 | def memoize(func): 2 | """Caches the return value of a function based on its arguments.""" 3 | cache = {} 4 | 5 | def wrapper(*args, **kwargs): 6 | # Create a key that uniquely identifies the function call 7 | key = str(args) + str(kwargs) 8 | 9 | if key not in cache: 10 | cache[key] = func(*args, **kwargs) 11 | return cache[key] 12 | 13 | return wrapper 14 | 15 | @memoize 16 | def fibonacci(n): 17 | """Calculate the nth Fibonacci number.""" 18 | if n <= 1: 19 | return n 20 | return fibonacci(n-1) + fibonacci(n-2) 21 | 22 | # Without memoization, this would be painfully slow 23 | result = fibonacci(50) # Returns almost instantly instead of taking forever 24 | print(f"The 50th Fibonacci number is {result}") 25 | 26 | import logging 27 | import functools 28 | 29 | def log_calls(func=None, level=logging.INFO): 30 | """Log function calls with arguments and return values.""" 31 | 32 | def decorator(func): 33 | @functools.wraps(func) 34 | def wrapper(*args, **kwargs): 35 | args_str = ", ".join([str(a) for a in args]) 36 | kwargs_str = ", ".join([f"{k}={v}" for k, v in kwargs.items()]) 37 | all_args = f"{args_str}{', ' if args_str and kwargs_str else ''}{kwargs_str}" 38 | 39 | logging.log(level, f"Calling {func.__name__}({all_args})") 40 | result = func(*args, **kwargs) 41 | logging.log(level, f"{func.__name__} returned {result}") 42 | 43 | return result 44 | return wrapper 45 | 46 | # Handle both @log_calls and @log_calls(level=logging.DEBUG) 47 | if func is None: 48 | return decorator 49 | return decorator(func) 50 | 51 | logging.basicConfig(level=logging.INFO) 52 | 53 | @log_calls 54 | def divide(a, b): 55 | return a / b 56 | 57 | # This will log the call and the return value 58 | result = divide(10, 2) 59 | 60 | # You can also customize the logging level 61 | @log_calls(level=logging.DEBUG) 62 | def multiply(a, b): 63 | return a * b 64 | 65 | result = multiply(5, 4) 66 | 67 | import time 68 | import functools 69 | 70 | def timeit(func): 71 | """Measure and print the execution time of a function.""" 72 | @functools.wraps(func) 73 | def wrapper(*args, **kwargs): 74 | start_time = time.time() 75 | result = func(*args, **kwargs) 76 | end_time = time.time() 77 | 78 | print(f"{func.__name__} took {end_time - start_time:.4f} seconds to run") 79 | return result 80 | 81 | return wrapper 82 | 83 | @timeit 84 | def slow_function(): 85 | """A deliberately slow function for demonstration.""" 86 | total = 0 87 | for i in range(10000000): 88 | total += i 89 | return total 90 | 91 | result = slow_function() # Will print execution time 92 | 93 | @timeit 94 | def slow_function(): 95 | """A deliberately slow function for demonstration.""" 96 | total = 0 97 | for i in range(10000000): 98 | total += i 99 | return total 100 | 101 | result = slow_function() # Will print execution time 102 | 103 | def retry(max_attempts=3, delay_seconds=1, backoff_factor=2, exceptions=(Exception,)): 104 | """Retry a function if it raises specified exceptions.""" 105 | def decorator(func): 106 | @functools.wraps(func) 107 | def wrapper(*args, **kwargs): 108 | attempts = 0 109 | current_delay = delay_seconds 110 | 111 | while attempts < max_attempts: 112 | try: 113 | return func(*args, **kwargs) 114 | except exceptions as e: 115 | attempts += 1 116 | if attempts == max_attempts: 117 | logging.error(f"Failed after {attempts} attempts. Last error: {e}") 118 | raise 119 | 120 | logging.warning( 121 | f"Attempt {attempts} failed with error: {e}. " 122 | f"Retrying in {current_delay} seconds..." 123 | ) 124 | 125 | time.sleep(current_delay) 126 | current_delay *= backoff_factor 127 | 128 | return wrapper 129 | return decorator 130 | 131 | import random 132 | import requests 133 | 134 | @retry(max_attempts=5, delay_seconds=1, exceptions=(requests.RequestException,)) 135 | def fetch_data(url): 136 | """Fetch data from an API with retry logic.""" 137 | response = requests.get(url, timeout=2) 138 | response.raise_for_status() # Raise exception for 4XX/5XX responses 139 | return response.json() 140 | 141 | # This will retry up to 5 times if the request fails 142 | try: 143 | data = fetch_data('https://api.example.com/data') 144 | print("Successfully fetched data!") 145 | except Exception as e: 146 | print(f"All retry attempts failed: {e}") 147 | 148 | def validate_positive_ints(func): 149 | def wrapper(*args): 150 | for arg in args: 151 | if not isinstance(arg, int) or arg <= 0: 152 | raise ValueError(f"{arg} must be a positive integer") 153 | return func(*args) 154 | return wrapper 155 | 156 | @validate_positive_ints 157 | def calculate_area(length, width): 158 | return length * width 159 | 160 | print(calculate_area(5, 10)) 161 | print(calculate_area(-1, 10)) 162 | -------------------------------------------------------------------------------- /memory-management/memory_management_examples.py: -------------------------------------------------------------------------------- 1 | # When you write this: 2 | x = [1, 2, 3] 3 | 4 | # What actually happens: 5 | # 1. Python creates a list object [1, 2, 3] somewhere in memory 6 | # 2. The variable 'x' stores a POINTER to that object's memory location 7 | # 3. 'x' doesn't "contain" the list - it points to it 8 | 9 | # Create an object 10 | my_list = [1, 2, 3] # my_list points to a list object in memory 11 | 12 | # Create another reference to the SAME object 13 | another_name = my_list # another_name points to the same list 14 | 15 | # They both point to the same object 16 | print(my_list is another_name) 17 | print(id(my_list) == id(another_name)) 18 | 19 | # Modifying through one affects the other (same object!) 20 | my_list.append(4) 21 | print(another_name) 22 | 23 | # But reassigning creates a NEW reference 24 | my_list = [5, 6, 7] # my_list now points to a DIFFERENT object 25 | print(another_name) 26 | 27 | class Person: 28 | def __init__(self, name): 29 | self.name = name 30 | self.friend = None # Will store a reference to another Person 31 | 32 | # Create two people 33 | alice = Person("Alice") 34 | bob = Person("Bob") 35 | 36 | # Make them friends - this creates a circular reference 37 | alice.friend = bob # Alice's object points to Bob's object 38 | bob.friend = alice # Bob's object points to Alice's object 39 | 40 | # Now we have a cycle: 41 | # alice → Person("Alice") → .friend → Person("Bob") → .friend → Person("Alice") → ... 42 | 43 | # More complex circular reference - a node pointing to itself 44 | class Node: 45 | def __init__(self, value): 46 | self.value = value 47 | self.next = None 48 | 49 | # Create a self-referencing node 50 | node = Node(1) 51 | node.next = node # Points to itself! 52 | 53 | # This is a cycle: 54 | # node → Node(1) → .next → Node(1) → .next → ... 55 | 56 | import sys 57 | 58 | # Create an object - reference count is 1 59 | my_list = [1, 2, 3] 60 | print(f"Reference count: {sys.getrefcount(my_list)}") 61 | 62 | # Create another reference - count increases 63 | another_ref = my_list 64 | print(f"Reference count: {sys.getrefcount(my_list)}") 65 | 66 | # Delete one reference - count decreases 67 | del another_ref 68 | print(f"Reference count: {sys.getrefcount(my_list)}") 69 | 70 | # Delete the last reference - object is destroyed 71 | del my_list 72 | 73 | class DataObject: 74 | """Object that announces when it's created and destroyed""" 75 | 76 | def __init__(self, name): 77 | self.name = name 78 | print(f"Created {self.name}") 79 | 80 | def __del__(self): 81 | """Called when object is about to be destroyed""" 82 | print(f"Deleting {self.name}") 83 | 84 | # Create and immediately lose reference 85 | print("Creating object 1:") 86 | obj1 = DataObject("Object 1") 87 | 88 | print("\nCreating object 2 and deleting it:") 89 | obj2 = DataObject("Object 2") 90 | del obj2 91 | 92 | print("\nReassigning obj1:") 93 | obj1 = DataObject("Object 3") 94 | 95 | print("\nFunction scope test:") 96 | def create_temporary(): 97 | temp = DataObject("Temporary") 98 | print("Inside function") 99 | 100 | create_temporary() 101 | print("After function") 102 | 103 | print("\nScript ending...") 104 | 105 | import gc 106 | import sys 107 | 108 | class Node: 109 | def __init__(self, name): 110 | self.name = name 111 | self.reference = None 112 | 113 | def __del__(self): 114 | print(f"Deleting {self.name}") 115 | 116 | # Create two separate objects 117 | print("Creating two nodes:") 118 | node1 = Node("Node 1") 119 | node2 = Node("Node 2") 120 | 121 | # Now create the circular reference 122 | print("\nCreating circular reference:") 123 | node1.reference = node2 124 | node2.reference = node1 125 | 126 | print(f"Node 1 refcount: {sys.getrefcount(node1) - 1}") 127 | print(f"Node 2 refcount: {sys.getrefcount(node2) - 1}") 128 | 129 | # Delete our variables 130 | print("\nDeleting our variables:") 131 | del node1 132 | del node2 133 | 134 | print("Objects still alive! (reference counts aren't zero)") 135 | print("They only reference each other, but counts are still 1 each") 136 | 137 | # Manually trigger garbage collection to find the cycle 138 | print("\nTriggering cyclic garbage collection:") 139 | collected = gc.collect() 140 | print(f"Collected {collected} objects") 141 | 142 | import gc 143 | 144 | # Check if automatic collection is enabled 145 | print(f"GC enabled: {gc.isenabled()}") 146 | 147 | # Get collection thresholds 148 | thresholds = gc.get_threshold() 149 | print(f"\nCollection thresholds: {thresholds}") 150 | print(f" Generation 0 threshold: {thresholds[0]} objects") 151 | print(f" Generation 1 threshold: {thresholds[1]} collections") 152 | print(f" Generation 2 threshold: {thresholds[2]} collections") 153 | 154 | # Get current collection counts 155 | counts = gc.get_count() 156 | print(f"\nCurrent counts: {counts}") 157 | print(f" Gen 0: {counts[0]} objects") 158 | print(f" Gen 1: {counts[1]} collections since last Gen 1") 159 | print(f" Gen 2: {counts[2]} collections since last Gen 2") 160 | 161 | # Manually trigger collection and see what was collected 162 | print(f"\nCollecting garbage...") 163 | collected = gc.collect() 164 | print(f"Collected {collected} objects") 165 | 166 | # Get list of all tracked objects 167 | all_objects = gc.get_objects() 168 | print(f"\nTotal tracked objects: {len(all_objects)}") 169 | 170 | 171 | -------------------------------------------------------------------------------- /secure-hashing/examples.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | import os 3 | import time 4 | 5 | 6 | # Create a simple hash 7 | message = "Hello, World!" 8 | hash_object = hashlib.sha256(message.encode()) 9 | hex_digest = hash_object.hexdigest() 10 | 11 | print(f"Original: {message}") 12 | print(f"SHA-256 Hash: {hex_digest}") 13 | 14 | # Small change, big difference 15 | message1 = "Hello, World!" 16 | message2 = "Hello, World?" # Only changed ! to ? 17 | 18 | hash1 = hashlib.sha256(message1.encode()).hexdigest() 19 | hash2 = hashlib.sha256(message2.encode()).hexdigest() 20 | 21 | print(f"Message 1: {message1}") 22 | print(f"Hash 1: {hash1}") 23 | print(f"\nMessage 2: {message2}") 24 | print(f"Hash 2: {hash2}") 25 | print(f"\nAre they the same? {hash1 == hash2}") 26 | 27 | # Simple password hashing (DON'T USE THIS!) 28 | password = "password123" 29 | hashed = hashlib.sha256(password.encode()).hexdigest() 30 | 31 | print(f"Password: {password}") 32 | print(f"Hash: {hashed}") 33 | 34 | def hash_password_with_salt(password): 35 | # Generate a random salt (16 bytes = 128 bits) 36 | salt = os.urandom(16) 37 | 38 | # Combine password and salt, then hash 39 | hash_object = hashlib.sha256(salt + password.encode()) 40 | password_hash = hash_object.hexdigest() 41 | 42 | # Return both salt and hash (you need the salt to verify later) 43 | return salt.hex(), password_hash 44 | 45 | # Hash the same password twice 46 | password = "password123" 47 | 48 | salt1, hash1 = hash_password_with_salt(password) 49 | salt2, hash2 = hash_password_with_salt(password) 50 | 51 | print(f"Password: {password}\n") 52 | print(f"First attempt:") 53 | print(f" Salt: {salt1}") 54 | print(f" Hash: {hash1}\n") 55 | print(f"Second attempt:") 56 | print(f" Salt: {salt2}") 57 | print(f" Hash: {hash2}\n") 58 | print(f"Same password, different hashes? {hash1 != hash2}") 59 | 60 | def hash_password(password, salt=None): 61 | """Hash a password with a salt. Generate new salt if not provided.""" 62 | if salt is None: 63 | salt = os.urandom(16) 64 | else: 65 | # Convert hex string back to bytes if needed 66 | if isinstance(salt, str): 67 | salt = bytes.fromhex(salt) 68 | 69 | password_hash = hashlib.sha256(salt + password.encode()).hexdigest() 70 | return salt.hex(), password_hash 71 | 72 | def verify_password(password, stored_salt, stored_hash): 73 | """Verify a password against a stored salt and hash.""" 74 | # Hash the provided password with the stored salt 75 | _, new_hash = hash_password(password, stored_salt) 76 | 77 | # Compare the hashes 78 | return new_hash == stored_hash 79 | 80 | print("=== User Registration ===") 81 | user_password = "mySecurePassword!" 82 | salt, password_hash = hash_password(user_password) 83 | print(f"Password: {user_password}") 84 | print(f"Salt: {salt}") 85 | print(f"Hash: {password_hash}") 86 | 87 | # Simulate user login attempts 88 | print("\n=== Login Attempts ===") 89 | correct_attempt = "mySecurePassword!" 90 | wrong_attempt = "wrongPassword" 91 | 92 | print(f"Attempt 1: '{correct_attempt}'") 93 | print(f" Valid? {verify_password(correct_attempt, salt, password_hash)}") 94 | 95 | print(f"\nAttempt 2: '{wrong_attempt}'") 96 | print(f" Valid? {verify_password(wrong_attempt, salt, password_hash)}") 97 | 98 | def hash_password_pbkdf2(password, salt=None, iterations=600000): 99 | """Hash password using PBKDF2 with SHA-256.""" 100 | if salt is None: 101 | salt = os.urandom(32) # 32 bytes = 256 bits 102 | elif isinstance(salt, str): 103 | salt = bytes.fromhex(salt) 104 | 105 | # PBKDF2 with 600,000 iterations (OWASP recommendation for 2024) 106 | password_hash = hashlib.pbkdf2_hmac( 107 | 'sha256', # Hash algorithm 108 | password.encode(), # Password as bytes 109 | salt, # Salt as bytes 110 | iterations, # Number of iterations 111 | dklen=32 # Desired key length (32 bytes = 256 bits) 112 | ) 113 | 114 | return salt.hex(), password_hash.hex(), iterations 115 | 116 | def verify_password_pbkdf2(password, stored_salt, stored_hash, iterations): 117 | """Verify password against PBKDF2 hash.""" 118 | _, new_hash, _ = hash_password_pbkdf2(password, stored_salt, iterations) 119 | return new_hash == stored_hash 120 | 121 | # Hash a password 122 | print("=== PBKDF2 Password Hashing ===") 123 | password = "SuperSecure123!" 124 | salt, hash_value, iterations = hash_password_pbkdf2(password) 125 | 126 | print(f"Password: {password}") 127 | print(f"Salt: {salt}") 128 | print(f"Hash: {hash_value}") 129 | print(f"Iterations: {iterations:,}") 130 | 131 | print("\n=== Verification ===") 132 | is_valid = verify_password_pbkdf2(password, salt, hash_value, iterations) 133 | print(f"Password valid? {is_valid}") 134 | 135 | # Show time comparison 136 | print("\n=== Speed Comparison ===") 137 | test_password = "test123" 138 | 139 | # Simple SHA-256 140 | start = time.time() 141 | for _ in range(100): 142 | hashlib.sha256(test_password.encode()).hexdigest() 143 | sha256_time = time.time() - start 144 | 145 | # PBKDF2 146 | start = time.time() 147 | for _ in range(100): 148 | hash_password_pbkdf2(test_password) 149 | pbkdf2_time = time.time() - start 150 | 151 | print(f"100 SHA-256 hashes: {sha256_time:.3f} seconds") 152 | print(f"100 PBKDF2 hashes: {pbkdf2_time:.3f} seconds") 153 | print(f"PBKDF2 is {pbkdf2_time/sha256_time:.1f}x slower") 154 | 155 | 156 | -------------------------------------------------------------------------------- /useful-python-functions/main.py: -------------------------------------------------------------------------------- 1 | # 1. bisect 2 | 3 | from bisect import bisect_left, bisect_right, insort 4 | 5 | # Let's create a grade tracking system 6 | grades = [60, 70, 75, 85, 90, 95] 7 | 8 | # Find where to insert a new grade while keeping the list sorted 9 | new_grade = 82 10 | position = bisect_left(grades, new_grade) 11 | print(f"Insert 82 at position: {position}") 12 | 13 | # Insert while maintaining sort order 14 | insort(grades, new_grade) 15 | print(f"Grades after insertion: {grades}") 16 | 17 | # Find grade ranges 18 | def grade_to_letter(score): 19 | breakpoints = [60, 70, 80, 90] # F, D, C, B, A 20 | grades = 'FDCBA' 21 | position = bisect_right(breakpoints, score) 22 | return grades[position] 23 | 24 | print(f"Score 82 gets grade: {grade_to_letter(82)}") 25 | print(f"Score 75 gets grade: {grade_to_letter(75)}") 26 | 27 | # 2. itertools.pairwise 28 | 29 | 30 | from itertools import pairwise 31 | 32 | # Let's analyze temperature changes 33 | temperatures = [20, 23, 24, 25, 23, 22, 20] 34 | 35 | # Calculate temperature changes between consecutive readings 36 | changes = [] 37 | for prev, curr in pairwise(temperatures): 38 | change = curr - prev 39 | changes.append(change) 40 | 41 | print("Temperature changes:", changes) 42 | 43 | 44 | # Calculate moving averages 45 | moving_averages = [] 46 | for t1, t2 in pairwise(temperatures): 47 | avg = (t1 + t2) / 2 48 | moving_averages.append(avg) 49 | 50 | print("Moving averages:", moving_averages) 51 | 52 | # Finding the largest temperature jump 53 | max_jump = max(abs(b - a) for a, b in pairwise(temperatures)) 54 | print(f"Largest temperature change: {max_jump} degrees") 55 | 56 | 57 | # 3. statistics.fmean 58 | 59 | from statistics import mean, fmean 60 | import time 61 | 62 | # Let's compare fmean with traditional mean using a real-world example 63 | # Imagine we're analyzing daily temperature readings 64 | temperatures = [ 65 | 21.5, 22.1, 23.4, 22.8, 21.8, 66 | 23.2, 22.7, 23.1, 22.6, 21.9 67 | ] * 100000 # Create a large dataset 68 | 69 | # Let's compare speed and precision 70 | start_time = time.perf_counter() 71 | regular_mean = mean(temperatures) 72 | regular_time = time.perf_counter() - start_time 73 | 74 | start_time = time.perf_counter() 75 | fast_mean = fmean(temperatures) 76 | fast_time = time.perf_counter() - start_time 77 | 78 | print(f"Regular mean: {regular_mean:.10f} (took {regular_time:.4f} seconds)") 79 | print(f"fmean: {fast_mean:.10f} (took {fast_time:.4f} seconds)") 80 | 81 | # 4. itertools.takewhile 82 | 83 | from itertools import takewhile 84 | 85 | # Processing log entries until an error 86 | log_entries = [ 87 | "INFO: System started", 88 | "INFO: Loading data", 89 | "INFO: Processing users", 90 | "ERROR: Database connection failed", 91 | "INFO: Retrying connection", 92 | ] 93 | 94 | # Get all logs until first error 95 | normal_operation = list(takewhile( 96 | lambda x: not x.startswith("ERROR"), 97 | log_entries 98 | )) 99 | print("Logs before first error:") 100 | for entry in normal_operation: 101 | print(entry) 102 | 103 | # 5. operator.attrgettr 104 | 105 | from operator import attrgetter 106 | from datetime import datetime 107 | 108 | # Let's create a simple class to demonstrate 109 | class Article: 110 | def __init__(self, title, author, views, date): 111 | self.title = title 112 | self.author = author 113 | self.stats = type('Stats', (), {'views': views}) # Nested attribute 114 | self.date = date 115 | 116 | def __repr__(self): 117 | return f"{self.title} by {self.author}" 118 | 119 | # Create some sample articles 120 | articles = [ 121 | Article("Python Tips", "Alice", 1500, datetime(2025, 1, 15)), 122 | Article("Data Science", "Bob", 2500, datetime(2025, 1, 20)), 123 | Article("Web Dev", "Alice", 1800, datetime(2025, 1, 10)) 124 | ] 125 | 126 | # Sort articles by multiple criteria 127 | get_author_views = attrgetter('author', 'stats.views') 128 | 129 | # Sort by author and then by views 130 | sorted_articles = sorted(articles, key=get_author_views) 131 | for article in sorted_articles: 132 | print(f"{article.author}: {article.title} ({article.stats.views} views)") 133 | 134 | # You can also use it to extract specific attributes 135 | dates = list(map(attrgetter('date'), articles)) 136 | print("\nArticle dates:", dates) 137 | 138 | # 6. itertools.chain 139 | 140 | 141 | from itertools import chain 142 | 143 | # Let's say we're processing data from multiple sources 144 | sales_data = [ 145 | [('Jan', 100), ('Feb', 150)], 146 | [('Mar', 200), ('Apr', 180)], 147 | [('May', 210), ('Jun', 190)] 148 | ] 149 | 150 | # Flatten the data efficiently 151 | flat_sales = list(chain.from_iterable(sales_data)) 152 | print("Flattened sales data:", flat_sales) 153 | 154 | # List comprehension approach (creates intermediate list): 155 | flat_list = [item for sublist in sales_data for item in sublist] 156 | 157 | # chain.from_iterable approach (generates items one at a time): 158 | flat_iterator = chain.from_iterable(sales_data) 159 | 160 | 161 | # 7. itertools.product 162 | from itertools import product 163 | 164 | # Available options for a custom laptop 165 | processors = ['i5', 'i7', 'i9'] 166 | ram = ['8GB', '16GB', '32GB'] 167 | storage = ['256GB', '512GB', '1TB'] 168 | 169 | # Generate all possible combinations 170 | configurations = list(product(processors, ram, storage)) 171 | 172 | print("Possible laptop configurations:") 173 | for config in configurations: 174 | print(f"Processor: {config[0]}, RAM: {config[1]}, Storage: {config[2]}") 175 | 176 | 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /regex/regex_examples.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "nbformat": 4, 3 | "nbformat_minor": 0, 4 | "metadata": { 5 | "colab": { 6 | "provenance": [] 7 | }, 8 | "kernelspec": { 9 | "name": "python3", 10 | "display_name": "Python 3" 11 | }, 12 | "language_info": { 13 | "name": "python" 14 | } 15 | }, 16 | "cells": [ 17 | { 18 | "cell_type": "code", 19 | "execution_count": null, 20 | "metadata": { 21 | "colab": { 22 | "base_uri": "https://localhost:8080/" 23 | }, 24 | "id": "YPKB2a5Hsw02", 25 | "outputId": "3da0932d-8614-477c-b6a6-d202c795f3da" 26 | }, 27 | "outputs": [ 28 | { 29 | "output_type": "stream", 30 | "name": "stdout", 31 | "text": [ 32 | "Contact info: 1234567890 and 9876543210.\n" 33 | ] 34 | } 35 | ], 36 | "source": [ 37 | "import re\n", 38 | "\n", 39 | "text = \"Contact info: (123)-456-7890 and 987-654-3210.\"\n", 40 | "cleaned_text = re.sub(r'[()-]', '', text)\n", 41 | "print(cleaned_text)\n" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "source": [ 47 | "text = \"Please reach out to us at support@example.org or help@example.org.\"\n", 48 | "emails = re.findall(r'\\b[\\w.-]+?@\\w+?\\.\\w+?\\b', text)\n", 49 | "print(emails)\n" 50 | ], 51 | "metadata": { 52 | "colab": { 53 | "base_uri": "https://localhost:8080/" 54 | }, 55 | "id": "eGS7s-zTs-9T", 56 | "outputId": "74a8b91b-5af7-48f6-9dd3-d690b9f36b21" 57 | }, 58 | "execution_count": null, 59 | "outputs": [ 60 | { 61 | "output_type": "stream", 62 | "name": "stdout", 63 | "text": [ 64 | "['support@example.org', 'help@example.org']\n" 65 | ] 66 | } 67 | ] 68 | }, 69 | { 70 | "cell_type": "code", 71 | "source": [ 72 | "text = \"This\tis\ta\tstring with multiple unnecessary spaces.\"\n", 73 | "cleaned_text = re.sub(r'\\s+', ' ', text)\n", 74 | "print(cleaned_text)\n" 75 | ], 76 | "metadata": { 77 | "colab": { 78 | "base_uri": "https://localhost:8080/" 79 | }, 80 | "id": "dd0K0-LrtmBi", 81 | "outputId": "5436543f-4b19-4081-f8d4-5ec6f64d6d6b" 82 | }, 83 | "execution_count": null, 84 | "outputs": [ 85 | { 86 | "output_type": "stream", 87 | "name": "stdout", 88 | "text": [ 89 | "This is a string with multiple unnecessary spaces.\n" 90 | ] 91 | } 92 | ] 93 | }, 94 | { 95 | "cell_type": "code", 96 | "source": [ 97 | "email = \"test@example.com\"\n", 98 | "if re.match(r'^\\b[\\w.-]+?@\\w+?\\.\\w+?\\b$', email):\n", 99 | " print(\"Valid email\") \n", 100 | "else:\n", 101 | " print(\"Invalid email\")\n" 102 | ], 103 | "metadata": { 104 | "colab": { 105 | "base_uri": "https://localhost:8080/" 106 | }, 107 | "id": "j05fGS1UyCfe", 108 | "outputId": "402a5319-ba31-44d8-e870-ccbc35535af3" 109 | }, 110 | "execution_count": null, 111 | "outputs": [ 112 | { 113 | "output_type": "stream", 114 | "name": "stdout", 115 | "text": [ 116 | "Valid email\n" 117 | ] 118 | } 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "source": [ 124 | "text = \"This is sentence one. And this is sentence two! Is this sentence three?\"\n", 125 | "sentences = re.split(r'[.!?]', text)\n", 126 | "print(sentences) \n" 127 | ], 128 | "metadata": { 129 | "colab": { 130 | "base_uri": "https://localhost:8080/" 131 | }, 132 | "id": "7f68JqnBzBX9", 133 | "outputId": "455de3e7-3cd0-4ffc-ad69-d058e9ecedff" 134 | }, 135 | "execution_count": null, 136 | "outputs": [ 137 | { 138 | "output_type": "stream", 139 | "name": "stdout", 140 | "text": [ 141 | "['This is sentence one', ' And this is sentence two', ' Is this sentence three', '']\n" 142 | ] 143 | } 144 | ] 145 | }, 146 | { 147 | "cell_type": "code", 148 | "source": [ 149 | "import pandas as pd\n", 150 | "\n", 151 | "data = {\n", 152 | "\t'names': ['Alice123', 'Bob!@#', 'Charlie$$$'],\n", 153 | "\t'emails': ['alice@example.com', 'bob_at_example.com', 'charlie@example.com']\n", 154 | "}\n", 155 | "df = pd.DataFrame(data)\n", 156 | "\n", 157 | "# Remove non-alphabetic characters from names\n", 158 | "df['names'] = df['names'].str.replace(r'[^a-zA-Z]', '', regex=True)\n", 159 | "\n", 160 | "# Validate email addresses\n", 161 | "df['valid_email'] = df['emails'].apply(lambda x: bool(re.match(r'^\\b[\\w.-]+?@\\w+?\\.\\w+?\\b$', x)))\n", 162 | "\n", 163 | "print(df)\n" 164 | ], 165 | "metadata": { 166 | "colab": { 167 | "base_uri": "https://localhost:8080/" 168 | }, 169 | "id": "qboHFiS30UMQ", 170 | "outputId": "eeb42cb5-ebcf-4ebe-f301-74c2c1ac184a" 171 | }, 172 | "execution_count": null, 173 | "outputs": [ 174 | { 175 | "output_type": "stream", 176 | "name": "stdout", 177 | "text": [ 178 | " names emails valid_email\n", 179 | "0 Alice alice@example.com True\n", 180 | "1 Bob bob_at_example.com False\n", 181 | "2 Charlie charlie@example.com True\n" 182 | ] 183 | } 184 | ] 185 | }, 186 | { 187 | "cell_type": "code", 188 | "source": [], 189 | "metadata": { 190 | "id": "la5oKWfX0U2Z" 191 | }, 192 | "execution_count": null, 193 | "outputs": [] 194 | } 195 | ] 196 | } 197 | -------------------------------------------------------------------------------- /parse-xml/xml_parsing_in_python.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """xml-parsing-in-python.ipynb 3 | 4 | Automatically generated by Colab. 5 | 6 | Original file is located at 7 | https://colab.research.google.com/drive/1M7NXoZhhQaWk6Eg8rx9VBjRd6IlcnF8d 8 | 9 | This code imports the `xml.etree.ElementTree` library and parses a simple XML string to demonstrate accessing the root element's tag and attributes. 10 | """ 11 | 12 | import xml.etree.ElementTree as ET 13 | 14 | xml_string = """ 15 | 16 | 17 | Wireless Keyboard 18 | 29.99 19 | 20 | 21 | """ 22 | 23 | root = ET.fromstring(xml_string) 24 | print(f"Root tag: {root.tag}") 25 | print(f"Root attributes: {root.attrib}") 26 | 27 | """This code demonstrates how to parse an XML file named `products.xml` and print the tag of the root element.""" 28 | 29 | # Parse an XML file 30 | tree = ET.parse('products.xml') 31 | root = tree.getroot() 32 | 33 | print(f"Root element: {root.tag}") 34 | 35 | """This code shows how to use different methods (`find()`, `findall()`, and `iter()`) to navigate and search within an XML structure parsed from a string.""" 36 | 37 | import xml.etree.ElementTree as ET 38 | 39 | xml_data = """ 40 | 41 | 42 | Wireless Keyboard 43 | 44 | Electronics 45 | Accessories 46 | 47 | 48 | 49 | USB Mouse 50 | 51 | Electronics 52 | 53 | 54 | 55 | """ 56 | 57 | root = ET.fromstring(xml_data) 58 | 59 | # Method 1: find() - returns the FIRST matching element 60 | first_product = root.find('product') 61 | print(f"First product ID: {first_product.get('id')}") 62 | 63 | # Method 2: findall() - returns ALL direct children that match 64 | all_products = root.findall('product') 65 | print(f"Total products: {len(all_products)}") 66 | 67 | # Method 3: iter() - recursively finds ALL matching elements 68 | all_categories = root.iter('category') 69 | category_list = [cat.text for cat in all_categories] 70 | print(f"All categories: {category_list}") 71 | 72 | """This code demonstrates how to extract text content and attributes from elements within a parsed XML string.""" 73 | 74 | xml_data = """ 75 | 76 | 77 | Wireless Keyboard 78 | 29.99 79 | 45 80 | 81 | 82 | """ 83 | 84 | root = ET.fromstring(xml_data) 85 | product = root.find('product') 86 | 87 | # Get element text content 88 | product_name = product.find('name').text 89 | price_text = product.find('price').text 90 | stock_text = product.find('stock').text 91 | 92 | # Get attributes (two ways) 93 | product_id = product.get('id') # Method 1: .get() 94 | product_id_alt = product.attrib['id'] # Method 2: .attrib dictionary 95 | 96 | # Get nested attributes 97 | price_element = product.find('price') 98 | currency = price_element.get('currency') 99 | 100 | print(f"Product: {product_name}") 101 | print(f"ID: {product_id}") 102 | print(f"Price: {currency} {price_text}") 103 | print(f"Stock: {stock_text}") 104 | 105 | """This code defines a function `parse_product_catalog` that takes an XML file path as input and parses it to extract product information, including nested categories, returning a list of product dictionaries. It then demonstrates how to use this function with the `product.xml` file and print the extracted product details.""" 106 | 107 | def parse_product_catalog(xml_file): 108 | """Parse an XML product catalog and return a list of product dictionaries.""" 109 | tree = ET.parse(xml_file) 110 | root = tree.getroot() 111 | 112 | products = [] 113 | 114 | for product_element in root.findall('product'): 115 | # Extract product data 116 | product = { 117 | 'id': product_element.get('id'), 118 | 'name': product_element.find('name').text, 119 | 'price': float(product_element.find('price').text), 120 | 'currency': product_element.find('price').get('currency'), 121 | 'stock': int(product_element.find('stock').text), 122 | 'categories': [] 123 | } 124 | 125 | # Extract categories (nested elements) 126 | categories_element = product_element.find('categories') 127 | if categories_element is not None: 128 | for category in categories_element.findall('category'): 129 | product['categories'].append(category.text) 130 | 131 | products.append(product) 132 | 133 | return products 134 | 135 | # Usage 136 | products = parse_product_catalog('product.xml') 137 | 138 | for product in products: 139 | print(f"\nProduct: {product['name']}") 140 | print(f" ID: {product['id']}") 141 | print(f" Price: {product['currency']} {product['price']}") 142 | print(f" Stock: {product['stock']}") 143 | print(f" Categories: {', '.join(product['categories'])}") 144 | 145 | """This code demonstrates how to safely handle potentially missing elements in the XML data by checking if an element exists before trying to access its text or attributes. It also shows how to provide a default value when getting an attribute if the attribute is missing.""" 146 | 147 | xml_data = """ 148 | 149 | 150 | Wireless Keyboard 151 | 29.99 152 | 153 | 154 | USB Mouse 155 | 156 | 157 | 158 | """ 159 | 160 | root = ET.fromstring(xml_data) 161 | 162 | for product in root.findall('product'): 163 | name = product.find('name').text 164 | 165 | # Safe way to handle potentially missing elements 166 | price_element = product.find('price') 167 | if price_element is not None: 168 | price = float(price_element.text) 169 | currency = price_element.get('currency', 'USD') # Default value 170 | print(f"{name}: {currency} {price}") 171 | else: 172 | print(f"{name}: Price not available") 173 | 174 | """**Note:** For more advanced XML navigation and selection, you can explore using XPath expressions. This is a powerful language for selecting nodes in an XML document and can be very useful for complex structures. We can cover this in another tutorial.""" 175 | 176 | -------------------------------------------------------------------------------- /pydantic-for-python-devs/examples.py: -------------------------------------------------------------------------------- 1 | # ! pip install pydantic 2 | 3 | from pydantic import BaseModel 4 | 5 | class User(BaseModel): 6 | name: str 7 | age: int 8 | email: str 9 | # Create a user 10 | user = User(name="Alice", age="25", email="alice@example.com") 11 | print(user.age) 12 | print(type(user.age)) 13 | 14 | from pydantic import BaseModel, Field 15 | from typing import Optional 16 | 17 | class Product(BaseModel): 18 | name: str 19 | price: float 20 | description: Optional[str] = None 21 | in_stock: bool = True 22 | category: str = Field(default="general", min_length=1) 23 | 24 | # All these work 25 | product1 = Product(name="Widget", price=9.99) 26 | product2 = Product(name="Gadget", price=15.50, description="Useful tool") 27 | 28 | from pydantic import BaseModel, field_validator 29 | import re 30 | 31 | class Account(BaseModel): 32 | username: str 33 | email: str 34 | password: str 35 | 36 | @field_validator('username') 37 | def validate_username(cls, v): 38 | if len(v) < 3: 39 | raise ValueError('Username must be at least 3 characters') 40 | if not v.isalnum(): 41 | raise ValueError('Username must be alphanumeric') 42 | return v.lower() # Normalize to lowercase 43 | 44 | @field_validator('email') 45 | def validate_email(cls, v): 46 | pattern = r'^[\w\.-]+@[\w\.-]+\.\w+$' 47 | if not re.match(pattern, v): 48 | raise ValueError('Invalid email format') 49 | return v 50 | 51 | @field_validator('password') 52 | def validate_password(cls, v): 53 | if len(v) < 8: 54 | raise ValueError('Password must be at least 8 characters') 55 | return v 56 | 57 | account = Account( 58 | username="JohnDoe123", 59 | email="john@example.com", 60 | password="secretpass123" 61 | ) 62 | 63 | from pydantic import BaseModel 64 | from typing import List, Optional 65 | from datetime import datetime 66 | 67 | class Address(BaseModel): 68 | street: str 69 | city: str 70 | state: str 71 | zip_code: str 72 | 73 | @field_validator('zip_code') 74 | def validate_zip(cls, v): 75 | if not v.isdigit() or len(v) != 5: 76 | raise ValueError('ZIP code must be 5 digits') 77 | return v 78 | 79 | class Contact(BaseModel): 80 | name: str 81 | phone: str 82 | email: Optional[str] = None 83 | 84 | class Company(BaseModel): 85 | name: str 86 | founded: datetime 87 | address: Address 88 | contacts: List[Contact] 89 | employee_count: int 90 | is_public: bool = False 91 | 92 | # Complex nested data gets fully validated 93 | company_data = { 94 | "name": "Tech Corp", 95 | "founded": "2020-01-15T10:00:00", 96 | "address": { 97 | "street": "123 Main St", 98 | "city": "San Francisco", 99 | "state": "CA", 100 | "zip_code": "94105" 101 | }, 102 | "contacts": [ 103 | {"name": "John Smith", "phone": "555-0123"}, 104 | {"name": "Jane Doe", "phone": "555-0456", "email": "jane@techcorp.com"} 105 | ], 106 | "employee_count": 150 107 | } 108 | 109 | company = Company(**company_data) 110 | 111 | 112 | from pydantic import BaseModel, Field, field_validator 113 | from typing import Union, Optional 114 | from datetime import datetime 115 | import json 116 | 117 | class APIResponse(BaseModel): 118 | status: str 119 | message: Optional[str] = None 120 | data: Optional[dict] = None 121 | timestamp: datetime = Field(default_factory=datetime.now) 122 | 123 | class UserProfile(BaseModel): 124 | id: int 125 | username: str 126 | full_name: Optional[str] = None 127 | age: Optional[int] = Field(None, ge=0, le=150) # Age constraints 128 | created_at: Union[datetime, str] # Handle multiple formats 129 | is_verified: bool = False 130 | 131 | @field_validator('created_at', mode='before') 132 | def parse_created_at(cls, v): 133 | if isinstance(v, str): 134 | try: 135 | return datetime.fromisoformat(v.replace('Z', '+00:00')) 136 | except ValueError: 137 | raise ValueError('Invalid datetime format') 138 | return v 139 | 140 | # Simulate API response 141 | api_json = ''' 142 | { 143 | "status": "success", 144 | "data": { 145 | "id": 123, 146 | "username": "alice_dev", 147 | "full_name": "Alice Johnson", 148 | "age": "28", 149 | "created_at": "2023-01-15T10:30:00Z", 150 | "is_verified": true 151 | } 152 | } 153 | ''' 154 | 155 | response_data = json.loads(api_json) 156 | api_response = APIResponse(**response_data) 157 | 158 | if api_response.data: 159 | user = UserProfile(**api_response.data) 160 | print(f"User {user.username} created at {user.created_at}") 161 | 162 | from pydantic import BaseModel, ValidationError 163 | from typing import List 164 | 165 | class Order(BaseModel): 166 | order_id: int 167 | customer_email: str 168 | items: List[str] 169 | total: float 170 | 171 | @field_validator('total') 172 | def positive_total(cls, v): 173 | if v <= 0: 174 | raise ValueError('Total must be positive') 175 | return v 176 | 177 | # Invalid data 178 | bad_data = { 179 | "order_id": "not_a_number", 180 | "customer_email": "invalid_email", 181 | "items": "should_be_list", 182 | "total": -10.50 183 | } 184 | 185 | try: 186 | order = Order(**bad_data) 187 | except ValidationError as e: 188 | print("Validation errors:") 189 | for error in e.errors(): 190 | field = error['loc'][0] 191 | message = error['msg'] 192 | print(f" {field}: {message}") 193 | 194 | # Get JSON representation of errors 195 | print("\nJSON errors:") 196 | print(e.json(indent=2)) 197 | 198 | 199 | from pydantic import BaseModel 200 | from datetime import datetime 201 | 202 | class Event(BaseModel): 203 | name: str 204 | date: datetime 205 | attendees: int 206 | is_public: bool = True 207 | 208 | event = Event( 209 | name="Python Meetup", 210 | date=datetime(2024, 3, 15, 18, 30), 211 | attendees=45 212 | ) 213 | 214 | # Export to dictionary 215 | event_dict = event.model_dump() 216 | print(event_dict) 217 | 218 | # Export to JSON string 219 | event_json = event.model_dump_json() 220 | print(event_json) 221 | 222 | # Export with exclusions 223 | public_data = event.model_dump(exclude={'attendees'}) 224 | print(public_data) 225 | 226 | # Export with custom serialization 227 | formatted_json = event.model_dump_json(indent=2) 228 | print(formatted_json) 229 | 230 | --------------------------------------------------------------------------------