Add tech_docs/python/python_functions.md

This commit is contained in:
2025-04-08 12:14:14 +00:00
parent 60d1d5caf4
commit bb4f6b41e0

View 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