Skip to content

Instantly share code, notes, and snippets.

@JeancarloBarrios
Created March 28, 2023 17:45
Show Gist options
  • Save JeancarloBarrios/3421841d915b8ea8a775f4ccaa112032 to your computer and use it in GitHub Desktop.
Save JeancarloBarrios/3421841d915b8ea8a775f4ccaa112032 to your computer and use it in GitHub Desktop.

attrs as typing

1. Introduction

In this Architecture Decision Record (ADR), we will explore Python options for type hints: Data Classes and attrs library for structuring and managing data in Python projects. Based on the comparison, we will justify our choice of using attrs over Data Classes.

2. Decision Drivers

  • Ease of use
  • Performance
  • Extensibility and customization
  • Compatibility with different Python versions
  • Type safety

3. Considered Options

  • Python Data Classes (available in Python 3.7+)
  • attrs library

4. Comparison

4.1. Ease of Use

Python Data Classes:

  • Introduced in Python 3.7, they offer a simple and clean syntax for defining classes with minimal boilerplate.
  • Automatically generates special methods like __init__, __repr__, and __eq__.
  • Supports default values and mutable default values using default_factory.
  • Provides asdict and astuple utility functions for easy conversion.

Example:

from dataclasses import dataclass
from typing import Dict, Optional, Tuple
from datetime import datetime

@dataclass
class Site:
    name: str
    gps_location: Tuple[float, float]
    metadata: Optional[Dict[str, str]] = None
    date_of_creation: datetime = datetime.now()

site = Site("Example Site", (40.7128, -74.0060), {"description": "An example site"})
print(site)

attrs:

  • Provides a more concise syntax and additional features.
  • Can be used with decorators or class decorators to generate special methods.
  • Offers a wide range of customization options, such as specifying custom validation functions, customizing generated methods, and providing default values.
  • Supports slots, which can improve performance and memory usage.
import attr
from typing import Dict, Optional, Tuple
from datetime import datetime

@attr.s
class Site:
    name: str = attr.ib()
    gps_location: Tuple[float, float] = attr.ib()
    metadata: Optional[Dict[str, str]] = attr.ib(default=None)
    date_of_creation: datetime = attr.ib(factory=datetime.now)

site = Site("Example Site", (40.7128, -74.0060), {"description": "An example site"})
print(site)

4.2. Performance

Python Data Classes:

  • Native to Python, so they offer good performance.
  • No built-in support for slots. Must be manually implemented for better memory usage.

attrs:

  • Claims to have better performance than Data Classes in certain scenarios.
  • Performance gains could be significant when using the slots=True option, which reduces memory overhead.

NOTE

slots is a feature that allows you to optimize memory usage and potentially improve the performance of your classes.

In Python, every object has a dynamic dictionary called __dict__ that stores the object's attributes. The dynamic nature of __dict__ allows you to add or remove attributes at runtime. However, this flexibility comes at the cost of increased memory overhead.

slots is a mechanism that allows you to define a fixed set of attributes for a class. When using slots, a more memory-efficient data structure is created for storing the object's attributes, instead of the default __dict__. This can lead to significant memory savings, especially when dealing with a large number of instances of a class.

To use slots with attrs, you can set the slots parameter to True when defining the class decorator. Here's an example:

import attr
from typing import List, Optional

@attr.s(slots=True)
class Person:
    name: str = attr.ib()
    age: int = attr.ib()
    email: Optional[str] = attr.ib(default=None)
    hobbies: List[str] = attr.ib(default=None)

4.3. Extensibility and Customization

Python Data Classes:

  • Limited customization options for generated methods.
  • Manual implementation is required for validation, conversion, and metadata support.

attrs:

  • Offers a wide range of customization options, such as specifying custom validation functions, customizing generated methods, and providing default values.
  • Supports slots, which can improve performance and memory usage.
  • Built-in support for validators, converters, and metadata.

4.4. Compatibility with Different Python Versions

