├── README.md ├── any-all ├── all_examples.py └── any_examples.py ├── automate-data-cleaning ├── automate_5_steps.py └── useful_snippets.py ├── better-python ├── README.md └── main.py ├── big-o-examples ├── README.md ├── o1_example.py ├── o2n_example.py ├── ofactorialn_example.py ├── ologn_example.py ├── on_example.py ├── onlogn_example.py └── onsq_example.py ├── bytes2str └── main.py ├── caching └── main.py ├── clean-python └── clean_python.py ├── common-gotchas ├── int_quirk.py ├── mutable_defaults.py ├── python_oddities.ipynb ├── shallow_and_deep_copy.py ├── tup_assignment.py └── var_scope.py ├── custom-exceptions └── main.py ├── custom_context_manager └── main.py ├── data-cleaning ├── README.md └── data_cleaning_one_liners.ipynb ├── data-structures ├── README.md └── python_data_structures.ipynb ├── decorator-patterns ├── examples │ ├── custom_python_decorator_patterns.ipynb │ └── main.py └── more_examples │ └── main.py ├── dict-to-json ├── handle_datetime.py └── main.py ├── efficient-python-for-beginners └── main.py ├── enums ├── main.py ├── main1.py └── task.py ├── error-handling ├── 1_context_manager.py ├── 2_custom_exceptions.py ├── 3_exception_chaining.py ├── 4_error_handling_decorators.py ├── 5_try_finally.py └── README.md ├── for-else └── main.py ├── function-args └── main.py ├── go-intro ├── data_structures_example.go ├── func_example.go ├── goroutine_example.go ├── loops_conditionals.go ├── loops_examples.go └── variables.go ├── handle-large-datasets ├── tip_1.py ├── tip_2.py ├── tip_3.py ├── tip_4.py └── tip_5.py ├── keyerrors └── main.py ├── merge-dict └── main.py ├── misused-python-functions ├── Commonly_Misused_Python_Functions.ipynb └── main.py ├── name-main-python ├── example-1 │ ├── __pycache__ │ │ └── module1.cpython-38.pyc │ ├── module1.py │ └── module2.py └── example-2 │ ├── __pycache__ │ ├── add.cpython-38.pyc │ └── test_add.cpython-38.pyc │ ├── add.py │ └── test_add.py ├── natural-sorting ├── README.md └── main.py ├── pandas-df ├── pandas_dataframe.ipynb └── students.csv ├── partial-functions ├── example1.py ├── example2.py ├── example3.py └── example4.py ├── pathlib-examples ├── README.md ├── back_up.py ├── organize.py └── search.py ├── pathlib-tutorial └── main.py ├── pydantic-basics ├── employees.json └── main.py ├── pytest ├── README.md ├── tests │ ├── __init__.py │ └── test_todo_manager.py └── todo │ ├── __init__.py │ └── todo_manager.py ├── python-lists └── basics.py ├── python-magic-methods └── main.py ├── python-sleep ├── countdown.py ├── delay_times.py ├── simple_example.py └── threads.py ├── readable-python-functions └── examples.py ├── regex ├── main.py └── regex_examples.ipynb ├── search ├── binary_search.py └── linear_search.py ├── sql-tips └── main.py ├── sqlite-tut ├── README.md ├── connect_to_db.py ├── delete_record.py ├── filter.py ├── insert.py └── read_and_update.py ├── string-manipulation ├── README.md └── string_manipulation_one_liners.ipynb ├── switch-case ├── main1.py ├── main2.py └── main3.py ├── timedelta └── examples.py ├── tracemalloc-tutorial ├── create_data.py └── main.py ├── unit-testing ├── classes │ ├── book.py │ ├── test_book.py │ ├── test_book_1.py │ └── test_book_2.py └── functions │ ├── prime_number.py │ ├── prime_number_1.py │ ├── test_prime.py │ └── test_prime_1.py ├── useful-decorators ├── README.md ├── Useful_Python_Decorators.ipynb ├── cached_property_dec.py ├── contextmanager_dec.py ├── lru_cache_dec.py ├── property_dec.py ├── singledispatch_dec.py ├── total_ordering_dec.py └── wraps_dec.py ├── useful-python-functions ├── main.py ├── useful_python_funcs.ipynb └── useless_python_functions.ipynb ├── walrus-operator ├── antipattern.py └── main.py └── write-better-funcs ├── README.md ├── tip1.py ├── tip2.py ├── tip3.py ├── tip4.py └── tip5.py /README.md: -------------------------------------------------------------------------------- 1 | # Python Tutorials 2 | 3 | > If you're coming from one of my Python tutorials, you'll find the code here. This repo is quite useless otherwise. :) 4 | > 5 | ![fimg-python-tutorials](https://i.imgur.com/4KchgGD.png) 6 | 7 | | Topic | Link to Code | Link to Tutorial| 8 | |-------|--------------|-----------------| 9 | | Writing Readable Python Functions|[Code](https://github.com/balapriyac/python-basics/tree/main/readable-python-functions)|[Tutorial](https://www.kdnuggets.com/the-art-of-writing-readable-python-functions)| 10 | | Efficient Python (for Beginners) | [Code](https://github.com/balapriyac/python-basics/blob/main/efficient-python-for-beginners/main.py) | [Tutorial](https://www.kdnuggets.com/how-to-write-efficient-python-code-even-if-youre-a-beginner)| 11 | | Python Data Structures| [Code](https://github.com/balapriyac/python-basics/tree/main/data-structures) | [Tutorial](https://www.kdnuggets.com/python-data-structures-every-programmer-should-know)| 12 | |Commonly Misused Python Functions | [Code](https://github.com/balapriyac/python-basics/tree/main/misused-python-functions) | [Tutorial](https://www.kdnuggets.com/7-python-functions-youre-probably-misusing-and-dont-realize-it)| 13 | | Intro to Golang | [Code](https://github.com/balapriyac/python-basics/tree/main/go-intro) | [Tutorial](https://www.kdnuggets.com/a-gentle-introduction-to-go-for-python-programmers)| 14 | | Big O Complexity | [Code](https://github.com/balapriyac/python-basics/tree/main/big-o-examples)|[Tutorial](https://www.kdnuggets.com/big-o-complexity-cheat-sheet-coding-interviews)| 15 | |Python Tips (for Data Efficiency and Speed)|[Code](https://github.com/balapriyac/python-basics/tree/main/better-python)|[Tutorial](https://www.kdnuggets.com/5-python-tips-for-data-efficiency-and-speed)| 16 | | Automated Data Cleaning Pipeline| [Code](https://github.com/balapriyac/python-basics/blob/main/automate-data-cleaning/useful_snippets.py) | [Tutorial](https://www.kdnuggets.com/creating-automated-data-cleaning-pipelines-using-python-and-pandas)| 17 | |Automate Data Cleaning in 5 Steps| [Code](https://github.com/balapriyac/python-basics/blob/main/automate-data-cleaning/automate_5_steps.py)|[Tutorial](https://www.kdnuggets.com/how-to-fully-automate-data-cleaning-with-python-in-5-steps)| 18 | |Advanced Error Handling|[Code](https://github.com/balapriyac/python-basics/tree/main/error-handling)|[Tutorial](https://www.kdnuggets.com/advanced-error-handling-in-python-beyond-try-except)| 19 | |`any()` and `all()` Functions | [Code](https://github.com/balapriyac/python-basics/tree/main/any-all)|[Tutorial](https://www.freecodecamp.org/news/python-any-and-all-functions-explained-with-examples/)| 20 | | Python Lists 101 | [Code](https://github.com/balapriyac/python-basics/tree/main/python-lists) | [Tutorial](https://www.freecodecamp.org/news/lists-in-python-comprehensive-guide/)| 21 | |Convert Bytes to String in Python| [Code](https://github.com/balapriyac/python-basics/tree/main/bytes2str) | [Tutorial](https://www.kdnuggets.com/convert-bytes-to-string-in-python-a-tutorial-for-beginners)| 22 | |RegEx for Data Cleaning| [Code](https://github.com/balapriyac/python-basics/tree/main/regex)|[Tutorial](https://www.kdnuggets.com/5-tips-for-using-regular-expressions-in-data-cleaning)| 23 | | Data Cleaning One-Liners | [Code](https://github.com/balapriyac/python-basics/tree/main/data-cleaning) | [Tutorial](https://www.kdnuggets.com/10-useful-python-one-liners-for-data-cleaning)| 24 | | Unit Testing (Python Functions) | [Code](https://github.com/balapriyac/python-basics/tree/main/unit-testing/functions)| [Tutorial](https://www.freecodecamp.org/news/how-to-write-unit-tests-for-python-functions/)| 25 | | Unit Testing (Instance Methods) | [Code](https://github.com/balapriyac/python-basics/tree/main/unit-testing/classes)| [Tutorial](https://www.freecodecamp.org/news/how-to-write-unit-tests-for-instance-methods-in-python/)| 26 | |Tips for Handling Large Datasets| [Code](https://github.com/balapriyac/python-basics/tree/main/handle-large-datasets) | [Tutorial](https://www.kdnuggets.com/tips-handling-large-datasets-python)| 27 | |Caching in Python| [Code](https://github.com/balapriyac/python-basics/tree/main/caching)| [Tutorial](https://www.kdnuggets.com/how-to-speed-up-python-code-with-caching)| 28 | | Creating Custom Exceptions|[Code](https://github.com/balapriyac/python-basics/tree/main/custom-exceptions)|[Tutorial](https://www.kdnuggets.com/how-and-why-to-create-custom-exceptions-in-python)| 29 | |Writing Better Python Functions|[Code](https://github.com/balapriyac/python-basics/tree/main/write-better-funcs)|[Tutorial](https://www.kdnuggets.com/5-tips-for-writing-better-python-functions)| 30 | |Function Arguments|[Code](https://github.com/balapriyac/python-basics/tree/main/function-args)|[Tutorial](https://www.kdnuggets.com/2023/02/python-function-arguments-definitive-guide.html)| 31 | | Partial Functions|[Code](https://github.com/balapriyac/python-basics/tree/main/partial-functions)|[Tutorial](https://www.kdnuggets.com/partial-functions-in-python-a-guide-for-developers)| 32 | |Useful Python Functions| [Code](https://github.com/balapriyac/python-basics/tree/main/useful-python-functions)|[Tutorial](https://www.kdnuggets.com/lesser-known-python-functions-that-are-super-useful)| 33 | |For-Else Loop Construct| [Code](https://github.com/balapriyac/python-basics/tree/main/for-else) | [Tutorial](https://www.freecodecamp.org/news/for-else-loop-in-python/)| 34 | |SQLite Tutorial| [Code](https://github.com/balapriyac/python-basics/tree/main/sqlite-tut)|[Tutorial](https://www.kdnuggets.com/a-guide-to-working-with-sqlite-databases-in-python)| 35 | |Psycopg2 Tutorial| [Code](https://github.com/balapriyac/psycopg2-tutorial) | [Tutorial](https://earthly.dev/blog/psycopg2-postgres-python/)| 36 | |Handling Key Errors|[Code](https://github.com/balapriyac/python-basics/tree/main/keyerrors) |[Tutorial](https://www.freecodecamp.org/news/how-to-handle-keyerror-exceptions-in-python/)| 37 | |Common Python Gotchas!| [Code](https://github.com/balapriyac/python-basics/tree/main/common-gotchas) | [Tutorial](https://www.kdnuggets.com/5-common-python-gotchas-and-how-to-avoid-them)| 38 | | Python Oddities | [Code](https://github.com/balapriyac/python-basics/blob/main/common-gotchas/python_oddities.ipynb) | [Tutorial](https://www.kdnuggets.com/python-oddities-might-surprise-you)| 39 | |Custom Context Managers in Python| [Code](https://github.com/balapriyac/python-basics/tree/main/custom_context_manager)|[Tutorial](https://www.kdnuggets.com/how-to-create-custom-context-managers-in-python)| 40 | |Convert Python Dict to JSON |[Code](https://github.com/balapriyac/python-basics/tree/main/dict-to-json) | [Tutorial](https://www.kdnuggets.com/convert-python-dict-to-json-a-tutorial-for-beginners)| 41 | |Building Enumerations in Python |[Code](https://github.com/balapriyac/python-basics/tree/main/enums) | [Tutorial](https://www.kdnuggets.com/python-enum-how-to-build-enumerations-in-python) 42 | |Understanding `if __name__=='__main__'` | [Code](https://github.com/balapriyac/python-basics/tree/main/name-main-python) | [Tutorial](https://geekflare.com/python-if-name-main/) 43 | |Natural Sorting (Install `natsort` with pip) | [Code](https://github.com/balapriyac/python-basics/tree/main/natural-sorting) | [Tutorial](https://www.kdnuggets.com/exploring-natural-sorting-in-python) 44 | |Pydantic Basics|[Code](https://github.com/balapriyac/python-basics/tree/main/pydantic-basics) | [Tutorial](https://www.kdnuggets.com/pydantic-tutorial-data-validation-in-python-made-simple)| 45 | |Pathlib Tutorial|[Code](https://github.com/balapriyac/python-basics/tree/main/pathlib-tutorial)|[Tutorial](https://www.kdnuggets.com/how-to-navigate-the-filesystem-with-pythons-pathlib)| 46 | |Pathlib File Management|[Code](https://github.com/balapriyac/python-basics/tree/main/pathlib-examples)|[Tutorial](https://www.kdnuggets.com/organize-search-and-back-up-files-with-pythons-pathlib)| 47 | |Walrus Operator|[Code](https://github.com/balapriyac/python-basics/tree/main/walrus-operator)|[Tutorial](https://www.kdnuggets.com/how-not-to-use-pythons-walrus-operator)| 48 | |Tracing Memory Allocation| [Code](https://github.com/balapriyac/python-basics/tree/main/tracemalloc-tutorial)|[Tutorial](https://www.kdnuggets.com/how-to-trace-memory-allocation-in-python)| 49 | |Merge Python Dicts|[Code](https://github.com/balapriyac/python-basics/blob/main/merge-dict/main.py)|[Tutorial](https://www.kdnuggets.com/3-simple-ways-to-merge-python-dictionaries)| 50 | | Unit Testing w/ Pytest| [Code](https://github.com/balapriyac/python-basics/tree/main/pytest) | [Tutorial](https://www.kdnuggets.com/beginners-guide-unit-testing-python-code-pytest)| 51 | | String Manipulation One-Liners| [Code](https://github.com/balapriyac/python-basics/blob/main/string-manipulation/string_manipulation_one_liners.ipynb) | [Tutorial](https://www.kdnuggets.com/15-useful-python-one-liners-string-manipulation)| 52 | | Useful Decorators | [Code](https://github.com/balapriyac/python-basics/tree/main/useful-decorators) | [Tutorial](https://www.kdnuggets.com/7-powerful-python-decorators-to-level-up-your-coding-game)| 53 | | Useless Python Functions | [Code](https://github.com/balapriyac/python-basics/blob/main/useful-python-functions/useless_python_functions.ipynb) | [Tutorial](https://www.kdnuggets.com/7-useless-python-standard-library-functions-you-should-know)| 54 | | Timedelta (work w/ time intervals) | [Code](https://github.com/balapriyac/python-basics/tree/main/timedelta) | [Tutorial](https://www.freecodecamp.org/news/how-to-use-timedelta-objects-in-python/)| 55 | 56 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /automate-data-cleaning/useful_snippets.py: -------------------------------------------------------------------------------- 1 | # Standardize Your Data Import Process 2 | 3 | def load_dataset(file_path, **kwargs): 4 | """ 5 | Load data from various file formats while handling common issues. 6 | 7 | Args: 8 | file_path (str): Path to the data file 9 | **kwargs: Additional arguments to pass to the appropriate pandas reader 10 | 11 | Returns: 12 | pd.DataFrame: Loaded and initially processed dataframe 13 | """ 14 | import pandas as pd 15 | from pathlib import Path 16 | 17 | file_type = Path(file_path).suffix.lower() 18 | 19 | # Dictionary of file handlers 20 | handlers = { 21 | '.csv': pd.read_csv, 22 | '.xlsx': pd.read_excel, 23 | '.json': pd.read_json, 24 | '.parquet': pd.read_parquet 25 | } 26 | 27 | # Get appropriate reader function 28 | reader = handlers.get(file_type) 29 | if reader is None: 30 | raise ValueError(f"Unsupported file type: {file_type}") 31 | 32 | # Load data with common cleaning parameters 33 | df = reader(file_path, **kwargs) 34 | 35 | # Initial cleaning steps 36 | df.columns = df.columns.str.strip().str.lower() # Standardize column names 37 | df = df.replace('', pd.NA) # Convert empty strings to NA 38 | 39 | return df 40 | 41 | # Implement Automated Data Validation 42 | 43 | def validate_dataset(df, validation_rules=None): 44 | """ 45 | Apply validation rules to a dataframe and return validation results. 46 | 47 | Args: 48 | df (pd.DataFrame): Input dataframe 49 | validation_rules (dict): Dictionary of column names and their validation rules 50 | 51 | Returns: 52 | dict: Validation results with issues found 53 | """ 54 | if validation_rules is None: 55 | validation_rules = { 56 | 'numeric_columns': { 57 | 'check_type': 'numeric', 58 | 'min_value': 0, 59 | 'max_value': 1000000 60 | }, 61 | 'date_columns': { 62 | 'check_type': 'date', 63 | 'min_date': '2000-01-01', 64 | 'max_date': '2025-12-31' 65 | } 66 | } 67 | 68 | validation_results = {} 69 | 70 | for column, rules in validation_rules.items(): 71 | if column not in df.columns: 72 | continue 73 | 74 | issues = [] 75 | 76 | # Check for missing values 77 | missing_count = df[column].isna().sum() 78 | if missing_count > 0: 79 | issues.append(f"Found {missing_count} missing values") 80 | 81 | # Type-specific validations 82 | if rules['check_type'] == 'numeric': 83 | if not pd.api.types.is_numeric_dtype(df[column]): 84 | issues.append("Column should be numeric") 85 | else: 86 | out_of_range = df[ 87 | (df[column] < rules['min_value']) | 88 | (df[column] > rules['max_value']) 89 | ] 90 | if len(out_of_range) > 0: 91 | issues.append(f"Found {len(out_of_range)} values outside allowed range") 92 | 93 | validation_results[column] = issues 94 | 95 | return validation_results 96 | 97 | # Create a Data Cleaning Pipeline 98 | 99 | class DataCleaningPipeline: 100 | """ 101 | A modular pipeline for cleaning data with customizable steps. 102 | """ 103 | 104 | def __init__(self): 105 | self.steps = [] 106 | 107 | def add_step(self, name, function): 108 | """Add a cleaning step.""" 109 | self.steps.append({'name': name, 'function': function}) 110 | 111 | def execute(self, df): 112 | """Execute all cleaning steps in order.""" 113 | results = [] 114 | current_df = df.copy() 115 | 116 | for step in self.steps: 117 | try: 118 | current_df = step['function'](current_df) 119 | results.append({ 120 | 'step': step['name'], 121 | 'status': 'success', 122 | 'rows_affected': len(current_df) 123 | }) 124 | except Exception as e: 125 | results.append({ 126 | 'step': step['name'], 127 | 'status': 'failed', 128 | 'error': str(e) 129 | }) 130 | break 131 | 132 | return current_df, results 133 | 134 | def remove_duplicates(df): 135 | return df.drop_duplicates() 136 | 137 | def standardize_dates(df): 138 | date_columns = df.select_dtypes(include=['datetime64']).columns 139 | for col in date_columns: 140 | df[col] = pd.to_datetime(df[col], errors='coerce') 141 | return df 142 | 143 | pipeline = DataCleaningPipeline() 144 | pipeline.add_step('remove_duplicates', remove_duplicates) 145 | pipeline.add_step('standardize_dates', standardize_dates) 146 | 147 | # Automate String Cleaning and Standardization 148 | 149 | def clean_text_columns(df, columns=None): 150 | """ 151 | Apply standardized text cleaning to specified columns. 152 | 153 | Args: 154 | df (pd.DataFrame): Input dataframe 155 | columns (list): List of columns to clean. If None, clean all object columns 156 | 157 | Returns: 158 | pd.DataFrame: Dataframe with cleaned text columns 159 | """ 160 | if columns is None: 161 | columns = df.select_dtypes(include=['object']).columns 162 | 163 | df = df.copy() 164 | 165 | for column in columns: 166 | if column not in df.columns: 167 | continue 168 | 169 | # Apply string cleaning operations 170 | df[column] = (df[column] 171 | .astype(str) 172 | .str.strip() 173 | .str.lower() 174 | .replace(r'\s+', ' ', regex=True) # Replace multiple spaces 175 | .replace(r'[^\w\s]', '', regex=True)) # Remove special characters 176 | 177 | return df 178 | 179 | # Monitor Data Quality Over Time 180 | 181 | def generate_quality_metrics(df, baseline_metrics=None): 182 | """ 183 | Generate quality metrics for a dataset and compare with baseline if provided. 184 | 185 | Args: 186 | df (pd.DataFrame): Input dataframe 187 | baseline_metrics (dict): Previous metrics to compare against 188 | 189 | Returns: 190 | dict: Current metrics and comparison with baseline 191 | """ 192 | metrics = { 193 | 'row_count': len(df), 194 | 'missing_values': df.isna().sum().to_dict(), 195 | 'unique_values': df.nunique().to_dict(), 196 | 'data_types': df.dtypes.astype(str).to_dict() 197 | } 198 | 199 | # Add descriptive statistics for numeric columns 200 | numeric_columns = df.select_dtypes(include=['number']).columns 201 | metrics['numeric_stats'] = df[numeric_columns].describe().to_dict() 202 | 203 | # Compare with baseline if provided 204 | if baseline_metrics: 205 | metrics['changes'] = { 206 | 'row_count_change': metrics['row_count'] - baseline_metrics['row_count'], 207 | 'missing_values_change': { 208 | col: metrics['missing_values'][col] - baseline_metrics['missing_values'][col] 209 | for col in metrics['missing_values'] 210 | } 211 | } 212 | 213 | return metrics 214 | -------------------------------------------------------------------------------- /better-python/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /big-o-examples/README.md: -------------------------------------------------------------------------------- 1 | ## Big O Complexity 2 | ![image](https://github.com/user-attachments/assets/f65d0a0a-f8b6-4ea8-97fc-704631b0363f) 3 | 4 | Examples: 5 | - [Constant time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/o1_example.py) 6 | - [Logarithmic time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/ologn_example.py) 7 | - [Linear time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/on_example.py) 8 | - [Linearithmic time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/onlogn_example.py) 9 | - [Quadratic time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/onsq_example.py) 10 | - [Exponential time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/o2n_example.py) 11 | - [Factorial time](https://github.com/balapriyac/python-basics/blob/main/big-o-examples/ofactorialn_example.py) 12 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /common-gotchas/python_oddities.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": "markdown", 19 | "source": [ 20 | "## Mutable Default Arguments" 21 | ], 22 | "metadata": { 23 | "id": "1xD4WrXCjT3g" 24 | } 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 5, 29 | "metadata": { 30 | "id": "5kKU9ebrcb_6" 31 | }, 32 | "outputs": [], 33 | "source": [ 34 | "def add_item(item, list_of_items=[]):\n", 35 | " list_of_items.append(item)\n", 36 | " return list_of_items" 37 | ] 38 | }, 39 | { 40 | "cell_type": "code", 41 | "source": [ 42 | "print(add_item(1))\n", 43 | "print(add_item(2))" 44 | ], 45 | "metadata": { 46 | "id": "yzExL8dFX0c9", 47 | "colab": { 48 | "base_uri": "https://localhost:8080/" 49 | }, 50 | "outputId": "723bd79a-5d39-4372-f685-ad0c1649947d" 51 | }, 52 | "execution_count": 6, 53 | "outputs": [ 54 | { 55 | "output_type": "stream", 56 | "name": "stdout", 57 | "text": [ 58 | "[1]\n", 59 | "[1, 2]\n" 60 | ] 61 | } 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "source": [ 67 | "def add_item(item, list_of_items=None):\n", 68 | " if list_of_items is None:\n", 69 | " list_of_items = []\n", 70 | " list_of_items.append(item)\n", 71 | " return list_of_items" 72 | ], 73 | "metadata": { 74 | "id": "K9E_FAEqhpcj" 75 | }, 76 | "execution_count": 7, 77 | "outputs": [] 78 | }, 79 | { 80 | "cell_type": "code", 81 | "source": [ 82 | "print(add_item(1))\n", 83 | "print(add_item(2))" 84 | ], 85 | "metadata": { 86 | "colab": { 87 | "base_uri": "https://localhost:8080/" 88 | }, 89 | "id": "J1sRQf_nh4RN", 90 | "outputId": "7dca28a3-4ef0-4b07-fd03-b56fa9662093" 91 | }, 92 | "execution_count": 8, 93 | "outputs": [ 94 | { 95 | "output_type": "stream", 96 | "name": "stdout", 97 | "text": [ 98 | "[1]\n", 99 | "[2]\n" 100 | ] 101 | } 102 | ] 103 | }, 104 | { 105 | "cell_type": "markdown", 106 | "source": [ 107 | "## Late Binding Closures" 108 | ], 109 | "metadata": { 110 | "id": "L2xJBvuljPoP" 111 | } 112 | }, 113 | { 114 | "cell_type": "code", 115 | "source": [ 116 | "functions = []\n", 117 | "for i in range(3):\n", 118 | " functions.append(lambda: i)\n", 119 | "\n", 120 | "print([f() for f in functions])" 121 | ], 122 | "metadata": { 123 | "colab": { 124 | "base_uri": "https://localhost:8080/" 125 | }, 126 | "id": "ynyKwa2RiGo-", 127 | "outputId": "2cbb21ea-12bc-4f3f-86db-5e7a517cc362" 128 | }, 129 | "execution_count": 9, 130 | "outputs": [ 131 | { 132 | "output_type": "stream", 133 | "name": "stdout", 134 | "text": [ 135 | "[2, 2, 2]\n" 136 | ] 137 | } 138 | ] 139 | }, 140 | { 141 | "cell_type": "code", 142 | "source": [ 143 | "functions = []\n", 144 | "for i in range(3):\n", 145 | " functions.append(lambda x=i: x) # Using default argument to capture current value\n", 146 | "\n", 147 | "print([f() for f in functions])" 148 | ], 149 | "metadata": { 150 | "colab": { 151 | "base_uri": "https://localhost:8080/" 152 | }, 153 | "id": "YB_x4K5ziYaG", 154 | "outputId": "e2c9caf3-0d94-40fd-a3d6-282e02f43d6e" 155 | }, 156 | "execution_count": 10, 157 | "outputs": [ 158 | { 159 | "output_type": "stream", 160 | "name": "stdout", 161 | "text": [ 162 | "[0, 1, 2]\n" 163 | ] 164 | } 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "source": [ 170 | "## Identity vs. Equality" 171 | ], 172 | "metadata": { 173 | "id": "xgWze-27jLWA" 174 | } 175 | }, 176 | { 177 | "cell_type": "code", 178 | "source": [ 179 | "# Integer caching\n", 180 | "a = 256\n", 181 | "b = 256\n", 182 | "print(a is b)" 183 | ], 184 | "metadata": { 185 | "id": "GQDWkQXSX03i", 186 | "colab": { 187 | "base_uri": "https://localhost:8080/" 188 | }, 189 | "outputId": "817045fc-7531-4027-be54-053cfa232cf7" 190 | }, 191 | "execution_count": 1, 192 | "outputs": [ 193 | { 194 | "output_type": "stream", 195 | "name": "stdout", 196 | "text": [ 197 | "True\n" 198 | ] 199 | } 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "source": [ 205 | "c = 257\n", 206 | "d = 257\n", 207 | "print(c is d)" 208 | ], 209 | "metadata": { 210 | "id": "htZVCasxX2fF", 211 | "colab": { 212 | "base_uri": "https://localhost:8080/" 213 | }, 214 | "outputId": "ea8ac079-6897-478d-c00f-f383d5643a96" 215 | }, 216 | "execution_count": 2, 217 | "outputs": [ 218 | { 219 | "output_type": "stream", 220 | "name": "stdout", 221 | "text": [ 222 | "False\n" 223 | ] 224 | } 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "source": [ 230 | "# String interning\n", 231 | "x = \"hello\"\n", 232 | "y = \"hello\"\n", 233 | "print(x is y)" 234 | ], 235 | "metadata": { 236 | "id": "1T2xgVk5X4Rk", 237 | "colab": { 238 | "base_uri": "https://localhost:8080/" 239 | }, 240 | "outputId": "b133aae7-5744-4e7a-bf1a-eeffcc69122e" 241 | }, 242 | "execution_count": 3, 243 | "outputs": [ 244 | { 245 | "output_type": "stream", 246 | "name": "stdout", 247 | "text": [ 248 | "True\n" 249 | ] 250 | } 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "source": [ 256 | "p = \"hello!\"\n", 257 | "q = \"hello!\"\n", 258 | "print(p is q)" 259 | ], 260 | "metadata": { 261 | "id": "yH_RlLOpX6IV", 262 | "colab": { 263 | "base_uri": "https://localhost:8080/" 264 | }, 265 | "outputId": "d142a5f3-03a6-4cf8-c518-5e940003b2d4" 266 | }, 267 | "execution_count": 4, 268 | "outputs": [ 269 | { 270 | "output_type": "stream", 271 | "name": "stdout", 272 | "text": [ 273 | "False\n" 274 | ] 275 | } 276 | ] 277 | }, 278 | { 279 | "cell_type": "markdown", 280 | "source": [ 281 | "## Variable Unpacking Surprises" 282 | ], 283 | "metadata": { 284 | "id": "OvZn0KQmjHxd" 285 | } 286 | }, 287 | { 288 | "cell_type": "code", 289 | "source": [ 290 | "a, b = 1, 2\n", 291 | "print(a, b)" 292 | ], 293 | "metadata": { 294 | "colab": { 295 | "base_uri": "https://localhost:8080/" 296 | }, 297 | "id": "h1gPz7jec9h3", 298 | "outputId": "2ee15a27-d3a6-4b34-e188-cbdd302fd0e9" 299 | }, 300 | "execution_count": 11, 301 | "outputs": [ 302 | { 303 | "output_type": "stream", 304 | "name": "stdout", 305 | "text": [ 306 | "1 2\n" 307 | ] 308 | } 309 | ] 310 | }, 311 | { 312 | "cell_type": "code", 313 | "source": [ 314 | "a, *b = 1, 2, 3, 4\n", 315 | "print(a, b)" 316 | ], 317 | "metadata": { 318 | "colab": { 319 | "base_uri": "https://localhost:8080/" 320 | }, 321 | "id": "7IDBOIodifXf", 322 | "outputId": "19d98c5f-4167-45a3-ce1f-f6171a09bbba" 323 | }, 324 | "execution_count": 15, 325 | "outputs": [ 326 | { 327 | "output_type": "stream", 328 | "name": "stdout", 329 | "text": [ 330 | "1 [2, 3, 4]\n" 331 | ] 332 | } 333 | ] 334 | }, 335 | { 336 | "cell_type": "code", 337 | "source": [ 338 | "a, *b, = 1,\n", 339 | "print(a, b)" 340 | ], 341 | "metadata": { 342 | "colab": { 343 | "base_uri": "https://localhost:8080/" 344 | }, 345 | "id": "517VRX0UihiE", 346 | "outputId": "19cf3eed-ccfc-4b01-e0eb-120e2e8be7b4" 347 | }, 348 | "execution_count": 14, 349 | "outputs": [ 350 | { 351 | "output_type": "stream", 352 | "name": "stdout", 353 | "text": [ 354 | "1 []\n" 355 | ] 356 | } 357 | ] 358 | }, 359 | { 360 | "cell_type": "code", 361 | "source": [ 362 | "(*a,) = [1, 2, 3]\n", 363 | "print(a)" 364 | ], 365 | "metadata": { 366 | "colab": { 367 | "base_uri": "https://localhost:8080/" 368 | }, 369 | "id": "HJzv3JivijVI", 370 | "outputId": "696d000f-3402-4591-8fad-97859b9ae5b7" 371 | }, 372 | "execution_count": 16, 373 | "outputs": [ 374 | { 375 | "output_type": "stream", 376 | "name": "stdout", 377 | "text": [ 378 | "[1, 2, 3]\n" 379 | ] 380 | } 381 | ] 382 | }, 383 | { 384 | "cell_type": "markdown", 385 | "source": [ 386 | "## List Multiplication" 387 | ], 388 | "metadata": { 389 | "id": "dLE9l8GVjD7e" 390 | } 391 | }, 392 | { 393 | "cell_type": "code", 394 | "source": [ 395 | "# Simple list multiplication\n", 396 | "simple_list = [1] * 3\n", 397 | "print(simple_list)" 398 | ], 399 | "metadata": { 400 | "colab": { 401 | "base_uri": "https://localhost:8080/" 402 | }, 403 | "id": "CMWsqdvHinte", 404 | "outputId": "75e0ac5b-0c51-4379-af61-9d90a7d0f761" 405 | }, 406 | "execution_count": 17, 407 | "outputs": [ 408 | { 409 | "output_type": "stream", 410 | "name": "stdout", 411 | "text": [ 412 | "[1, 1, 1]\n" 413 | ] 414 | } 415 | ] 416 | }, 417 | { 418 | "cell_type": "code", 419 | "source": [ 420 | "nested_list = [[1, 2]] * 3\n", 421 | "print(nested_list)" 422 | ], 423 | "metadata": { 424 | "colab": { 425 | "base_uri": "https://localhost:8080/" 426 | }, 427 | "id": "Tb27gHK6irCB", 428 | "outputId": "d3535272-84fe-470b-9699-6106f608ab4f" 429 | }, 430 | "execution_count": 18, 431 | "outputs": [ 432 | { 433 | "output_type": "stream", 434 | "name": "stdout", 435 | "text": [ 436 | "[[1, 2], [1, 2], [1, 2]]\n" 437 | ] 438 | } 439 | ] 440 | }, 441 | { 442 | "cell_type": "code", 443 | "source": [ 444 | "nested_list[0][0] = 5\n", 445 | "print(nested_list)" 446 | ], 447 | "metadata": { 448 | "colab": { 449 | "base_uri": "https://localhost:8080/" 450 | }, 451 | "id": "P_69Sf_ois7E", 452 | "outputId": "741d89eb-5d99-4174-99d9-3a9fbcb760f9" 453 | }, 454 | "execution_count": 19, 455 | "outputs": [ 456 | { 457 | "output_type": "stream", 458 | "name": "stdout", 459 | "text": [ 460 | "[[5, 2], [5, 2], [5, 2]]\n" 461 | ] 462 | } 463 | ] 464 | }, 465 | { 466 | "cell_type": "code", 467 | "source": [ 468 | "nested_list = [[1, 2] for _ in range(3)]\n", 469 | "nested_list[0][0] = 5\n", 470 | "print(nested_list)" 471 | ], 472 | "metadata": { 473 | "colab": { 474 | "base_uri": "https://localhost:8080/" 475 | }, 476 | "id": "tPOyPvwfivCc", 477 | "outputId": "bfe03323-b992-4a7e-bdb4-d03e76d3d2a8" 478 | }, 479 | "execution_count": 20, 480 | "outputs": [ 481 | { 482 | "output_type": "stream", 483 | "name": "stdout", 484 | "text": [ 485 | "[[5, 2], [1, 2], [1, 2]]\n" 486 | ] 487 | } 488 | ] 489 | } 490 | ] 491 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /data-cleaning/README.md: -------------------------------------------------------------------------------- 1 | ✨ Some things interesting on data cleaning w/ Python 2 | -------------------------------------------------------------------------------- /data-cleaning/data_cleaning_one_liners.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": "markdown", 19 | "source": [ 20 | "## Sample Data" 21 | ], 22 | "metadata": { 23 | "id": "I32ZuXl9HGpi" 24 | } 25 | }, 26 | { 27 | "cell_type": "code", 28 | "source": [ 29 | "data = [\n", 30 | "\t{\"name\": \"alice smith\", \"age\": 30, \"email\": \"alice@example.com\", \"salary\": 50000.00, \"join_date\": \"2022-03-15\"},\n", 31 | "\t{\"name\": \"bob gray\", \"age\": 17, \"email\": \"bob@not-an-email\", \"salary\": 60000.00, \"join_date\": \"invalid-date\"},\n", 32 | "\t{\"name\": \"charlie brown\", \"age\": None, \"email\": \"charlie@example.com\", \"salary\": -1500.00, \"join_date\": \"2022-09-21\"},\n", 33 | "\t{\"name\": \"dave davis\", \"age\": 45, \"email\": \"dave@example.com\", \"salary\": 70000.00, \"join_date\": \"2021-07-01\"},\n", 34 | "\t{\"name\": \"eve green\", \"age\": 25, \"email\": \"eve@example.com\", \"salary\": None, \"join_date\": \"2023-12-31\"},\n", 35 | "]" 36 | ], 37 | "metadata": { 38 | "id": "g8DAlQ7a3VaX" 39 | }, 40 | "execution_count": 1, 41 | "outputs": [] 42 | }, 43 | { 44 | "cell_type": "markdown", 45 | "source": [ 46 | "## Data Cleaning One-Liners" 47 | ], 48 | "metadata": { 49 | "id": "l_GtyrFMHLPJ" 50 | } 51 | }, 52 | { 53 | "cell_type": "code", 54 | "source": [ 55 | "# Capitalizing the names for consistency\n", 56 | "data = [{**d, \"name\": d[\"name\"].title()} for d in data]" 57 | ], 58 | "metadata": { 59 | "id": "UbChkQyk6bBf" 60 | }, 61 | "execution_count": 2, 62 | "outputs": [] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "source": [ 67 | "# Converting age to an integer type, defaulting to 25 if conversion fails\n", 68 | "data = [{**d, \"age\": int(d[\"age\"]) if isinstance(d[\"age\"], (int, float)) else 25} for d in data]" 69 | ], 70 | "metadata": { 71 | "id": "EiTeBxJ37Kn-" 72 | }, 73 | "execution_count": 3, 74 | "outputs": [] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "source": [ 79 | "# Ensuring age is an integer within the range of 18 to 60; otherwise, set to 25\n", 80 | "data = [{**d, \"age\": d[\"age\"] if isinstance(d[\"age\"], int) and 18 <= d[\"age\"] <= 60 else 25} for d in data]" 81 | ], 82 | "metadata": { 83 | "id": "Bekzwl3k6bdF" 84 | }, 85 | "execution_count": 4, 86 | "outputs": [] 87 | }, 88 | { 89 | "cell_type": "code", 90 | "source": [ 91 | "data = [{**d, \"email\": d[\"email\"] if \"@\" in d[\"email\"] and \".\" in d[\"email\"] else \"invalid@example.com\"} for d in data]" 92 | ], 93 | "metadata": { 94 | "id": "OSg1V-e26dQP" 95 | }, 96 | "execution_count": 5, 97 | "outputs": [] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "source": [ 102 | "data = [{**d, \"salary\": d[\"salary\"] if d[\"salary\"] is not None else 30000.00} for d in data]" 103 | ], 104 | "metadata": { 105 | "id": "0EddJ3hX6fZ-" 106 | }, 107 | "execution_count": 6, 108 | "outputs": [] 109 | }, 110 | { 111 | "cell_type": "code", 112 | "source": [ 113 | "from datetime import datetime\n", 114 | "data = [{**d, \"join_date\": (lambda x: (datetime.strptime(x, '%Y-%m-%d').date() if '-' in x and len(x) == 10 else datetime.strptime(x, '%d-%m-%Y').date()) if x and 'invalid-date' not in x else '2023-01-01')(d['join_date'])} for d in data]\n" 115 | ], 116 | "metadata": { 117 | "id": "peV6gzs06hpK" 118 | }, 119 | "execution_count": 7, 120 | "outputs": [] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "source": [ 125 | "# Replacing negative salary values with zero to ensure all values are non-negative.\n", 126 | "data = [{**d, \"salary\": max(d[\"salary\"], 0)} for d in data]" 127 | ], 128 | "metadata": { 129 | "id": "pvCwX5Dv6jhs" 130 | }, 131 | "execution_count": 8, 132 | "outputs": [] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "source": [ 137 | "# Keeping only unique entries based on the name field\n", 138 | "data = {tuple(d.items()) for d in data} # Using a set to remove duplicates\n", 139 | "data = [dict(t) for t in data] # Converting back to list of dictionaries" 140 | ], 141 | "metadata": { 142 | "id": "3sITtMbD6nwm" 143 | }, 144 | "execution_count": 9, 145 | "outputs": [] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "source": [ 150 | "# Normalizing salary values to a percentage of the maximum salary\n", 151 | "max_salary = max(d[\"salary\"] for d in data)\n", 152 | "data = [{**d, \"salary\": (d[\"salary\"] / max_salary * 100) if max_salary > 0 else 0} for d in data]" 153 | ], 154 | "metadata": { 155 | "id": "O35E6Inm6pfV" 156 | }, 157 | "execution_count": 10, 158 | "outputs": [] 159 | }, 160 | { 161 | "cell_type": "code", 162 | "source": [ 163 | "# Trimming whitespace from names for cleaner data\n", 164 | "data = [{**d, \"name\": d[\"name\"].strip()} for d in data]" 165 | ], 166 | "metadata": { 167 | "id": "ZDLsoMou6rDO" 168 | }, 169 | "execution_count": 11, 170 | "outputs": [] 171 | }, 172 | { 173 | "cell_type": "markdown", 174 | "source": [ 175 | "## Data After Cleaning Steps" 176 | ], 177 | "metadata": { 178 | "id": "nHieFDdwHOhO" 179 | } 180 | }, 181 | { 182 | "cell_type": "code", 183 | "source": [ 184 | "data" 185 | ], 186 | "metadata": { 187 | "colab": { 188 | "base_uri": "https://localhost:8080/" 189 | }, 190 | "id": "Wjd2ogiO7U4-", 191 | "outputId": "39508a79-ddff-484e-ade6-f8d062f9728d" 192 | }, 193 | "execution_count": 12, 194 | "outputs": [ 195 | { 196 | "output_type": "execute_result", 197 | "data": { 198 | "text/plain": [ 199 | "[{'name': 'Bob Gray',\n", 200 | " 'age': 25,\n", 201 | " 'email': 'invalid@example.com',\n", 202 | " 'salary': 85.71428571428571,\n", 203 | " 'join_date': '2023-01-01'},\n", 204 | " {'name': 'Alice Smith',\n", 205 | " 'age': 30,\n", 206 | " 'email': 'alice@example.com',\n", 207 | " 'salary': 71.42857142857143,\n", 208 | " 'join_date': datetime.date(2022, 3, 15)},\n", 209 | " {'name': 'Charlie Brown',\n", 210 | " 'age': 25,\n", 211 | " 'email': 'charlie@example.com',\n", 212 | " 'salary': 0.0,\n", 213 | " 'join_date': datetime.date(2022, 9, 21)},\n", 214 | " {'name': 'Dave Davis',\n", 215 | " 'age': 45,\n", 216 | " 'email': 'dave@example.com',\n", 217 | " 'salary': 100.0,\n", 218 | " 'join_date': datetime.date(2021, 7, 1)},\n", 219 | " {'name': 'Eve Green',\n", 220 | " 'age': 25,\n", 221 | " 'email': 'eve@example.com',\n", 222 | " 'salary': 42.857142857142854,\n", 223 | " 'join_date': datetime.date(2023, 12, 31)}]" 224 | ] 225 | }, 226 | "metadata": {}, 227 | "execution_count": 12 228 | } 229 | ] 230 | } 231 | ] 232 | } -------------------------------------------------------------------------------- /data-structures/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /data-structures/python_data_structures.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": "f21v6Q76ODfG", 25 | "outputId": "e560528b-8b62-47f6-8f08-451d0635e772" 26 | }, 27 | "outputs": [ 28 | { 29 | "output_type": "stream", 30 | "name": "stdout", 31 | "text": [ 32 | "Tasks left: ['write report', 'check calendar', 'attend meeting', 'review pull request']\n", 33 | "Completed: send email\n" 34 | ] 35 | } 36 | ], 37 | "source": [ 38 | "tasks = [\"write report\", \"send email\", \"attend meeting\"]\n", 39 | "tasks.append(\"review pull request\") # Add a task at the end\n", 40 | "tasks.insert(1, \"check calendar\") # Insert task at position 1\n", 41 | "completed_task = tasks.pop(2) # Remove and return the item at index 2\n", 42 | "\n", 43 | "print(\"Tasks left:\", tasks)\n", 44 | "print(\"Completed:\", completed_task)\n" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "source": [ 50 | "coordinates = (37.7749, -122.4194) # Latitude and longitude of San Francisco\n", 51 | "print(f\"Latitude: {coordinates[0]}, Longitude: {coordinates[1]}\")\n" 52 | ], 53 | "metadata": { 54 | "colab": { 55 | "base_uri": "https://localhost:8080/" 56 | }, 57 | "id": "lbr64V2ROHRP", 58 | "outputId": "73f22a4e-ece1-49e7-9956-434e56b2a9eb" 59 | }, 60 | "execution_count": null, 61 | "outputs": [ 62 | { 63 | "output_type": "stream", 64 | "name": "stdout", 65 | "text": [ 66 | "Latitude: 37.7749, Longitude: -122.4194\n" 67 | ] 68 | } 69 | ] 70 | }, 71 | { 72 | "cell_type": "code", 73 | "source": [ 74 | "def min_max(numbers):\n", 75 | " return (min(numbers), max(numbers))\n", 76 | "\n", 77 | "print(min_max([3, 7, 1, 9]))\n" 78 | ], 79 | "metadata": { 80 | "colab": { 81 | "base_uri": "https://localhost:8080/" 82 | }, 83 | "id": "LhZxBmv7OQvV", 84 | "outputId": "0446cba4-09f9-4e62-c4e3-c370007ced21" 85 | }, 86 | "execution_count": 15, 87 | "outputs": [ 88 | { 89 | "output_type": "stream", 90 | "name": "stdout", 91 | "text": [ 92 | "(1, 9)\n" 93 | ] 94 | } 95 | ] 96 | }, 97 | { 98 | "cell_type": "code", 99 | "source": [ 100 | "\n", 101 | "user = {\n", 102 | " \"name\": \"Alice\",\n", 103 | " \"email\": \"alice@example.com\",\n", 104 | " \"is_active\": True\n", 105 | "}\n", 106 | "\n", 107 | "user[\"is_active\"] = False # Update a value\n", 108 | "print(f\"User {user['name']} is active: {user['is_active']}\")\n" 109 | ], 110 | "metadata": { 111 | "colab": { 112 | "base_uri": "https://localhost:8080/" 113 | }, 114 | "id": "sqPqVdKyOU2t", 115 | "outputId": "d69dc55e-4f19-4173-e5b3-ad875b0fe3cf" 116 | }, 117 | "execution_count": null, 118 | "outputs": [ 119 | { 120 | "output_type": "stream", 121 | "name": "stdout", 122 | "text": [ 123 | "User Alice is active: False\n" 124 | ] 125 | } 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "source": [ 131 | "def word_count(text):\n", 132 | " counts = {}\n", 133 | " for word in text.lower().split():\n", 134 | " counts[word] = counts.get(word, 0) + 1\n", 135 | " return counts\n", 136 | "\n", 137 | "print(word_count(\"Python is powerful and Python is fast\"))\n" 138 | ], 139 | "metadata": { 140 | "colab": { 141 | "base_uri": "https://localhost:8080/" 142 | }, 143 | "id": "vVc2POxDOa6w", 144 | "outputId": "48e42f99-9121-4d7a-fd1b-0ef4c8f13397" 145 | }, 146 | "execution_count": 16, 147 | "outputs": [ 148 | { 149 | "output_type": "stream", 150 | "name": "stdout", 151 | "text": [ 152 | "{'python': 2, 'is': 2, 'powerful': 1, 'and': 1, 'fast': 1}\n" 153 | ] 154 | } 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "source": [ 160 | "python_devs = {\"Alice\", \"Bob\", \"Charlie\"}\n", 161 | "javascript_devs = {\"Alice\", \"Eve\", \"Dave\"}\n", 162 | "\n", 163 | "both = python_devs & javascript_devs # Intersection\n", 164 | "either = python_devs | javascript_devs # Union\n", 165 | "only_python = python_devs - javascript_devs # Difference\n", 166 | "\n", 167 | "print(\"Knows both:\", both)\n", 168 | "print(\"Knows either:\", either)\n", 169 | "print(\"Knows only Python:\", only_python)\n" 170 | ], 171 | "metadata": { 172 | "colab": { 173 | "base_uri": "https://localhost:8080/" 174 | }, 175 | "id": "fKueJgTGOjTU", 176 | "outputId": "9f226138-828a-4e14-a7bc-802776a2af60" 177 | }, 178 | "execution_count": 17, 179 | "outputs": [ 180 | { 181 | "output_type": "stream", 182 | "name": "stdout", 183 | "text": [ 184 | "Knows both: {'Alice'}\n", 185 | "Knows either: {'Bob', 'Charlie', 'Eve', 'Dave', 'Alice'}\n", 186 | "Knows only Python: {'Bob', 'Charlie'}\n" 187 | ] 188 | } 189 | ] 190 | }, 191 | { 192 | "cell_type": "code", 193 | "source": [ 194 | "emails = [\"a@example.com\", \"b@example.com\", \"a@example.com\"]\n", 195 | "unique_emails = set(emails)\n", 196 | "print(unique_emails)\n" 197 | ], 198 | "metadata": { 199 | "colab": { 200 | "base_uri": "https://localhost:8080/" 201 | }, 202 | "id": "iwH_C3m-Ops9", 203 | "outputId": "09327eb2-37dd-4695-d273-b084853cbcc4" 204 | }, 205 | "execution_count": 18, 206 | "outputs": [ 207 | { 208 | "output_type": "stream", 209 | "name": "stdout", 210 | "text": [ 211 | "{'b@example.com', 'a@example.com'}\n" 212 | ] 213 | } 214 | ] 215 | }, 216 | { 217 | "cell_type": "code", 218 | "source": [ 219 | "from collections import deque\n", 220 | "\n", 221 | "# Initialize a deque\n", 222 | "tasks = deque([\"email client\", \"compile report\", \"team meeting\"])\n", 223 | "\n", 224 | "# Add a new urgent task to the beginning\n", 225 | "tasks.appendleft(\"fix production issue\")\n", 226 | "\n", 227 | "# Add a low-priority task to the end\n", 228 | "tasks.append(\"update documentation\")\n", 229 | "\n", 230 | "# Process tasks\n", 231 | "next_task = tasks.popleft() # Handles \"fix production issue\"\n", 232 | "later_task = tasks.pop() # Handles \"update documentation\"\n", 233 | "\n", 234 | "print(tasks)\n" 235 | ], 236 | "metadata": { 237 | "colab": { 238 | "base_uri": "https://localhost:8080/" 239 | }, 240 | "id": "Y-uwsuqLOzRw", 241 | "outputId": "3361c4ac-777a-4c1d-8529-530286822351" 242 | }, 243 | "execution_count": null, 244 | "outputs": [ 245 | { 246 | "output_type": "stream", 247 | "name": "stdout", 248 | "text": [ 249 | "deque(['email client', 'compile report', 'team meeting'])\n" 250 | ] 251 | } 252 | ] 253 | }, 254 | { 255 | "cell_type": "code", 256 | "source": [ 257 | "from collections import defaultdict\n", 258 | "# Group employees by department\n", 259 | "employees = [\n", 260 | " (\"HR\", \"Alice\"),\n", 261 | " (\"Engineering\", \"Bob\"),\n", 262 | " (\"HR\", \"Carol\"),\n", 263 | " (\"Engineering\", \"Dave\"),\n", 264 | " (\"Sales\", \"Eve\")\n", 265 | "]\n", 266 | "departments = defaultdict(list)\n", 267 | "for dept, name in employees:\n", 268 | " departments[dept].append(name)\n", 269 | "print(departments)\n" 270 | ], 271 | "metadata": { 272 | "colab": { 273 | "base_uri": "https://localhost:8080/" 274 | }, 275 | "id": "jUV3pX1cPCCq", 276 | "outputId": "649060ba-31f3-443a-aae2-ce041eb471d9" 277 | }, 278 | "execution_count": null, 279 | "outputs": [ 280 | { 281 | "output_type": "stream", 282 | "name": "stdout", 283 | "text": [ 284 | "defaultdict(, {'HR': ['Alice', 'Carol'], 'Engineering': ['Bob', 'Dave'], 'Sales': ['Eve']})\n" 285 | ] 286 | } 287 | ] 288 | }, 289 | { 290 | "cell_type": "code", 291 | "source": [ 292 | "from collections import Counter\n", 293 | "# Analyze page visits\n", 294 | "page_visits = [\n", 295 | " \"/home\", \"/products\", \"/about\", \"/products\", \"/home\", \"/contact\"\n", 296 | "]\n", 297 | "\n", 298 | "visit_counter = Counter(page_visits)\n", 299 | "# Most visited pages\n", 300 | "print(visit_counter.most_common(2))\n", 301 | "# Adding more page views\n", 302 | "visit_counter.update([\"/home\", \"/blog\"])\n", 303 | "print(visit_counter)\n" 304 | ], 305 | "metadata": { 306 | "colab": { 307 | "base_uri": "https://localhost:8080/" 308 | }, 309 | "id": "5vm-xn4GPJGz", 310 | "outputId": "019a8fa0-4c84-4fcd-850a-0b3e9e2962dd" 311 | }, 312 | "execution_count": null, 313 | "outputs": [ 314 | { 315 | "output_type": "stream", 316 | "name": "stdout", 317 | "text": [ 318 | "[('/home', 2), ('/products', 2)]\n", 319 | "Counter({'/home': 3, '/products': 2, '/about': 1, '/contact': 1, '/blog': 1})\n" 320 | ] 321 | } 322 | ] 323 | }, 324 | { 325 | "cell_type": "code", 326 | "source": [ 327 | "import heapq\n", 328 | "# Manage tasks by priority (lower number = higher priority)\n", 329 | "tasks = [(3, \"write report\"), (1, \"fix critical bug\"), (4, \"team meeting\")]\n", 330 | "\n", 331 | "# Convert the list into a heap\n", 332 | "heapq.heapify(tasks)\n", 333 | "\n", 334 | "# Add a new task\n", 335 | "heapq.heappush(tasks, (2, \"code review\"))\n", 336 | "\n", 337 | "# Process tasks by priority\n", 338 | "while tasks:\n", 339 | " priority, task = heapq.heappop(tasks)\n", 340 | " print(f\"Processing {task} with priority {priority}\")\n" 341 | ], 342 | "metadata": { 343 | "colab": { 344 | "base_uri": "https://localhost:8080/" 345 | }, 346 | "id": "6xnPGZ-NPPtp", 347 | "outputId": "a7b9ae1a-e0cf-4b8b-a0bc-40240c5e597c" 348 | }, 349 | "execution_count": null, 350 | "outputs": [ 351 | { 352 | "output_type": "stream", 353 | "name": "stdout", 354 | "text": [ 355 | "Processing fix critical bug with priority 1\n", 356 | "Processing code review with priority 2\n", 357 | "Processing write report with priority 3\n", 358 | "Processing team meeting with priority 4\n" 359 | ] 360 | } 361 | ] 362 | }, 363 | { 364 | "cell_type": "code", 365 | "source": [], 366 | "metadata": { 367 | "id": "NWuvBkGiPVrv" 368 | }, 369 | "execution_count": null, 370 | "outputs": [] 371 | } 372 | ] 373 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /efficient-python-for-beginners/main.py: -------------------------------------------------------------------------------- 1 | # instead of this 2 | def process_sales_data(sales): 3 | highest_sale = sales[0] 4 | for sale in sales: 5 | if sale > highest_sale: 6 | highest_sale = sale 7 | 8 | total_sales = 0 9 | for sale in sales: 10 | total_sales += sale 11 | 12 | return highest_sale, total_sales, total_sales / len(sales) 13 | 14 | # do this 15 | def process_sales_data(sales): 16 | return max(sales), sum(sales), sum(sales) / len(sales) 17 | 18 | # Instead of this 19 | def get_premium_customer_emails(customers): 20 | premium_emails = [] 21 | for customer in customers: 22 | if customer['membership_level'] == 'premium' and customer['active']: 23 | email = customer['email'].lower().strip() 24 | premium_emails.append(email) 25 | return premium_emails 26 | 27 | # Do this 28 | def get_premium_customer_emails(customers): 29 | return [ 30 | customer['email'].lower().strip() 31 | for customer in customers 32 | if customer['membership_level'] == 'premium' and customer['active'] 33 | ] 34 | 35 | # Instead of this 36 | def has_permission(user_id, permitted_users): 37 | # permitted_users is a list of user IDs 38 | for p_user in permitted_users: 39 | if p_user == user_id: 40 | return True 41 | return False 42 | 43 | # Usage: 44 | permitted_users = [1001, 1023, 1052, 1076, 1088, 1095, 1102, 1109] 45 | print(has_permission(1088, permitted_users)) # True 46 | 47 | # Do this 48 | def has_permission(user_id, permitted_users): 49 | # permitted_users is now a set of user IDs 50 | return user_id in permitted_users 51 | 52 | # Usage: 53 | permitted_users = {1001, 1023, 1052, 1076, 1088, 1095, 1102, 1109} 54 | print(has_permission(1088, permitted_users)) # True 55 | 56 | # Instead of this 57 | def find_errors(log_file): 58 | with open(log_file, 'r') as file: 59 | lines = file.readlines() 60 | 61 | error_messages = [] 62 | for line in lines: 63 | if '[ERROR]' in line: 64 | timestamp = line.split('[ERROR]')[0].strip() 65 | message = line.split('[ERROR]')[1].strip() 66 | error_messages.append((timestamp, message)) 67 | 68 | return error_messages 69 | 70 | # Do this 71 | def find_errors(log_file): 72 | with open(log_file, 'r') as file: 73 | for line in file: 74 | if '[ERROR]' in line: 75 | timestamp = line.split('[ERROR]')[0].strip() 76 | message = line.split('[ERROR]')[1].strip() 77 | yield (timestamp, message) 78 | 79 | # Usage: 80 | for timestamp, message in find_errors('application.log'): 81 | print(f"Error at {timestamp}: {message}") 82 | 83 | # Instead of this 84 | import re 85 | from datetime import datetime 86 | 87 | def find_recent_errors(logs): 88 | recent_errors = [] 89 | 90 | for log in logs: 91 | # This regex compilation happens on every iteration 92 | timestamp_pattern = re.compile(r'\[(.*?)\]') 93 | timestamp_match = timestamp_pattern.search(log) 94 | 95 | if timestamp_match and '[ERROR]' in log: 96 | # The datetime parsing happens on every iteration 97 | log_time = datetime.strptime(timestamp_match.group(1), '%Y-%m-%d %H:%M:%S') 98 | current_time = datetime.now() 99 | 100 | # Check if the log is from the last 24 hours 101 | time_diff = (current_time - log_time).total_seconds() / 3600 102 | if time_diff <= 24: 103 | recent_errors.append(log) 104 | 105 | return recent_errors 106 | 107 | # do this 108 | import re 109 | from datetime import datetime 110 | 111 | def find_recent_errors(logs): 112 | recent_errors = [] 113 | 114 | # Compile the regex once 115 | timestamp_pattern = re.compile(r'\[(.*?)\]') 116 | # Get the current time once 117 | current_time = datetime.now() 118 | 119 | for log in logs: 120 | timestamp_match = timestamp_pattern.search(log) 121 | 122 | if timestamp_match and '[ERROR]' in log: 123 | log_time = datetime.strptime(timestamp_match.group(1), '%Y-%m-%d %H:%M:%S') 124 | 125 | # Check if the log is recent (last 24 hours) 126 | time_diff = (current_time - log_time).total_seconds() / 3600 127 | if time_diff <= 24: 128 | recent_errors.append(log) 129 | 130 | return recent_errors 131 | 132 | # instead of this 133 | def generate_html_report(data_points): 134 | html = "

