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