【问题标题】:How to get index of python3 dataclass instance?如何获取python3数据类实例的索引?
【发布时间】:2020-03-04 11:04:35
【问题描述】:

我想限制一个数据类实例的最大数量并知道该实例的索引。这是我想要的行为:

Veget('tomato', 2.4, 5)
Veget('salad', 3.5, 2)
Veget('carot', 1.2, 7)

for Veget in Veget.instances:
    print(Veget)
Veget(index=0, name='tomato', price=2.4, quantity=5)
Veget(index=1, name='salad', price=3.5, quantity=2)
Veget(index=2, name='carot', price=1.2, quantity=7)

我尝试了以下方法,它确实处理了创建限制:

from dataclasses import dataclass

MAX_COUNT = 3


class Limited:

    instances = []

    def __new__(cls, *_):
        if len(cls.instances) < MAX_COUNT:
            newobj = super().__new__(cls)
            cls.instances.append(newobj)
            return newobj
        else:
            raise RuntimeError('Too many instances')

@dataclass
class Veget(Limited):
    name: str
    price: float
    quantity: int

但打印时不会显示索引:

Veget(name='tomato', price=2.4, quantity=5)
Veget(name='salad', price=3.5, quantity=2)
Veget(name='carot', price=1.2, quantity=7)

【问题讨论】:

  • 继承并不是一个很好的解决方案,因为从Limited 继承的所有类都将共享相同的实例列表。我认为您想要一个设置各种类变量和新类的__new__ 方法的元类。
  • 附带说明,当您编写泛型用法__new__ 时,您应该将两个参数以及关键字参数传递给__init__(例如def __new__(cls, *_, **__))。如果你不这样做,继承类将不能再有关键字参数,这对数据类尤其不利。

标签: python-3.x python-dataclasses


【解决方案1】:

对数据类进行隐式限制或需要某种验证通常通过指定的__post_init__ 来实现,而不是使用对象继承。利用它的实现可能看起来像这样,在我看来这会更容易维护和理解:

from dataclasses import dataclass, field

MAX_COUNT = 3
VEGET_INDEX = []


@dataclass
class Veget:
    index: int = field(init=False)
    name: str
    price: float
    quantity: int

    def __post_init__(self):
        self.index = len(VEGET_INDEX)
        if self.index >= MAX_COUNT:
            raise RuntimeError("Too many instances")
        VEGET_INDEX.append(self)

您也可以使用计数器而不是在初始化后例程中递增的列表,但参考列表似乎便于调试。无论如何,创建三个允许的实例并尝试创建第四个看起来像这样:

>>> Veget('tomato', 2.4, 5)
Veget(index=0, name='tomato', price=2.4, quantity=5)
>>> Veget('salad', 3.5, 2)
Veget(index=1, name='salad', price=3.5, quantity=2)
>>> Veget('carot', 1.2, 7)
Veget(index=2, name='carot', price=1.2, quantity=7)
>>> Veget('potato', 0.7, 3)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<string>", line 5, in __init__
  File "<input>", line 17, in __post_init__
RuntimeError: Too many instances

【讨论】:

  • 我明白你的意思,但无论如何要在数据类中实现类属性?只是想看看它是怎么写的。
  • 是的,它在文档中列出了 here 如何做到这一点,here 是您采用这种风格的示例的要点。它介绍了 typing 模块和一个特殊的 hack dataclass 确实关于它,这就是为什么我没有在我的帖子中使用它。但是,如果它可以帮助您更好地组织代码,那就太好了。
  • 很高兴我能帮上忙 =)
【解决方案2】:

我们想修改实例化行为 因此,我们需要使用类方法创建本质上的构造函数。

from dataclasses import dataclass 

MAX_COUNT = 3
VEGET_INDEX = []

@dataclass 
class Veget:
    name : str 
    price : float 
    quantity : int
    

    @classmethod
    def create(cls, name, price, quantity):
        index = len(VEGET_INDEX)
        if index >= MAX_COUNT:
            raise RuntimeError("Too many instances")
        print('ok')
        the_new_thing = cls(name, price, quantity)
        VEGET_INDEX.append(the_new_thing)
        return the_new_thing


Veget.create('tomato', 2.4, 5)
Veget.create('salad', 3.5, 2)
Veget.create('carot', 1.2, 7)
Veget.create('potato', 0.7, 3)
ok
ok
ok
Traceback (most recent call last):
  File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 31, in <module>
    start(fakepyfile,mainpyfile)  File "/data/user/0/ru.iiec.pydroid3/files/accomp_files/iiec_run/iiec_run.py", line 30, in start
    exec(open(mainpyfile).read(),  __main__.__dict__)
  File "<string>", line 27, in <module>
  File "<string>", line 17, in create
RuntimeError: Too many instances
[Program finished]

根据 OP 的要求,我们可以打印索引,

from dataclasses import dataclass, field

MAX_COUNT = 3
VEGET_INDEX = []


@dataclass
class Veget:
    index: int = field(init=False)
    name: str
    price: float
    quantity: int

    def __post_init__(self):
        self.index = len(VEGET_INDEX)
        if self.index >= MAX_COUNT:
            raise RuntimeError("Too many instances")
        VEGET_INDEX.append(self)

Veget('tomato', 2.4, 5)
Veget('salad', 3.5, 2)
Veget('carot', 1.2, 7)
#Veget('potato', 0.7, 3)

for Veget in VEGET_INDEX :
    print(Veget)
Veget(index=0, name='tomato', price=2.4, quantity=5)
Veget(index=1, name='salad', price=3.5, quantity=2)
Veget(index=2, name='carot', price=1.2, quantity=7)

[Program finished] 

【讨论】:

    猜你喜欢
    • 2011-10-26
    • 2022-10-15
    • 2014-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多