Data Report

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

Data Report

") 151 | return "".join(parts) 152 | 153 | 154 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /error-handling/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /go-intro/goroutine_example.go: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /go-intro/loops_examples.go: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /name-main-python/example-1/__pycache__/module1.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balapriyac/python-basics/7525b7800c11538814a44886cfc7dc8f2602c61e/name-main-python/example-1/__pycache__/module1.cpython-38.pyc -------------------------------------------------------------------------------- /name-main-python/example-1/module1.py: -------------------------------------------------------------------------------- 1 | print("This is module1.") 2 | print(f"The __name__ variable of module 1 is: {__name__}.") -------------------------------------------------------------------------------- /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__/add.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balapriyac/python-basics/7525b7800c11538814a44886cfc7dc8f2602c61e/name-main-python/example-2/__pycache__/add.cpython-38.pyc -------------------------------------------------------------------------------- /name-main-python/example-2/__pycache__/test_add.cpython-38.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/balapriyac/python-basics/7525b7800c11538814a44886cfc7dc8f2602c61e/name-main-python/example-2/__pycache__/test_add.cpython-38.pyc -------------------------------------------------------------------------------- /name-main-python/example-2/add.py: -------------------------------------------------------------------------------- 1 | def add_ab(a,b): 2 | return a + b -------------------------------------------------------------------------------- /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() -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pytest/tests/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /pytest/todo/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /python-magic-methods/main.py: -------------------------------------------------------------------------------- 1 | p 2 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /string-manipulation/README.md: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /string-manipulation/string_manipulation_one_liners.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": "markdown", 19 | "source": [ 20 | "## Some String Manipulation One-Liners" 21 | ], 22 | "metadata": { 23 | "id": "AOEBN5oM-Zr7" 24 | } 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": 47, 29 | "metadata": { 30 | "colab": { 31 | "base_uri": "https://localhost:8080/" 32 | }, 33 | "id": "Bh5qtzyR97v6", 34 | "outputId": "83b10efe-0606-4856-fe5b-f1b311f26af8" 35 | }, 36 | "outputs": [ 37 | { 38 | "output_type": "stream", 39 | "name": "stdout", 40 | "text": [ 41 | "['HELLO', 'WORLD', 'PYTHON', 'ROCKS']\n" 42 | ] 43 | } 44 | ], 45 | "source": [ 46 | "strings = [\"hello\", \"world\", \"python\", \"rocks\"]\n", 47 | "uppercase_strings = [s.upper() for s in strings]\n", 48 | "print(uppercase_strings)" 49 | ] 50 | }, 51 | { 52 | "cell_type": "code", 53 | "source": [ 54 | "fruits = [\"apple\", \"banana\", \"cherry\", \"apricot\", \"blueberry\"]\n", 55 | "filtered = [s for s in fruits if \"ap\" in s]\n", 56 | "print(filtered)" 57 | ], 58 | "metadata": { 59 | "colab": { 60 | "base_uri": "https://localhost:8080/" 61 | }, 62 | "id": "hmzEV-q6-cL6", 63 | "outputId": "e6699f9a-e4ad-492e-d93b-609b49ed24fc" 64 | }, 65 | "execution_count": 48, 66 | "outputs": [ 67 | { 68 | "output_type": "stream", 69 | "name": "stdout", 70 | "text": [ 71 | "['apple', 'apricot']\n" 72 | ] 73 | } 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "source": [ 79 | "strings = [\" rust \", \" python \"]\n", 80 | "trimmed_strings = [s.strip() for s in strings]\n", 81 | "print(trimmed_strings)" 82 | ], 83 | "metadata": { 84 | "colab": { 85 | "base_uri": "https://localhost:8080/" 86 | }, 87 | "id": "16ycMEXI-hyA", 88 | "outputId": "61397243-dfc0-4f62-b6aa-40d86f654495" 89 | }, 90 | "execution_count": 49, 91 | "outputs": [ 92 | { 93 | "output_type": "stream", 94 | "name": "stdout", 95 | "text": [ 96 | "['rust', 'python']\n" 97 | ] 98 | } 99 | ] 100 | }, 101 | { 102 | "cell_type": "code", 103 | "source": [ 104 | "to_do = [\"code\", \"debug\", \"refactor\"]\n", 105 | "reversed_strings = [task[::-1] for task in to_do]\n", 106 | "print(reversed_strings)" 107 | ], 108 | "metadata": { 109 | "colab": { 110 | "base_uri": "https://localhost:8080/" 111 | }, 112 | "id": "r_aGvLdK-jsi", 113 | "outputId": "566d80f9-d27f-43ce-99f2-bd21ef20efe1" 114 | }, 115 | "execution_count": 50, 116 | "outputs": [ 117 | { 118 | "output_type": "stream", 119 | "name": "stdout", 120 | "text": [ 121 | "['edoc', 'gubed', 'rotcafer']\n" 122 | ] 123 | } 124 | ] 125 | }, 126 | { 127 | "cell_type": "code", 128 | "source": [ 129 | "strings = [\"code\", \"debug\", \"test\"]\n", 130 | "prefixed_strings = [f\"py-{s}\" for s in strings]\n", 131 | "print(prefixed_strings)" 132 | ], 133 | "metadata": { 134 | "colab": { 135 | "base_uri": "https://localhost:8080/" 136 | }, 137 | "id": "NcAPKv2S-4D_", 138 | "outputId": "f782f6fc-bbf1-4933-927f-a406433b13e0" 139 | }, 140 | "execution_count": 51, 141 | "outputs": [ 142 | { 143 | "output_type": "stream", 144 | "name": "stdout", 145 | "text": [ 146 | "['py-code', 'py-debug', 'py-test']\n" 147 | ] 148 | } 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "source": [ 154 | "strings = [\"learn python\", \"python is fun\"]\n", 155 | "split_strings = [s.split() for s in strings]\n", 156 | "print(split_strings)" 157 | ], 158 | "metadata": { 159 | "colab": { 160 | "base_uri": "https://localhost:8080/" 161 | }, 162 | "id": "mx2fSMOS-53n", 163 | "outputId": "3e38f4a7-b081-4f5c-9108-327b270812d3" 164 | }, 165 | "execution_count": 52, 166 | "outputs": [ 167 | { 168 | "output_type": "stream", 169 | "name": "stdout", 170 | "text": [ 171 | "[['learn', 'python'], ['python', 'is', 'fun']]\n" 172 | ] 173 | } 174 | ] 175 | }, 176 | { 177 | "cell_type": "code", 178 | "source": [ 179 | "strings = [\"C is cool\", \"I code in C\", \"I like C\"]\n", 180 | "replaced_strings = [s.replace(\"C\", \"Python\") for s in strings]\n", 181 | "print(replaced_strings)" 182 | ], 183 | "metadata": { 184 | "colab": { 185 | "base_uri": "https://localhost:8080/" 186 | }, 187 | "id": "dAl_Umz3-7eM", 188 | "outputId": "1ef1bfa0-dd22-4f13-8c6e-dc0a9b8db424" 189 | }, 190 | "execution_count": 53, 191 | "outputs": [ 192 | { 193 | "output_type": "stream", 194 | "name": "stdout", 195 | "text": [ 196 | "['Python is cool', 'I code in Python', 'I like Python']\n" 197 | ] 198 | } 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "source": [ 204 | "strings = [\"apple\", \"banana\", \"cherry\"]\n", 205 | "char_counts = [s.count(\"a\") for s in strings]\n", 206 | "print(char_counts)" 207 | ], 208 | "metadata": { 209 | "colab": { 210 | "base_uri": "https://localhost:8080/" 211 | }, 212 | "id": "nTJAGazq-9Mg", 213 | "outputId": "49b3afb9-c050-4a39-a685-f8c6417c1611" 214 | }, 215 | "execution_count": 54, 216 | "outputs": [ 217 | { 218 | "output_type": "stream", 219 | "name": "stdout", 220 | "text": [ 221 | "[1, 3, 0]\n" 222 | ] 223 | } 224 | ] 225 | }, 226 | { 227 | "cell_type": "code", 228 | "source": [ 229 | "strings = [\"Python\", \"is\", \"great\"]\n", 230 | "sentence = \" \".join(strings)\n", 231 | "print(sentence)" 232 | ], 233 | "metadata": { 234 | "colab": { 235 | "base_uri": "https://localhost:8080/" 236 | }, 237 | "id": "HqhaI68P_FLt", 238 | "outputId": "b6d084d8-9c75-45d9-ad02-288f50f207e4" 239 | }, 240 | "execution_count": 55, 241 | "outputs": [ 242 | { 243 | "output_type": "stream", 244 | "name": "stdout", 245 | "text": [ 246 | "Python is great\n" 247 | ] 248 | } 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "source": [ 254 | "strings = [\"python\", \"rocks\"]\n", 255 | "capitalized_strings = [s.capitalize() for s in strings]\n", 256 | "print(capitalized_strings)" 257 | ], 258 | "metadata": { 259 | "colab": { 260 | "base_uri": "https://localhost:8080/" 261 | }, 262 | "id": "NqRhH8ot_HRh", 263 | "outputId": "b76cf8fa-d3ea-4761-d0b2-87eb8e319642" 264 | }, 265 | "execution_count": 56, 266 | "outputs": [ 267 | { 268 | "output_type": "stream", 269 | "name": "stdout", 270 | "text": [ 271 | "['Python', 'Rocks']\n" 272 | ] 273 | } 274 | ] 275 | }, 276 | { 277 | "cell_type": "code", 278 | "source": [ 279 | "strings = [\"elephant\", \"cat\", \"dinosaur\", \"ant\"]\n", 280 | "lengths = [len(s) for s in strings]\n", 281 | "print(lengths)" 282 | ], 283 | "metadata": { 284 | "colab": { 285 | "base_uri": "https://localhost:8080/" 286 | }, 287 | "id": "Cb7x4nIp_KMT", 288 | "outputId": "7610bfc8-02e3-4e97-f7c9-57d1660b70f7" 289 | }, 290 | "execution_count": 57, 291 | "outputs": [ 292 | { 293 | "output_type": "stream", 294 | "name": "stdout", 295 | "text": [ 296 | "[8, 3, 8, 3]\n" 297 | ] 298 | } 299 | ] 300 | }, 301 | { 302 | "cell_type": "code", 303 | "source": [ 304 | "strings = [\"hello123\", \"world!\", \"python3.12\", \"rocks\"]\n", 305 | "is_alphanumeric = [s.isalnum() for s in strings]\n", 306 | "print(is_alphanumeric)" 307 | ], 308 | "metadata": { 309 | "colab": { 310 | "base_uri": "https://localhost:8080/" 311 | }, 312 | "id": "KcpZBqTU_NfD", 313 | "outputId": "c1f5c9ed-7738-4213-8635-b3dbef77efd6" 314 | }, 315 | "execution_count": 58, 316 | "outputs": [ 317 | { 318 | "output_type": "stream", 319 | "name": "stdout", 320 | "text": [ 321 | "[True, False, False, True]\n" 322 | ] 323 | } 324 | ] 325 | }, 326 | { 327 | "cell_type": "code", 328 | "source": [ 329 | "files = [\"main\", \"test\", \"app\"]\n", 330 | "suffixed_files = [file + \".py\" for file in files]\n", 331 | "print(suffixed_files)" 332 | ], 333 | "metadata": { 334 | "colab": { 335 | "base_uri": "https://localhost:8080/" 336 | }, 337 | "id": "ZI49TDmy_PzV", 338 | "outputId": "fa2a816b-0ec8-44af-bcb1-c5b1aebcd72d" 339 | }, 340 | "execution_count": 59, 341 | "outputs": [ 342 | { 343 | "output_type": "stream", 344 | "name": "stdout", 345 | "text": [ 346 | "['main.py', 'test.py', 'app.py']\n" 347 | ] 348 | } 349 | ] 350 | }, 351 | { 352 | "cell_type": "code", 353 | "source": [ 354 | "strings = [\"banana\", \"cherry\", \"date\", \"blueberry\"]\n", 355 | "first_letters = [s[0] for s in strings]\n", 356 | "print(first_letters)" 357 | ], 358 | "metadata": { 359 | "colab": { 360 | "base_uri": "https://localhost:8080/" 361 | }, 362 | "id": "r5_gc0NB_Rcr", 363 | "outputId": "335a163e-2da6-4947-c1d8-a36505fa1bbe" 364 | }, 365 | "execution_count": 60, 366 | "outputs": [ 367 | { 368 | "output_type": "stream", 369 | "name": "stdout", 370 | "text": [ 371 | "['b', 'c', 'd', 'b']\n" 372 | ] 373 | } 374 | ] 375 | }, 376 | { 377 | "cell_type": "code", 378 | "source": [ 379 | "strings = [\"Apple\", \"banana\", \"Cherry\", \"date\"]\n", 380 | "sorted_strings = sorted(strings, key=lambda s: s.lower())\n", 381 | "print(sorted_strings)" 382 | ], 383 | "metadata": { 384 | "colab": { 385 | "base_uri": "https://localhost:8080/" 386 | }, 387 | "id": "ipVhrIYW_THz", 388 | "outputId": "a639b83b-d471-44ab-fccd-a103e3abac05" 389 | }, 390 | "execution_count": 61, 391 | "outputs": [ 392 | { 393 | "output_type": "stream", 394 | "name": "stdout", 395 | "text": [ 396 | "['Apple', 'banana', 'Cherry', 'date']\n" 397 | ] 398 | } 399 | ] 400 | } 401 | ] 402 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/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/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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /useful-python-functions/useful_python_funcs.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": "3mRSgpziXAsP", 25 | "outputId": "dd718f3b-1c10-4dbd-bb97-9b9d2df14045" 26 | }, 27 | "outputs": [ 28 | { 29 | "output_type": "stream", 30 | "name": "stdout", 31 | "text": [ 32 | "Insert 82 at position: 3\n", 33 | "Grades after insertion: [60, 70, 75, 82, 85, 90, 95]\n", 34 | "Score 82 gets grade: B\n", 35 | "Score 75 gets grade: C\n" 36 | ] 37 | } 38 | ], 39 | "source": [ 40 | "from bisect import bisect_left, bisect_right, insort\n", 41 | "\n", 42 | "# Let's create a grade tracking system\n", 43 | "grades = [60, 70, 75, 85, 90, 95]\n", 44 | "\n", 45 | "# Find where to insert a new grade while keeping the list sorted\n", 46 | "new_grade = 82\n", 47 | "position = bisect_left(grades, new_grade)\n", 48 | "print(f\"Insert 82 at position: {position}\")\n", 49 | "\n", 50 | "# Insert while maintaining sort order\n", 51 | "insort(grades, new_grade)\n", 52 | "print(f\"Grades after insertion: {grades}\")\n", 53 | "\n", 54 | "# Find grade ranges\n", 55 | "def grade_to_letter(score):\n", 56 | "\tbreakpoints = [60, 70, 80, 90] # F, D, C, B, A\n", 57 | "\tgrades = 'FDCBA'\n", 58 | "\tposition = bisect_right(breakpoints, score)\n", 59 | "\treturn grades[position]\n", 60 | "\n", 61 | "print(f\"Score 82 gets grade: {grade_to_letter(82)}\")\n", 62 | "print(f\"Score 75 gets grade: {grade_to_letter(75)}\")\n" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "source": [ 68 | "from itertools import pairwise\n", 69 | "\n", 70 | "# Let's analyze temperature changes\n", 71 | "temperatures = [20, 23, 24, 25, 23, 22, 20]\n", 72 | "\n", 73 | "# Calculate temperature changes between consecutive readings\n", 74 | "changes = []\n", 75 | "for prev, curr in pairwise(temperatures):\n", 76 | "\tchange = curr - prev\n", 77 | "\tchanges.append(change)\n", 78 | "\n", 79 | "print(\"Temperature changes:\", changes)\n", 80 | "\n", 81 | "\n", 82 | "# Calculate moving averages\n", 83 | "moving_averages = []\n", 84 | "for t1, t2 in pairwise(temperatures):\n", 85 | "\tavg = (t1 + t2) / 2\n", 86 | "\tmoving_averages.append(avg)\n", 87 | "\n", 88 | "print(\"Moving averages:\", moving_averages)\n", 89 | "\n", 90 | "# Finding the largest temperature jump\n", 91 | "max_jump = max(abs(b - a) for a, b in pairwise(temperatures))\n", 92 | "print(f\"Largest temperature change: {max_jump} degrees\")\n" 93 | ], 94 | "metadata": { 95 | "colab": { 96 | "base_uri": "https://localhost:8080/" 97 | }, 98 | "id": "DMDrJ59jbtSG", 99 | "outputId": "4e3f0959-a599-4583-ac4a-d80a1582146a" 100 | }, 101 | "execution_count": 1, 102 | "outputs": [ 103 | { 104 | "output_type": "stream", 105 | "name": "stdout", 106 | "text": [ 107 | "Temperature changes: [3, 1, 1, -2, -1, -2]\n", 108 | "Moving averages: [21.5, 23.5, 24.5, 24.0, 22.5, 21.0]\n", 109 | "Largest temperature change: 3 degrees\n" 110 | ] 111 | } 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "source": [ 117 | "from statistics import mean, fmean\n", 118 | "import time\n", 119 | "\n", 120 | "# Let's compare fmean with traditional mean using a real-world example\n", 121 | "# Imagine we're analyzing daily temperature readings\n", 122 | "temperatures = [\n", 123 | "\t21.5, 22.1, 23.4, 22.8, 21.8,\n", 124 | "\t23.2, 22.7, 23.1, 22.6, 21.9\n", 125 | "] * 100000 # Create a large dataset\n", 126 | "\n", 127 | "# Let's compare speed and precision\n", 128 | "start_time = time.perf_counter()\n", 129 | "regular_mean = mean(temperatures)\n", 130 | "regular_time = time.perf_counter() - start_time\n", 131 | "\n", 132 | "start_time = time.perf_counter()\n", 133 | "fast_mean = fmean(temperatures)\n", 134 | "fast_time = time.perf_counter() - start_time\n", 135 | "\n", 136 | "print(f\"Regular mean: {regular_mean:.10f} (took {regular_time:.4f} seconds)\")\n", 137 | "print(f\"fmean: {fast_mean:.10f} (took {fast_time:.4f} seconds)\")\n" 138 | ], 139 | "metadata": { 140 | "colab": { 141 | "base_uri": "https://localhost:8080/" 142 | }, 143 | "id": "_-cprD99bAFI", 144 | "outputId": "baf69e43-eb79-489b-ac49-a62bffdd79c7" 145 | }, 146 | "execution_count": null, 147 | "outputs": [ 148 | { 149 | "output_type": "stream", 150 | "name": "stdout", 151 | "text": [ 152 | "Regular mean: 22.5100000000 (took 0.4748 seconds)\n", 153 | "fmean: 22.5100000000 (took 0.0164 seconds)\n" 154 | ] 155 | } 156 | ] 157 | }, 158 | { 159 | "cell_type": "code", 160 | "source": [ 161 | "from itertools import takewhile\n", 162 | "\n", 163 | "# Processing log entries until an error\n", 164 | "log_entries = [\n", 165 | "\t\"INFO: System started\",\n", 166 | "\t\"INFO: Loading data\",\n", 167 | "\t\"INFO: Processing users\",\n", 168 | "\t\"ERROR: Database connection failed\",\n", 169 | "\t\"INFO: Retrying connection\",\n", 170 | "]\n", 171 | "\n", 172 | "# Get all logs until first error\n", 173 | "normal_operation = list(takewhile(\n", 174 | "\tlambda x: not x.startswith(\"ERROR\"),\n", 175 | "\tlog_entries\n", 176 | "))\n", 177 | "print(\"Logs before first error:\")\n", 178 | "for entry in normal_operation:\n", 179 | "\tprint(entry)\n" 180 | ], 181 | "metadata": { 182 | "colab": { 183 | "base_uri": "https://localhost:8080/" 184 | }, 185 | "id": "TOWLuHrQbNry", 186 | "outputId": "ad885da6-3709-4382-9bd0-c88c7ab19b95" 187 | }, 188 | "execution_count": null, 189 | "outputs": [ 190 | { 191 | "output_type": "stream", 192 | "name": "stdout", 193 | "text": [ 194 | "Logs before first error:\n", 195 | "INFO: System started\n", 196 | "INFO: Loading data\n", 197 | "INFO: Processing users\n" 198 | ] 199 | } 200 | ] 201 | }, 202 | { 203 | "cell_type": "code", 204 | "source": [ 205 | "from operator import attrgetter\n", 206 | "from datetime import datetime\n", 207 | "\n", 208 | "# Let's create a simple class to demonstrate\n", 209 | "class Article:\n", 210 | " def __init__(self, title, author, views, date):\n", 211 | " self.title = title\n", 212 | " self.author = author\n", 213 | " self.stats = type('Stats', (), {'views': views}) # Nested attribute\n", 214 | " self.date = date\n", 215 | "\n", 216 | " def __repr__(self):\n", 217 | " return f\"{self.title} by {self.author}\"\n", 218 | "\n", 219 | "# Create some sample articles\n", 220 | "articles = [\n", 221 | "\tArticle(\"Python Tips\", \"Alice\", 1500, datetime(2025, 1, 15)),\n", 222 | "\tArticle(\"Data Science\", \"Bob\", 2500, datetime(2025, 1, 20)),\n", 223 | "\tArticle(\"Web Dev\", \"Alice\", 1800, datetime(2025, 1, 10))\n", 224 | "]\n", 225 | "\n", 226 | "# Sort articles by multiple criteria\n", 227 | "get_author_views = attrgetter('author', 'stats.views')\n", 228 | "\n", 229 | "# Sort by author and then by views\n", 230 | "sorted_articles = sorted(articles, key=get_author_views)\n", 231 | "for article in sorted_articles:\n", 232 | "\tprint(f\"{article.author}: {article.title} ({article.stats.views} views)\")\n", 233 | "\n", 234 | "# You can also use it to extract specific attributes\n", 235 | "dates = list(map(attrgetter('date'), articles))\n", 236 | "print(\"\\nArticle dates:\", dates)\n" 237 | ], 238 | "metadata": { 239 | "colab": { 240 | "base_uri": "https://localhost:8080/" 241 | }, 242 | "id": "5BZsBwtccPnL", 243 | "outputId": "881898ba-f71e-4055-c4da-29930742f206" 244 | }, 245 | "execution_count": 2, 246 | "outputs": [ 247 | { 248 | "output_type": "stream", 249 | "name": "stdout", 250 | "text": [ 251 | "Alice: Python Tips (1500 views)\n", 252 | "Alice: Web Dev (1800 views)\n", 253 | "Bob: Data Science (2500 views)\n", 254 | "\n", 255 | "Article dates: [datetime.datetime(2025, 1, 15, 0, 0), datetime.datetime(2025, 1, 20, 0, 0), datetime.datetime(2025, 1, 10, 0, 0)]\n" 256 | ] 257 | } 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "source": [ 263 | "from itertools import chain\n", 264 | "\n", 265 | "# Let's say we're processing data from multiple sources\n", 266 | "sales_data = [\n", 267 | "\t[('Jan', 100), ('Feb', 150)],\n", 268 | "\t[('Mar', 200), ('Apr', 180)],\n", 269 | "\t[('May', 210), ('Jun', 190)]\n", 270 | "]\n", 271 | "\n", 272 | "# Flatten the data efficiently\n", 273 | "flat_sales = list(chain.from_iterable(sales_data))\n", 274 | "print(\"Flattened sales data:\", flat_sales)\n", 275 | "\n", 276 | "# List comprehension approach (creates intermediate list):\n", 277 | "flat_list = [item for sublist in sales_data for item in sublist]\n", 278 | "\n", 279 | "# chain.from_iterable approach (generates items one at a time):\n", 280 | "flat_iterator = chain.from_iterable(sales_data)\n" 281 | ], 282 | "metadata": { 283 | "colab": { 284 | "base_uri": "https://localhost:8080/" 285 | }, 286 | "id": "ypIugob-dKrI", 287 | "outputId": "1b769ce6-85d2-4f27-f9a9-17755b15a88e" 288 | }, 289 | "execution_count": null, 290 | "outputs": [ 291 | { 292 | "output_type": "stream", 293 | "name": "stdout", 294 | "text": [ 295 | "Flattened sales data: [('Jan', 100), ('Feb', 150), ('Mar', 200), ('Apr', 180), ('May', 210), ('Jun', 190)]\n" 296 | ] 297 | } 298 | ] 299 | }, 300 | { 301 | "cell_type": "code", 302 | "source": [ 303 | "from itertools import product\n", 304 | "\n", 305 | "# Available options for a custom laptop\n", 306 | "processors = ['i5', 'i7', 'i9']\n", 307 | "ram = ['8GB', '16GB', '32GB']\n", 308 | "storage = ['256GB', '512GB', '1TB']\n", 309 | "\n", 310 | "# Generate all possible combinations\n", 311 | "configurations = list(product(processors, ram, storage))\n", 312 | "\n", 313 | "print(\"Possible laptop configurations:\")\n", 314 | "for config in configurations:\n", 315 | "\tprint(f\"Processor: {config[0]}, RAM: {config[1]}, Storage: {config[2]}\")\n" 316 | ], 317 | "metadata": { 318 | "colab": { 319 | "base_uri": "https://localhost:8080/" 320 | }, 321 | "id": "7CizPOYGfgIC", 322 | "outputId": "308a292f-4b88-433f-8304-c369c7e22076" 323 | }, 324 | "execution_count": null, 325 | "outputs": [ 326 | { 327 | "output_type": "stream", 328 | "name": "stdout", 329 | "text": [ 330 | "Possible laptop configurations:\n", 331 | "Processor: i5, RAM: 8GB, Storage: 256GB\n", 332 | "Processor: i5, RAM: 8GB, Storage: 512GB\n", 333 | "Processor: i5, RAM: 8GB, Storage: 1TB\n", 334 | "Processor: i5, RAM: 16GB, Storage: 256GB\n", 335 | "Processor: i5, RAM: 16GB, Storage: 512GB\n", 336 | "Processor: i5, RAM: 16GB, Storage: 1TB\n", 337 | "Processor: i5, RAM: 32GB, Storage: 256GB\n", 338 | "Processor: i5, RAM: 32GB, Storage: 512GB\n", 339 | "Processor: i5, RAM: 32GB, Storage: 1TB\n", 340 | "Processor: i7, RAM: 8GB, Storage: 256GB\n", 341 | "Processor: i7, RAM: 8GB, Storage: 512GB\n", 342 | "Processor: i7, RAM: 8GB, Storage: 1TB\n", 343 | "Processor: i7, RAM: 16GB, Storage: 256GB\n", 344 | "Processor: i7, RAM: 16GB, Storage: 512GB\n", 345 | "Processor: i7, RAM: 16GB, Storage: 1TB\n", 346 | "Processor: i7, RAM: 32GB, Storage: 256GB\n", 347 | "Processor: i7, RAM: 32GB, Storage: 512GB\n", 348 | "Processor: i7, RAM: 32GB, Storage: 1TB\n", 349 | "Processor: i9, RAM: 8GB, Storage: 256GB\n", 350 | "Processor: i9, RAM: 8GB, Storage: 512GB\n", 351 | "Processor: i9, RAM: 8GB, Storage: 1TB\n", 352 | "Processor: i9, RAM: 16GB, Storage: 256GB\n", 353 | "Processor: i9, RAM: 16GB, Storage: 512GB\n", 354 | "Processor: i9, RAM: 16GB, Storage: 1TB\n", 355 | "Processor: i9, RAM: 32GB, Storage: 256GB\n", 356 | "Processor: i9, RAM: 32GB, Storage: 512GB\n", 357 | "Processor: i9, RAM: 32GB, Storage: 1TB\n" 358 | ] 359 | } 360 | ] 361 | }, 362 | { 363 | "cell_type": "code", 364 | "source": [], 365 | "metadata": { 366 | "id": "S4NDCtY0fmKX" 367 | }, 368 | "execution_count": null, 369 | "outputs": [] 370 | } 371 | ] 372 | } -------------------------------------------------------------------------------- /useful-python-functions/useless_python_functions.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": 9, 20 | "metadata": { 21 | "colab": { 22 | "base_uri": "https://localhost:8080/" 23 | }, 24 | "id": "l0U2hADqYEll", 25 | "outputId": "094db3c8-5d5e-4446-9aa7-bb557bcf91cc" 26 | }, 27 | "outputs": [ 28 | { 29 | "output_type": "stream", 30 | "name": "stdout", 31 | "text": [ 32 | "This is a multi-line string\n", 33 | "that will have consistent indentation\n", 34 | "regardless of how it's indented in the code.\n", 35 | "Pretty neat, right?\n" 36 | ] 37 | } 38 | ], 39 | "source": [ 40 | "import textwrap\n", 41 | "\n", 42 | "def my_function():\n", 43 | " # Without dedent, this would preserve all the leading spaces\n", 44 | " description = textwrap.dedent(\"\"\"\n", 45 | " This is a multi-line string\n", 46 | " that will have consistent indentation\n", 47 | " regardless of how it's indented in the code.\n", 48 | " Pretty neat, right?\n", 49 | " \"\"\").strip()\n", 50 | "\n", 51 | " return description\n", 52 | "\n", 53 | "print(my_function())\n" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "source": [ 59 | "import difflib\n", 60 | "\n", 61 | "words = [\"python\", \"javascript\", \"typescript\", \"ruby\", \"golang\"]\n", 62 | "search = \"pythn\"\n", 63 | "\n", 64 | "matches = difflib.get_close_matches(search, words, n=3, cutoff=0.6)\n", 65 | "print(f\"Did you mean: {matches}\")\n", 66 | "\n", 67 | "search = \"typescript\"\n", 68 | "matches = difflib.get_close_matches(search, words)\n", 69 | "print(f\"Matches: {matches}\")\n" 70 | ], 71 | "metadata": { 72 | "colab": { 73 | "base_uri": "https://localhost:8080/" 74 | }, 75 | "id": "-V3keWnzYVcZ", 76 | "outputId": "492750f6-c813-4953-e0a2-badbda57c0ed" 77 | }, 78 | "execution_count": 10, 79 | "outputs": [ 80 | { 81 | "output_type": "stream", 82 | "name": "stdout", 83 | "text": [ 84 | "Did you mean: ['python']\n", 85 | "Matches: ['typescript', 'javascript']\n" 86 | ] 87 | } 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "source": [ 93 | "import uuid\n", 94 | "\n", 95 | "# Generate a random UUID\n", 96 | "random_id = uuid.uuid4()\n", 97 | "print(f\"Unique ID: {random_id}\")\n", 98 | "\n", 99 | "# Use as string for filenames, database keys, etc.\n", 100 | "filename = f\"document-{uuid.uuid4()}.pdf\"\n", 101 | "print(filename)\n" 102 | ], 103 | "metadata": { 104 | "colab": { 105 | "base_uri": "https://localhost:8080/" 106 | }, 107 | "id": "1VDXGK32YeMG", 108 | "outputId": "0f9c8905-324a-446d-c12f-16ebe3004584" 109 | }, 110 | "execution_count": 11, 111 | "outputs": [ 112 | { 113 | "output_type": "stream", 114 | "name": "stdout", 115 | "text": [ 116 | "Unique ID: fc4c6638-9707-437b-83a1-76206b5f7191\n", 117 | "document-b5ccbe7a-fad9-4611-8163-be1015c634b9.pdf\n" 118 | ] 119 | } 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "source": [ 125 | "import shutil\n", 126 | "\n", 127 | "columns, rows = shutil.get_terminal_size()\n", 128 | "print(f\"Your terminal is {columns} columns wide and {rows} rows tall\")\n", 129 | "\n", 130 | "# Create a horizontal divider that fits perfectly\n", 131 | "print(\"-\" * columns)\n" 132 | ], 133 | "metadata": { 134 | "colab": { 135 | "base_uri": "https://localhost:8080/" 136 | }, 137 | "id": "l8mLXo4UZq7v", 138 | "outputId": "6fa7fb2f-bfbc-4b9d-8ba5-6500dbb1d599" 139 | }, 140 | "execution_count": 12, 141 | "outputs": [ 142 | { 143 | "output_type": "stream", 144 | "name": "stdout", 145 | "text": [ 146 | "Your terminal is 80 columns wide and 24 rows tall\n", 147 | "--------------------------------------------------------------------------------\n" 148 | ] 149 | } 150 | ] 151 | }, 152 | { 153 | "cell_type": "code", 154 | "source": [ 155 | "from itertools import groupby\n", 156 | "from operator import itemgetter\n", 157 | "\n", 158 | "# Sample data: (name, department)\n", 159 | "employees = [\n", 160 | " (\"Alice\", \"Engineering\"),\n", 161 | " (\"Bob\", \"Marketing\"),\n", 162 | " (\"Charlie\", \"Engineering\"),\n", 163 | " (\"Diana\", \"HR\"),\n", 164 | " (\"Evan\", \"Marketing\"),\n", 165 | "]\n", 166 | "\n", 167 | "# Sort by department first (groupby works on consecutive items)\n", 168 | "employees.sort(key=itemgetter(1))\n", 169 | "\n", 170 | "# Group by department\n", 171 | "for department, group in groupby(employees, key=itemgetter(1)):\n", 172 | " print(f\"\\n{department} Department:\")\n", 173 | " for name, _ in group:\n", 174 | " print(f\" - {name}\")\n" 175 | ], 176 | "metadata": { 177 | "colab": { 178 | "base_uri": "https://localhost:8080/" 179 | }, 180 | "id": "RuBPV4akZ26d", 181 | "outputId": "d0a1cf22-eb84-4bbc-c8ac-b31a1863fab8" 182 | }, 183 | "execution_count": 13, 184 | "outputs": [ 185 | { 186 | "output_type": "stream", 187 | "name": "stdout", 188 | "text": [ 189 | "\n", 190 | "Engineering Department:\n", 191 | " - Alice\n", 192 | " - Charlie\n", 193 | "\n", 194 | "HR Department:\n", 195 | " - Diana\n", 196 | "\n", 197 | "Marketing Department:\n", 198 | " - Bob\n", 199 | " - Evan\n" 200 | ] 201 | } 202 | ] 203 | }, 204 | { 205 | "cell_type": "code", 206 | "source": [ 207 | "from collections import ChainMap\n", 208 | "\n", 209 | "defaults = {\"theme\": \"dark\", \"language\": \"en\", \"timeout\": 30}\n", 210 | "user_settings = {\"theme\": \"light\"}\n", 211 | "session_settings = {\"timeout\": 60}\n", 212 | "\n", 213 | "# Create a combined view of all settings\n", 214 | "settings = ChainMap(session_settings, user_settings, defaults)\n", 215 | "\n", 216 | "print(settings[\"theme\"])\n", 217 | "print(settings[\"language\"])\n", 218 | "print(settings[\"timeout\"])\n" 219 | ], 220 | "metadata": { 221 | "colab": { 222 | "base_uri": "https://localhost:8080/" 223 | }, 224 | "id": "upb3AgmhaYBt", 225 | "outputId": "515168f7-c6ab-4d60-f9ce-3ceea58353fc" 226 | }, 227 | "execution_count": 14, 228 | "outputs": [ 229 | { 230 | "output_type": "stream", 231 | "name": "stdout", 232 | "text": [ 233 | "light\n", 234 | "en\n", 235 | "60\n" 236 | ] 237 | } 238 | ] 239 | }, 240 | { 241 | "cell_type": "code", 242 | "source": [ 243 | "import os.path\n", 244 | "\n", 245 | "paths = [\n", 246 | " \"/home/user/documents/work/report.pdf\",\n", 247 | " \"/home/user/documents/personal/taxes.xlsx\",\n", 248 | " \"/home/user/documents/work/presentation.pptx\"\n", 249 | "]\n", 250 | "\n", 251 | "common = os.path.commonpath(paths)\n", 252 | "print(f\"Common directory: {common}\")" 253 | ], 254 | "metadata": { 255 | "colab": { 256 | "base_uri": "https://localhost:8080/" 257 | }, 258 | "id": "59u6rRgdbNH8", 259 | "outputId": "17053c61-d0dc-4f19-c9e5-e07f011eff91" 260 | }, 261 | "execution_count": 15, 262 | "outputs": [ 263 | { 264 | "output_type": "stream", 265 | "name": "stdout", 266 | "text": [ 267 | "Common directory: /home/user/documents\n" 268 | ] 269 | } 270 | ] 271 | }, 272 | { 273 | "cell_type": "code", 274 | "source": [], 275 | "metadata": { 276 | "id": "Lo3SDGvhcPWY" 277 | }, 278 | "execution_count": 15, 279 | "outputs": [] 280 | } 281 | ] 282 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------