├── 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 | 
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
"
135 |
136 | for point in data_points:
137 | # This creates a new string object on each iteration
138 | html += f"- {point['name']}: {point['value']} ({point['timestamp']})
"
139 |
140 | html += "
"
141 | return html
142 |
143 | # do this
144 | def generate_html_report(data_points):
145 | parts = ["Data Report
"]
146 |
147 | for point in data_points:
148 | parts.append(f"- {point['name']}: {point['value']} ({point['timestamp']})
")
149 |
150 | parts.append("
")
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 |
--------------------------------------------------------------------------------