Advanced Type Checking
Complex Type Validation Techniques
from typing import Any, Callable, TypeVar, Generic
T = TypeVar('T')
class TypeValidator(Generic[T]):
def __init__(self, validator: Callable[[Any], bool]):
self._validator = validator
def validate(self, value: Any) -> T:
if not self._validator(value):
raise TypeError(f"Invalid type for {value}")
return value
def complex_type_checker():
def is_positive_integer(x):
return isinstance(x, int) and x > 0
def is_non_empty_string(x):
return isinstance(x, str) and len(x) > 0
positive_int_validator = TypeValidator(is_positive_integer)
non_empty_string_validator = TypeValidator(is_non_empty_string)
return positive_int_validator, non_empty_string_validator
Nested Type Validation
def validate_nested_structure(spec):
def validate(data):
if isinstance(spec, dict):
if not isinstance(data, dict):
return False
return all(
key in data and validate(data[key])
for key, value in spec.items()
)
elif isinstance(spec, type):
return isinstance(data, spec)
return False
return validate
## Example usage
user_spec = {
'name': str,
'age': int,
'address': {
'city': str,
'zip': str
}
}
validator = validate_nested_structure(user_spec)
Type Checking Strategies
Strategy |
Description |
Use Case |
Runtime Validation |
Check types during execution |
Dynamic type safety |
Structural Typing |
Validate object structure |
Complex data validation |
Generic Type Checking |
Support flexible type constraints |
Reusable type validation |
Decorator-Based Advanced Validation
def validate_args(**type_specs):
def decorator(func):
def wrapper(*args, **kwargs):
## Validate positional arguments
for i, (arg, spec) in enumerate(zip(args, type_specs.values())):
if not isinstance(arg, spec):
raise TypeError(f"Argument {i} must be {spec}")
## Validate keyword arguments
for key, value in kwargs.items():
if key in type_specs and not isinstance(value, type_specs[key]):
raise TypeError(f"Argument {key} must be {type_specs[key]}")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_args(name=str, age=int, active=bool)
def create_user(name, age, active=True):
return {"name": name, "age": age, "active": active}
Type Validation Workflow
graph TD
A[Input Data] --> B{Structural Check}
B -->|Valid Structure| C{Type Validation}
C -->|Pass Type Check| D[Process Data]
C -->|Fail Type Check| E[Raise TypeError]
B -->|Invalid Structure| F[Reject Data]
Dynamic Type Inference
def infer_and_validate(data, expected_type=None):
def get_type_hints(obj):
return {
list: lambda x: all(isinstance(item, type(x[0])) for item in x),
dict: lambda x: all(isinstance(k, str) and isinstance(v, (int, str)) for k, v in x.items())
}.get(type(obj), lambda x: True)
if expected_type and not isinstance(data, expected_type):
raise TypeError(f"Expected {expected_type}, got {type(data)}")
type_validator = get_type_hints(data)
if not type_validator(data):
raise TypeError("Inconsistent data types")
return data
LabEx Insights
At LabEx, we recommend combining static type hints with runtime validation for comprehensive type checking.
Best Practices
- Use type hints for static analysis
- Implement runtime type validation
- Create reusable validation decorators
- Handle type conversion gracefully
- Provide meaningful error messages
- Minimize validation overhead
- Cache validation results
- Use lazy evaluation techniques
- Implement selective type checking