[英]How does python know that dataclasses.field function is not a default value in a dataclass?
[英]Quickly locate an item in a dataclass containing a list of dataclasses by field value
我有一个具有这种结构的数据类:
from dataclasses import dataclass
from typing import List
@dataclass
class PartData:
id: int = 0
name: str = None
value: int = 0
@dataclass
class StockData:
stock_1: List[PartData] = None
stock_2: List[PartData] = None
def __getitem__(self, key):
return super().__getattribute__(key)
现在我创建数据类并用项目填充它们:
PARTS = [{"id": 1, "name": "screw"}, {"id": 3, "name": "bolt"}, {"id": 42, "name": "glue"}, {"id": 11, "name": "nail"}, {"id": 31, "name": "hammer"}, {"id": 142, "name": "paper"}]
dc_stock = StockData()
for p in PARTS:
dc_part = PartData()
dc_part.id = p["id"]
if dc_part.id % 2 == 0:
dc_stock_list = "stock_1"
else:
dc_stock_list = "stock_2"
if getattr(dc_stock, dc_stock_list) == None:
setattr(dc_stock, dc_stock_list, [dc_part])
else:
dc_stock[dc_stock_list].append(dc_part)
print(dc_stock)
# StockData(stock_1=[PartData(id=42, name=None, value=0), PartData(id=142, name=None, value=0)],
# stock_2=[PartData(id=1, name=None, value=0), PartData(id=3, name=None, value=0), PartData(id=11, name=None, value=0), PartData(id=31, name=None, value=0)])
我知道我可以遍历所有项目并比较它们,但是我可以定义一个方法,将part_id
作为参数,并可以用新value
更新dc_stock
中的任何part_id
吗? 这可以作为StockData
的一种方法来实现吗? 假设我不知道零件是在stock_1
还是stock_2
中。
编辑
为了更好地理解,我想分享我的方法,这对我来说看起来非常循环和昂贵:
@dataclass
class StockData:
stock_1: List[PartData] = None
stock_2: List[PartData] = None
def __getitem__(self, key):
return super().__getattribute__(key)
def update_part(self, id, value):
for stock_list in [f for f in fields(self) if f.name.startswith("stock")]:
stock = getattr(self, stock_list.name)
if len(stock) > 0:
for part in stock:
if part.id == id:
part.value = value
return None
print(dc_stock)
dc_stock.update_part(1, 10)
print(dc_stock)
这是设置它的一种方法。 如果您始终知道需要按id
进行查找,则可以改用 id 到零件的dict
映射,因为dict
查找比从列表中查找零件要快得多。 我还缓存了与股票相关的数据类字段列表,因为这也是一个好主意。
from dataclasses import dataclass, fields, field
from functools import cached_property
from typing import List, Dict, Union, Tuple
@dataclass
class PartData:
id: int = 0
name: str = None
value: int = 0
@dataclass
class StockData:
stock_1: Dict[int, PartData] = field(default_factory=dict)
stock_2: Dict[int, PartData] = field(default_factory=dict)
@cached_property
def stock_fields(self) -> Tuple[str, ...]:
return tuple(f.name for f in fields(self)
if f.name.startswith("stock"))
@classmethod
def from_parts(cls, parts: List[Dict[str, Union[str, int]]]):
"""Create a new `StockData` object from list of parts."""
stock = cls()
for p in parts:
part = PartData(**p)
if part.id % 2 == 0:
stock_list = 'stock_1'
else:
stock_list = 'stock_2'
getattr(stock, stock_list)[part.id] = part
return stock
def update_part(self, id, value):
"""Update value for a part, given the part id."""
for stock_field in self.stock_fields:
stock = getattr(self, stock_field)
if id in stock:
stock[id].value = value
return None
用法与您的使用方式非常相似。 我还添加了一个from_parts
辅助方法,因为它似乎是从零件列表构造StockData
实例的常见模式。 请注意,由于股票字段现在是字典,您可以访问.values()
以迭代每个股票中的PartData
项目。
def main():
PARTS = [{"id": 1, "name": "screw"}, {"id": 3, "name": "bolt"},
{"id": 42, "name": "glue"}, {"id": 11, "name": "nail"},
{"id": 31, "name": "hammer"}, {"id": 142, "name": "paper"}]
dc_stock = StockData.from_parts(PARTS)
assert dc_stock.stock_2[1].value == 0
print(dc_stock)
dc_stock.update_part(1, 10)
assert dc_stock.stock_2[1].value == 10
print(dc_stock)
print('Stock 1:')
print(list(dc_stock.stock_1.values()))
if __name__ == '__main__':
main()
你要求的是索引。
基本上,您的数据结构{<field value>: <items with this value>}
有一个dict
,它会在您更新数据时适当更新。
如果该字段是唯一的(因为项目 ID 应该是),那就更容易了:您只需要从一个键链接到 1 个项目,而不是链接到项目列表。
如您所见,保持索引最新是一项额外的工作,因此它只会让您超过一定的数据量; 数据写入和读取的频率也很重要(一旦索引查找开销变得比遍历整个表更快,索引会在更新时花费时间,但在 select 上节省时间超过特定数据大小)以及查询的百分比受益于指数。
首先,考虑不要重新发明轮子并使用像SQLAlchemy这样的 Pythonic ORM 而不是支持透明索引的数据类。 您无需运行数据库服务器即可从中受益,因为它也可以使用像 SQLite 这样的无服务器数据库作为后端。 此外,编译后端可能比纯 Python 快得多(数量级)。
将基于dict
的索引集成到数据结构中的方法是将其保存在表 class ( StockData
) 中,并命令表实例在写入任何索引字段时更新索引(包括首次初始化时) .
这是“最简单的方法”选项的示例:
class PartData:
<...>
_table: StockData
def __setitem__(self, key, new_value):
if key == 'id':
self._table.update_id_index(self, new_value, self.__getitem__(key))
super(self,PartData).__setitem__(self, key, new_value)
class StockData:
<...>
# assuming id is unique
id_index: {object: PartData} = {}
def update_id_index(self, record, new_value, old_value = None):
try: del self.id_index[old_value]
except KeyError: pass
self.id_index[new_value] = record
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.