Python Data Classes:

  • Available only in Python 3.7 and later versions.

attrs:

  • Compatible with Python 2.7 and Python 3.4+.

4.5. Type Safety

Python Data Classes:

Runtime Safety

  • Data Classes do not include built-in runtime validation for type safety.
  • Manual implementation of runtime checks in the __post_init__ method is required to ensure type safety at runtime.

Static Safety Checks

  • Offers seamless integration with Python's type hinting system, providing static type safety and improved developer experience.
  • Supports the use of TypeVar, NewType, and other typing constructs.
  • Compatible with static type checkers like mypy, which can catch type-related issues at development time.

Integration with Editors

  • Python Data Classes are natively supported in Python 3.7+ and are well-integrated with most Python editors and IDEs.
  • Type hints used in Data Classes can provide code completion, linting, and error checking, enhancing the development experience.

attrs:

Runtime Safety

  • attrs provides additional safety through built-in support for validators and converters, ensuring that the data conforms to the expected types and constraints at runtime.
  • Custom validation functions can be specified using the validator parameter in the attribute definition.

Static Safety Checks

  • attrs also integrates well with Python's type hinting system, providing static type safety and improved developer experience.
  • Can work with TypeVar, NewType, and other typing constructs.
  • Compatible with static type checkers like mypy.

Integration with Editors

  • As a third-party library, attrs may require additional configuration or plugins to enable full editor support.
  • However, once configured, most Python editors and IDEs can provide code completion, linting, and error checking for attrs classes, similar to Data Classes.

4.6. Code Examples

Python Data Classes:

from dataclasses import dataclass, field, InitVar
import sys

def validate_soil_id(value, name):
    valid_soils = ['clay', 'dirt', 'in-between']
    if value not in valid_soils:
        raise ValueError(f"{name} must be one of {', '.join(valid_soils)}")

@dataclass
class Site:
    name: str
    soil_id: str
    _valid_soil_id: InitVar = field(default=None, init=False)

    def __post_init__(self, _valid_soil_id):
        validate_soil_id(self.soil_id, "Soil ID")
        object.__setattr__(self, '__dict__', None)
        object.__setattr__(self, '__weakref__', None)

    def __setattr__(self, name, value):
        raise AttributeError("You cannot modify attributes in this class")

    def __delattr__(self, name):
        raise AttributeError("You cannot delete attributes in this class")

site = Site("Sample Site", "clay")
print(site)

attrs:

import attr

def validate_soil_id(instance, attribute, value):
    valid_soils = ['clay', 'dirt', 'in-between']
    if value not in valid_soils:
        raise ValueError(f"{attribute.name} must be one of {', '.join(valid_soils)}")

@attr.s(slots=True)
class Site:
    name: str = attr.ib()
    soil_id: str = attr.ib(validator=validate_soil_id)

site = Site("Sample Site", "clay")
print(site)

Feature Matrix

Feature Python Data Classes attrs
Built-in Python support Yes No
Runtime validation No (manual) Yes
Static type checking Yes Yes
Default values for attributes Yes Yes
Custom attribute converters No (manual) Yes
Support for slots Yes (manual) Yes
Flexible decorators/ordering Limited Yes
Readonly/Immutable attributes No (manual) Yes
Type hinting compatibility Yes Yes
Mypy and other type checkers Yes Yes
IDE/editor integration Native Plugin

References

  1. Python Data Classes documentation: https://docs.python.org/3/library/dataclasses.html
  2. attrs library documentation: https://www.attrs.org/en/stable/index.html
  3. PEP 557 - Data Classes: https://www.python.org/dev/peps/pep-0557/
  4. Python __slots__ magic: https://stackoverflow.com/questions/472000/usage-of-slots
  5. Type hinting in Python: https://docs.python.org/3/library/typing.html
  6. Mypy - Optional Static Typing for Python: https://mypy.readthedocs.io/en/stable/index.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment