Python: единое целое число для класса данных?

#python #python-dataclasses

#питон #python-классы данных

Вопрос:

У меня есть следующий класс данных.

 @dataclass(frozen=True)
class myDataClass:
    x: float
    y: float
 

Я хочу, чтобы каждый раз, когда я создаю объект этого класса, он помечался уникальным идентификатором, который увеличивается с 0.

Итак, в первый раз я говорю first = myDataClass(0, 1) , что тогда я должен был first.id == 0 , а потом, если я говорю second = myDataClass(0, 1) , я должен получить second.id == 1 .

Ответ №1:

Потокобезопасный уникальный идентификатор может быть сгенерирован с помощью itertools.count() метода экземпляра __next__ в качестве default_factory для поля:

 from dataclasses import dataclass, field
from itertools import count

@dataclass(frozen=True)
class myDataClass:
    x: float
    y: float
    id: int = field(default_factory=count().__next__, init=False)
 

Это быстрее, чем другие варианты, не требует пользовательского кода и, по крайней мере, в ссылочном интерпретаторе CPython, потокобезопасно ( itertools.count реализовано на C и не освобождает GIL, поэтому нет шансов, что два экземпляра получат одинаковое id значение ).

Комментарии:

1. о-о-о, это действительно здорово.

Ответ №2:

Это немного сложно для замороженных классов данных, потому что любой код, который вы пишете, который динамически обновляет поле, сталкивается с FrozenInstanceError , но это, безусловно, возможно:

 from dataclasses import dataclass, field

@dataclass(frozen=True)
class myDataClass:
    x: float
    y: float
    id: int = field(init=False)

    def __post_init__(self):
        if not hasattr(myDataClass, "_COUNT"):
            myDataClass._COUNT = 0
        object.__setattr__(self, "id", myDataClass._COUNT)
        myDataClass._COUNT  = 1
 

Что должно работать так, как вы ожидали:

 >>> (myDataClass(1.0,2.0))
myDataClass(x=1.0, y=2.0, id=0)
>>> (myDataClass(1.0,2.0))
myDataClass(x=1.0, y=2.0, id=1)
>>> (myDataClass(1.0,2.0))
myDataClass(x=1.0, y=2.0, id=2)
>>> (myDataClass(1.0,2.0))
myDataClass(x=1.0, y=2.0, id=3)
 

Ответ №3:

Вы можете использовать default_factory для атрибутов классов данных, которые функционируют так, как вы хотите, и использовать переменную класса для отслеживания максимального используемого идентификатора.

 from dataclasses import dataclass, field
from typing import ClassVar


def _assign_id():
    new_id = myDataClass._next_id
    myDataClass._next_id  = 1
    return new_id


@dataclass(frozen=True)
class myDataClass:
    x: float
    y: float
    id: int = field(default_factory=_assign_id, init=False)
    _next_id: ClassVar[int] = 0