Add tech_docs/python/python_functions.md
This commit is contained in:
408
tech_docs/python/python_functions.md
Normal file
408
tech_docs/python/python_functions.md
Normal file
@@ -0,0 +1,408 @@
|
|||||||
|
# Python Functions Cheatsheet
|
||||||
|
|
||||||
|
## Basic Function Types
|
||||||
|
|
||||||
|
### Built-in Functions
|
||||||
|
```python
|
||||||
|
# Common built-in functions
|
||||||
|
len(sequence) # Returns length of a sequence
|
||||||
|
print(*objects) # Prints objects to console
|
||||||
|
type(object) # Returns the type of an object
|
||||||
|
range(start, stop) # Creates sequence of numbers
|
||||||
|
max(iterable) # Returns maximum value
|
||||||
|
min(iterable) # Returns minimum value
|
||||||
|
sum(iterable) # Sums items in an iterable
|
||||||
|
```
|
||||||
|
|
||||||
|
### User-defined Functions
|
||||||
|
```python
|
||||||
|
# Basic function definition
|
||||||
|
def function_name(parameters):
|
||||||
|
"""Docstring: explains what function does"""
|
||||||
|
# Function body
|
||||||
|
return value # Optional return statement
|
||||||
|
|
||||||
|
# Example
|
||||||
|
def square(x):
|
||||||
|
"""Returns the square of a number."""
|
||||||
|
return x * x
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lambda Functions (Anonymous)
|
||||||
|
```python
|
||||||
|
# lambda arguments: expression
|
||||||
|
square = lambda x: x * x
|
||||||
|
add = lambda x, y: x + y
|
||||||
|
|
||||||
|
# With higher-order functions
|
||||||
|
list(map(lambda x: x * 2, [1, 2, 3])) # [2, 4, 6]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Function Parameters
|
||||||
|
|
||||||
|
### No Parameters
|
||||||
|
```python
|
||||||
|
def say_hello():
|
||||||
|
return "Hello, world!"
|
||||||
|
|
||||||
|
# Call with no arguments
|
||||||
|
say_hello()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Required Parameters
|
||||||
|
```python
|
||||||
|
def multiply(a, b):
|
||||||
|
return a * b
|
||||||
|
|
||||||
|
# Must provide all required arguments
|
||||||
|
result = multiply(5, 3) # 15
|
||||||
|
```
|
||||||
|
|
||||||
|
### Default Parameters
|
||||||
|
```python
|
||||||
|
def power(base, exponent=2):
|
||||||
|
return base ** exponent
|
||||||
|
|
||||||
|
# Default parameters are optional
|
||||||
|
power(5) # 25 (uses default exponent=2)
|
||||||
|
power(5, 3) # 125 (overrides default)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Keyword Arguments
|
||||||
|
```python
|
||||||
|
def create_person(name, age, job):
|
||||||
|
return f"{name} is {age} years old and works as a {job}"
|
||||||
|
|
||||||
|
# Order doesn't matter with keyword args
|
||||||
|
create_person(age=30, job="developer", name="Alice")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Positional-Only and Keyword-Only Arguments (Python 3.8+)
|
||||||
|
```python
|
||||||
|
def process_data(pos1, pos2, /, pos_or_kwd, *, kwd1, kwd2):
|
||||||
|
"""
|
||||||
|
pos1, pos2: positional-only (before /)
|
||||||
|
pos_or_kwd: positional or keyword
|
||||||
|
kwd1, kwd2: keyword-only (after *)
|
||||||
|
"""
|
||||||
|
return f"Processing: {pos1}, {pos2}, {pos_or_kwd}, {kwd1}, {kwd2}"
|
||||||
|
|
||||||
|
# Valid calls:
|
||||||
|
process_data(1, 2, 3, kwd1=4, kwd2=5) # All specified
|
||||||
|
process_data(1, 2, pos_or_kwd=3, kwd1=4, kwd2=5) # pos_or_kwd as keyword
|
||||||
|
|
||||||
|
# Invalid calls:
|
||||||
|
# process_data(pos1=1, pos2=2, pos_or_kwd=3, kwd1=4, kwd2=5) # Error: pos1, pos2 can't be keyword
|
||||||
|
# process_data(1, 2, 3, 4, 5) # Error: kwd1, kwd2 can't be positional
|
||||||
|
```
|
||||||
|
|
||||||
|
### Variable-length Arguments (*args and **kwargs)
|
||||||
|
```python
|
||||||
|
def sum_all(*args):
|
||||||
|
"""Accept any number of positional arguments"""
|
||||||
|
return sum(args)
|
||||||
|
|
||||||
|
def user_info(**kwargs):
|
||||||
|
"""Accept any number of keyword arguments"""
|
||||||
|
for key, value in kwargs.items():
|
||||||
|
print(f"{key}: {value}")
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
sum_all(1, 2, 3, 4, 5) # 15
|
||||||
|
user_info(name="Alice", age=30, city="New York")
|
||||||
|
|
||||||
|
# Combination
|
||||||
|
def process_all(*args, **kwargs):
|
||||||
|
print(f"Args: {args}")
|
||||||
|
print(f"Kwargs: {kwargs}")
|
||||||
|
|
||||||
|
process_all(1, 2, 3, name="Alice", age=30) # Args: (1, 2, 3), Kwargs: {'name': 'Alice', 'age': 30}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Variable Scope and the LEGB Rule
|
||||||
|
|
||||||
|
```python
|
||||||
|
# The LEGB rule determines how Python looks up variable names:
|
||||||
|
# Local → Enclosing → Global → Built-in
|
||||||
|
|
||||||
|
x = 10 # Global scope
|
||||||
|
|
||||||
|
def outer_function():
|
||||||
|
y = 20 # Enclosing scope for inner_function
|
||||||
|
|
||||||
|
def inner_function():
|
||||||
|
z = 30 # Local scope
|
||||||
|
print(f"Local: {z}")
|
||||||
|
print(f"Enclosing: {y}")
|
||||||
|
print(f"Global: {x}")
|
||||||
|
print(f"Built-in: {len}") # Built-in function
|
||||||
|
|
||||||
|
return inner_function
|
||||||
|
|
||||||
|
func = outer_function()
|
||||||
|
func() # Prints all variables from different scopes
|
||||||
|
|
||||||
|
# Modifying variables in outer scopes
|
||||||
|
def modify_global():
|
||||||
|
global x
|
||||||
|
x = 100 # Modifies the global x
|
||||||
|
|
||||||
|
def modify_enclosing():
|
||||||
|
y = 20
|
||||||
|
|
||||||
|
def inner():
|
||||||
|
nonlocal y
|
||||||
|
y = 200 # Modifies the enclosing y
|
||||||
|
|
||||||
|
inner()
|
||||||
|
print(y) # 200
|
||||||
|
|
||||||
|
modify_global()
|
||||||
|
print(x) # 100
|
||||||
|
modify_enclosing()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Advanced Function Concepts
|
||||||
|
|
||||||
|
### Recursive Functions
|
||||||
|
```python
|
||||||
|
def factorial(n):
|
||||||
|
if n <= 1:
|
||||||
|
return 1
|
||||||
|
return n * factorial(n-1)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Closures
|
||||||
|
```python
|
||||||
|
def counter_factory():
|
||||||
|
count = 0 # This variable is "enclosed" in the returned function
|
||||||
|
|
||||||
|
def increment():
|
||||||
|
nonlocal count
|
||||||
|
count += 1
|
||||||
|
return count
|
||||||
|
|
||||||
|
return increment
|
||||||
|
|
||||||
|
counter = counter_factory()
|
||||||
|
print(counter()) # 1
|
||||||
|
print(counter()) # 2
|
||||||
|
print(counter()) # 3
|
||||||
|
|
||||||
|
# Another example: function with "memory"
|
||||||
|
def make_multiplier(factor):
|
||||||
|
def multiply(number):
|
||||||
|
return number * factor # Uses factor from enclosing scope
|
||||||
|
return multiply
|
||||||
|
|
||||||
|
double = make_multiplier(2)
|
||||||
|
triple = make_multiplier(3)
|
||||||
|
print(double(5)) # 10
|
||||||
|
print(triple(5)) # 15
|
||||||
|
```
|
||||||
|
|
||||||
|
### Higher-order Functions
|
||||||
|
```python
|
||||||
|
# Functions that take/return other functions
|
||||||
|
def apply(func, value):
|
||||||
|
return func(value)
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
apply(lambda x: x*2, 5) # 10
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generator Functions
|
||||||
|
```python
|
||||||
|
def count_up_to(n):
|
||||||
|
i = 1
|
||||||
|
while i <= n:
|
||||||
|
yield i
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# Creates a generator object
|
||||||
|
counter = count_up_to(3)
|
||||||
|
next(counter) # 1
|
||||||
|
next(counter) # 2
|
||||||
|
next(counter) # 3
|
||||||
|
```
|
||||||
|
|
||||||
|
### Decorators
|
||||||
|
```python
|
||||||
|
from functools import wraps
|
||||||
|
|
||||||
|
def my_decorator(func):
|
||||||
|
@wraps(func) # Preserves metadata of the original function
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
print("Before function call")
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
print("After function call")
|
||||||
|
return result
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
@my_decorator
|
||||||
|
def greet(name):
|
||||||
|
"""Greet someone by name."""
|
||||||
|
return f"Hello, {name}!"
|
||||||
|
|
||||||
|
# The metadata is preserved
|
||||||
|
print(greet.__name__) # 'greet' (not 'wrapper')
|
||||||
|
print(greet.__doc__) # 'Greet someone by name.'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Type Hints (PEP 484)
|
||||||
|
```python
|
||||||
|
from typing import List, Dict, Tuple, Optional, Union, Any, Callable
|
||||||
|
|
||||||
|
def headline(text: str, align: bool = True) -> str:
|
||||||
|
"""Format text as a headline."""
|
||||||
|
if align:
|
||||||
|
return f"{text.title()}\n{'-' * len(text)}"
|
||||||
|
else:
|
||||||
|
return f"{text.title()}"
|
||||||
|
|
||||||
|
# More complex type hints
|
||||||
|
def process_items(items: List[str]) -> Dict[str, int]:
|
||||||
|
return {item: len(item) for item in items}
|
||||||
|
|
||||||
|
# Optional parameters
|
||||||
|
def greeting(name: Optional[str] = None) -> str:
|
||||||
|
if name is None:
|
||||||
|
return "Hello, Guest!"
|
||||||
|
return f"Hello, {name}!"
|
||||||
|
|
||||||
|
# Union types
|
||||||
|
def process_id(id: Union[int, str]) -> str:
|
||||||
|
return str(id)
|
||||||
|
|
||||||
|
# Function type hints
|
||||||
|
def apply_formatter(value: str, formatter: Callable[[str], str]) -> str:
|
||||||
|
return formatter(value)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docstring Conventions (PEP 257)
|
||||||
|
|
||||||
|
```python
|
||||||
|
def calculate_discount(price: float, rate: float = 0.1) -> float:
|
||||||
|
"""
|
||||||
|
Calculate the discounted price.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
price: The original price
|
||||||
|
rate: The discount rate (default: 0.1)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The price after applying the discount
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If price or rate is negative
|
||||||
|
"""
|
||||||
|
if price < 0 or rate < 0:
|
||||||
|
raise ValueError("Price and rate must be non-negative")
|
||||||
|
return price * (1 - rate)
|
||||||
|
|
||||||
|
# Google style
|
||||||
|
def fetch_data(resource_id: str, max_results: int = 100) -> List[Dict]:
|
||||||
|
"""Fetches data from the specified resource.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
resource_id: The ID of the resource to fetch.
|
||||||
|
max_results: The maximum number of results to return.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of dictionaries containing the fetched data.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ConnectionError: If unable to connect to the data source.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
```
|
||||||
|
|
||||||
|
## Class Methods
|
||||||
|
|
||||||
|
### Instance Methods
|
||||||
|
```python
|
||||||
|
class MyClass:
|
||||||
|
def instance_method(self, x):
|
||||||
|
"""Access instance attributes with self"""
|
||||||
|
self.value = x
|
||||||
|
```
|
||||||
|
|
||||||
|
### Class Methods
|
||||||
|
```python
|
||||||
|
class MyClass:
|
||||||
|
@classmethod
|
||||||
|
def class_method(cls, x):
|
||||||
|
"""Access class attributes with cls"""
|
||||||
|
return cls(x) # Can create instances
|
||||||
|
```
|
||||||
|
|
||||||
|
### Static Methods
|
||||||
|
```python
|
||||||
|
class MyClass:
|
||||||
|
@staticmethod
|
||||||
|
def static_method(x):
|
||||||
|
"""No access to instance or class"""
|
||||||
|
return x * 2
|
||||||
|
```
|
||||||
|
|
||||||
|
## Functional Programming
|
||||||
|
|
||||||
|
### map()
|
||||||
|
```python
|
||||||
|
# Apply function to all items in an iterable
|
||||||
|
numbers = [1, 2, 3, 4]
|
||||||
|
squares = list(map(lambda x: x**2, numbers)) # [1, 4, 9, 16]
|
||||||
|
```
|
||||||
|
|
||||||
|
### filter()
|
||||||
|
```python
|
||||||
|
# Filter items based on a function
|
||||||
|
numbers = [1, 2, 3, 4, 5, 6]
|
||||||
|
evens = list(filter(lambda x: x % 2 == 0, numbers)) # [2, 4, 6]
|
||||||
|
```
|
||||||
|
|
||||||
|
### reduce()
|
||||||
|
```python
|
||||||
|
from functools import reduce
|
||||||
|
# Apply function cumulatively to items
|
||||||
|
numbers = [1, 2, 3, 4]
|
||||||
|
product = reduce(lambda x, y: x * y, numbers) # 24
|
||||||
|
```
|
||||||
|
|
||||||
|
### Other functools Utilities
|
||||||
|
```python
|
||||||
|
from functools import partial, lru_cache
|
||||||
|
|
||||||
|
# partial: Create new functions with pre-filled arguments
|
||||||
|
def power(base, exponent):
|
||||||
|
return base ** exponent
|
||||||
|
|
||||||
|
square = partial(power, exponent=2)
|
||||||
|
cube = partial(power, exponent=3)
|
||||||
|
|
||||||
|
print(square(4)) # 16
|
||||||
|
print(cube(4)) # 64
|
||||||
|
|
||||||
|
# lru_cache: Memoization decorator (caches results)
|
||||||
|
@lru_cache(maxsize=128)
|
||||||
|
def fibonacci(n):
|
||||||
|
if n <= 1:
|
||||||
|
return n
|
||||||
|
return fibonacci(n-1) + fibonacci(n-2)
|
||||||
|
|
||||||
|
# First call computes result, subsequent calls with same args use the cache
|
||||||
|
print(fibonacci(30)) # Fast, even for large values
|
||||||
|
```
|
||||||
|
|
||||||
|
## Function Design Best Practices
|
||||||
|
|
||||||
|
1. **Single Responsibility**: Functions should do one thing well
|
||||||
|
2. **Pure Functions**: Avoid side effects when possible
|
||||||
|
3. **Descriptive Names**: Use clear, descriptive function names
|
||||||
|
4. **Parameter Count**: Aim for fewer than 4 parameters
|
||||||
|
5. **Default Values**: Use sensible defaults for optional parameters
|
||||||
|
6. **Docstrings**: Document what the function does, parameters, and return values
|
||||||
|
7. **Type Hints**: Use type annotations for better tooling and clarity
|
||||||
|
8. **Error Handling**: Validate inputs and handle errors gracefully
|
||||||
|
9. **Return Values**: Be consistent with return types
|
||||||
|
10. **Early Returns**: Use early returns to avoid deep nesting
|
||||||
Reference in New Issue
Block a user