Practical Type Validation
Overview of Type Validation Techniques
Type validation ensures data integrity and prevents runtime errors by systematically checking input types and structures.
Comprehensive Validation Strategies
1. Data Class Validation
from dataclasses import dataclass
from typing import List
import re
@dataclass
class User:
name: str
email: str
age: int
skills: List[str]
def __post_init__(self):
## Custom validation logic
if not re.match(r"[^@]+@[^@]+\.[^@]+", self.email):
raise ValueError("Invalid email format")
if self.age < 18:
raise ValueError("User must be 18 or older")
if len(self.skills) == 0:
raise ValueError("At least one skill is required")
## Example usage
try:
user = User(
name="LabEx Developer",
email="[email protected]",
age=25,
skills=["Python", "Data Science"]
)
except ValueError as e:
print(f"Validation Error: {e}")
2. Pydantic Model Validation
from pydantic import BaseModel, validator, EmailStr
from typing import List
class AdvancedUser(BaseModel):
name: str
email: EmailStr
age: int
skills: List[str]
@validator('age')
def validate_age(cls, age):
if age < 18:
raise ValueError("Must be 18 or older")
return age
@validator('skills')
def validate_skills(cls, skills):
if len(skills) < 1:
raise ValueError("At least one skill required")
return skills
## Validation example
try:
user = AdvancedUser(
name="LabEx Developer",
email="[email protected]",
age=25,
skills=["Python", "Machine Learning"]
)
except ValueError as e:
print(f"Validation Error: {e}")
Validation Techniques Comparison
Technique |
Pros |
Cons |
Use Case |
Manual Validation |
Full Control |
Verbose |
Simple Scenarios |
Data Classes |
Built-in Python |
Limited Validation |
Structured Data |
Pydantic |
Comprehensive |
External Dependency |
Complex Validation |
Validation Flow Diagram
graph TD
A[Input Data] --> B{Structural Check}
B --> |Pass| C{Type Check}
B --> |Fail| D[Reject Data]
C --> |Pass| E{Custom Validation}
C --> |Fail| F[Reject Data]
E --> |Pass| G[Accept Data]
E --> |Fail| H[Reject Data]
Advanced Validation Patterns
Custom Validation Decorator
from functools import wraps
from typing import Callable, Any
def validate_types(*type_args, **type_kwargs):
def decorator(func: Callable):
@wraps(func)
def wrapper(*args, **kwargs):
## Validate positional arguments
for arg, expected_type in zip(args, type_args):
if not isinstance(arg, expected_type):
raise TypeError(f"Expected {expected_type}, got {type(arg)}")
## Validate keyword arguments
for key, value in kwargs.items():
if key in type_kwargs:
expected_type = type_kwargs[key]
if not isinstance(value, expected_type):
raise TypeError(f"Expected {expected_type} for {key}, got {type(value)}")
return func(*args, **kwargs)
return wrapper
return decorator
@validate_types(str, int, name=str)
def create_profile(username: str, age: int, name: str):
return f"{name} (Age: {age})"
## Usage examples
try:
profile = create_profile("developer", 25, name="LabEx")
print(profile)
except TypeError as e:
print(f"Validation Error: {e}")
Best Practices for Type Validation
- Use type hints consistently
- Implement comprehensive validation logic
- Provide clear error messages
- Balance between strictness and flexibility
- Choose appropriate validation techniques
- Minimize validation complexity
- Use efficient validation libraries
- Implement lazy validation when possible
- Profile and optimize validation logic
By implementing robust type validation, developers can create more reliable and self-documenting Python applications with enhanced data integrity.