Search code examples
pythonattributesattr

Creating a unique id in a python dataclass


I need a unique (unsigned int) id for my python data class. This is very similar to this so post, but without explicit ctors.

import attr
from attrs import field
from itertools import count
@attr.s(auto_attribs=True)
class Person:
    #: each Person has a unique id
    _counter: count[int] = field(init=False, default=count())
    _unique_id: int = field(init=False)

    @_unique_id.default
    def _initialize_unique_id(self) -> int:
        return next(self._counter)

Is there any more-"pythonic" solution?


Solution

  • Use a default factory instead of just a default. This allows to define a call to get the next id on each instantiation.
    A simple means to get a callable that counts up is to use count().__next__, the equivalent of calling next(...) on a count instance.1

    The common "no explicit ctor" libraries attr and dataclasses both support this:

    from itertools import count
    from dataclasses import dataclass, field
    
    @dataclass
    class C:
        identifier: int = field(default_factory=count().__next__)
    
    import attr
    
    @attr.s
    class C:
        identifier: int = attr.field(factory=count().__next__)
    

    To always use the automatically generated value and prevent passing one in as a parameter, use init=False.

    @dataclass
    class C:
        identifier: int = field(default_factory=count().__next__, init=False)
    

    1 If one wants to avoid explicitly addressing magic methods, one can use a closure over a count. For example, factory=lambda counter=count(): next(counter).