数据容器类的通用接口
项目描述
itemadapter
ItemAdapter
类是对数据容器对象的包装,提供了一种通用的接口,以统一的方式处理不同类型的数据对象,无论它们的底层实现如何。
目前支持的类型包括
scrapy.item.Item
dict
dataclass
基类attrs
基类pydantic
基类的类(当前尚不支持pydantic>=2
)
此外,还支持与任意类型交互,通过实现预定义的接口(参见 扩展 itemadapter
)。
要求
- Python 3.8+
scrapy
:可选,用于与scrapy
项目交互attrs
:可选,用于与基于attrs
的项目交互pydantic
:可选,用于与基于pydantic
的项目交互(当前尚不支持pydantic>=2
)
安装
itemadapter
可在 PyPI
上找到,可以使用 pip
安装
pip install itemadapter
许可
itemadapter
采用 BSD-3 许可证分发。
基本用法
以下是一个使用 dataclass
对象的简单示例。考虑以下类型定义
>>> from dataclasses import dataclass
>>> from itemadapter import ItemAdapter
>>> @dataclass
... class InventoryItem:
... name: str
... price: float
... stock: int
>>>
可以像字典一样处理 ItemAdapter
对象
>>> obj = InventoryItem(name='foo', price=20.5, stock=10)
>>> ItemAdapter.is_item(obj)
True
>>> adapter = ItemAdapter(obj)
>>> len(adapter)
3
>>> adapter["name"]
'foo'
>>> adapter.get("price")
20.5
>>>
包装的对象将被就地修改
>>> adapter["name"] = "bar"
>>> adapter.update({"price": 12.7, "stock": 9})
>>> adapter.item
InventoryItem(name='bar', price=12.7, stock=9)
>>> adapter.item is obj
True
>>>
转换为字典
ItemAdapter
类提供了 asdict
方法,该方法递归地将嵌套项目转换为字典。考虑以下示例
>>> from dataclasses import dataclass
>>> from itemadapter import ItemAdapter
>>> @dataclass
... class Price:
... value: int
... currency: str
>>> @dataclass
... class Product:
... name: str
... price: Price
>>>
>>> item = Product("Stuff", Price(42, "UYU"))
>>> adapter = ItemAdapter(item)
>>> adapter.asdict()
{'name': 'Stuff', 'price': {'value': 42, 'currency': 'UYU'}}
>>>
请注意,仅将适配器对象传递给内置的 dict
也可以,但它不会递归地遍历对象并将嵌套项目转换为字典
>>> dict(adapter)
{'name': 'Stuff', 'price': Price(value=42, currency='UYU')}
>>>
API 参考
内置适配器
以下适配器是默认包含的
itemadapter.adapter.ScrapyItemAdapter
:处理Scrapy
项目itemadapter.adapter.DictAdapter
:处理Python
字典itemadapter.adapter.DataclassAdapter
:处理dataclass
对象itemadapter.adapter.AttrsAdapter
:处理attrs
对象itemadapter.adapter.PydanticAdapter
:处理pydantic
对象
类 itemadapter.adapter.ItemAdapter(item: Any)
这是包的主要入口点。通常,用户代码使用此类包装一个项目,并使用提供的接口处理它。ItemAdapter
实现了 MutableMapping
接口,提供了一个类似 dict
的 API 来操作它所包装的对象的数据(该对象将被就地修改)。
属性
类属性 ADAPTER_CLASSES: Iterable
存储当前已注册的适配器类。
适配器注册的顺序很重要。为特定项目创建 ItemAdapter
对象时,将按顺序遍历已注册的适配器,并使用第一个适配器类(对于其 is_item
类方法的 item
参数返回 True
的类)执行后续操作。默认顺序在 内置适配器 部分中定义。
默认实现使用 collections.deque
支持高效地向两端添加/删除适配器类,但如果您正在派生子类(有关更多信息,请参见 扩展 itemadapter 部分),则任何其他可迭代对象(例如 list
、tuple
)都将有效。
方法
类方法 is_item(item: Any) -> bool
如果任何已注册的适配器可以处理项目(即,如果其中任何一个返回 True
对于其 is_item
方法与 item
作为参数),则返回 True
,否则返回 False
。
类方法 is_item_class(item_class: type) -> bool
如果任何已注册的适配器可以处理项目类(即,如果其中任何一个返回 True
对于其 is_item_class
方法与 item_class
作为参数),则返回 True
,否则返回 False
。
类方法 get_field_meta_from_class(item_class: type, field_name: str) -> MappingProxyType
返回一个 types.MappingProxyType
对象,该对象是一个只读映射,包含有关给定字段的元数据。如果项目类不支持字段元数据,或者没有给定字段的元数据,则返回一个空对象。
返回值取自以下来源,具体取决于项目类型
scrapy.item.Field
用于scrapy.item.Item
dataclasses.field.metadata
用于基于dataclass
的项目attr.Attribute.metadata
用于基于attrs
的项目pydantic.fields.FieldInfo
用于基于pydantic
的项目
类方法 get_field_names_from_class(item_class: type) -> Optional[list[str]]
返回一个包含为项目类定义的所有字段名称的列表。如果项目类不支持预先定义字段,则返回 None。
get_field_meta(field_name: str) -> MappingProxyType
如果可用,则返回给定字段的元数据。除非在自定义适配器类中重写,否则默认情况下此方法调用适配器的 get_field_meta_from_class
方法,并传递包装项目的类。
field_names() -> collections.abc.KeysView
返回一个包含为项目定义的所有字段名称的 keys view。
asdict() -> dict
返回一个包含适配器内容的 dict
对象。这比调用 dict(adapter)
略微不同,因为它递归地应用于嵌套项目(如果有的话)。
函数 itemadapter.utils.is_item(obj: Any) -> bool
如果给定的对象属于(至少)支持的类型之一,则返回 True
,否则返回 False
。这是一个别名,建议使用 itemadapter.adapter.ItemAdapter.is_item
类方法以获得更好的性能。
函数 itemadapter.utils.get_field_meta_from_class(item_class: type, field_name: str) -> types.MappingProxyType
是 itemadapter.adapter.ItemAdapter.get_field_meta_from_class
的别名
元数据支持
scrapy.item.Item
、dataclass
、attrs
和 pydantic
对象允许定义任意字段元数据。这可以通过一个 MappingProxyType
对象访问,该对象可以通过使用 itemadapter.adapter.ItemAdapter.get_field_meta
从项目实例中检索,或者使用 itemadapter.adapter.ItemAdapter.get_field_meta_from_class
方法(或其别名 itemadapter.utils.get_field_meta_from_class
)从项目类中检索。数据源取决于底层类型(请参阅 ItemAdapter.get_field_meta_from_class
的文档)。
scrapy.item.Item
对象
>>> from scrapy.item import Item, Field
>>> from itemadapter import ItemAdapter
>>> class InventoryItem(Item):
... name = Field(serializer=str)
... value = Field(serializer=int, limit=100)
...
>>> adapter = ItemAdapter(InventoryItem(name="foo", value=10))
>>> adapter.get_field_meta("name")
mappingproxy({'serializer': <class 'str'>})
>>> adapter.get_field_meta("value")
mappingproxy({'serializer': <class 'int'>, 'limit': 100})
>>>
dataclass
对象
>>> from dataclasses import dataclass, field
>>> @dataclass
... class InventoryItem:
... name: str = field(metadata={"serializer": str})
... value: int = field(metadata={"serializer": int, "limit": 100})
...
>>> adapter = ItemAdapter(InventoryItem(name="foo", value=10))
>>> adapter.get_field_meta("name")
mappingproxy({'serializer': <class 'str'>})
>>> adapter.get_field_meta("value")
mappingproxy({'serializer': <class 'int'>, 'limit': 100})
>>>
attrs
对象
>>> import attr
>>> @attr.s
... class InventoryItem:
... name = attr.ib(metadata={"serializer": str})
... value = attr.ib(metadata={"serializer": int, "limit": 100})
...
>>> adapter = ItemAdapter(InventoryItem(name="foo", value=10))
>>> adapter.get_field_meta("name")
mappingproxy({'serializer': <class 'str'>})
>>> adapter.get_field_meta("value")
mappingproxy({'serializer': <class 'int'>, 'limit': 100})
>>>
pydantic
对象
>>> from pydantic import BaseModel, Field
>>> class InventoryItem(BaseModel):
... name: str = Field(serializer=str)
... value: int = Field(serializer=int, limit=100)
...
>>> adapter = ItemAdapter(InventoryItem(name="foo", value=10))
>>> adapter.get_field_meta("name")
mappingproxy({'serializer': <class 'str'>})
>>> adapter.get_field_meta("value")
mappingproxy({'serializer': <class 'int'>, 'limit': 100})
>>>
扩展 itemadapter
此软件包允许通过实现适配器接口来处理任意项目类
类 itemadapter.adapter.AdapterInterface(item: Any)
适配器的抽象基类。处理特定类型项目的适配器必须继承此类并实现其上定义的抽象方法。AdapterInterface
继承自 collections.abc.MutableMapping
,因此必须实现 MutableMapping
接口的所有方法。
-
类方法
is_item_class(cls, item_class: type) -> bool
如果适配器可以处理给定的项目类,则返回
True
,否则返回False
。抽象(强制)。 -
类方法
is_item(cls, item: Any) -> bool
如果适配器可以处理给定的项目,则返回
True
,否则返回False
。默认实现调用cls.is_item_class(item.__class__)
。 -
类方法
get_field_meta_from_class(cls, item_class: type) -> bool
返回给定项目类和字段名的元数据(如果有的话)。默认情况下,此方法返回一个空的
MappingProxyType
对象。如果您想根据自定义逻辑处理字段元数据,请提供自己的方法定义。有关更多信息,请参阅元数据支持部分。 -
方法
get_field_meta(self, field_name: str) -> types.MappingProxyType
返回给定字段名的元数据(如果有的话)。通常无需重写此方法,因为基类
itemadapter.adapter.AdapterInterface
提供了一个默认实现,该实现使用包装项目的类作为参数调用ItemAdapter.get_field_meta_from_class
。有关更多信息,请参阅元数据支持部分。 -
方法
field_names(self) -> collections.abc.KeysView
:返回项目字段名的动态视图。默认情况下,此方法返回对当前适配器调用
keys()
的结果,即它的返回值取决于MutableMapping
接口(更具体地说,它取决于__iter__
的返回值)。如果您想获取项目所有字段(无论是否已填充),则可能需要重写此方法。例如,Scrapy 使用此方法在导出项目到 CSV 时定义列名。
注册适配器
将您的自定义适配器类添加到 itemadapter.adapter.ItemAdapter.ADAPTER_CLASSES
类属性中,以处理自定义项目类。
示例
>>> from itemadapter.adapter import ItemAdapter
>>> from tests.test_interface import BaseFakeItemAdapter, FakeItemClass
>>>
>>> ItemAdapter.ADAPTER_CLASSES.appendleft(BaseFakeItemAdapter)
>>> item = FakeItemClass()
>>> adapter = ItemAdapter(item)
>>> adapter
<ItemAdapter for FakeItemClass()>
>>>
多个适配器类
如果您需要针对不同情况有不同的处理程序和/或优先级,可以继承 ItemAdapter
类并设置所需的 ADAPTER_CLASSES
属性。
示例
>>> from itemadapter.adapter import (
... ItemAdapter,
... AttrsAdapter,
... DataclassAdapter,
... DictAdapter,
... PydanticAdapter,
... ScrapyItemAdapter,
... )
>>> from scrapy.item import Item, Field
>>>
>>> class BuiltinTypesItemAdapter(ItemAdapter):
... ADAPTER_CLASSES = [DictAdapter, DataclassAdapter]
...
>>> class ThirdPartyTypesItemAdapter(ItemAdapter):
... ADAPTER_CLASSES = [AttrsAdapter, PydanticAdapter, ScrapyItemAdapter]
...
>>> class ScrapyItem(Item):
... foo = Field()
...
>>> BuiltinTypesItemAdapter.is_item(dict())
True
>>> ThirdPartyTypesItemAdapter.is_item(dict())
False
>>> BuiltinTypesItemAdapter.is_item(ScrapyItem(foo="bar"))
False
>>> ThirdPartyTypesItemAdapter.is_item(ScrapyItem(foo="bar"))
True
>>>
更多示例
scrapy.item.Item
对象
>>> from scrapy.item import Item, Field
>>> from itemadapter import ItemAdapter
>>> class InventoryItem(Item):
... name = Field()
... price = Field()
...
>>> item = InventoryItem(name="foo", price=10)
>>> adapter = ItemAdapter(item)
>>> adapter.item is item
True
>>> adapter["name"]
'foo'
>>> adapter["name"] = "bar"
>>> adapter["price"] = 5
>>> item
{'name': 'bar', 'price': 5}
>>>
dict
>>> from itemadapter import ItemAdapter
>>> item = dict(name="foo", price=10)
>>> adapter = ItemAdapter(item)
>>> adapter.item is item
True
>>> adapter["name"]
'foo'
>>> adapter["name"] = "bar"
>>> adapter["price"] = 5
>>> item
{'name': 'bar', 'price': 5}
>>>
dataclass
对象
>>> from dataclasses import dataclass
>>> from itemadapter import ItemAdapter
>>> @dataclass
... class InventoryItem:
... name: str
... price: int
...
>>> item = InventoryItem(name="foo", price=10)
>>> adapter = ItemAdapter(item)
>>> adapter.item is item
True
>>> adapter["name"]
'foo'
>>> adapter["name"] = "bar"
>>> adapter["price"] = 5
>>> item
InventoryItem(name='bar', price=5)
>>>
attrs
对象
>>> import attr
>>> from itemadapter import ItemAdapter
>>> @attr.s
... class InventoryItem:
... name = attr.ib()
... price = attr.ib()
...
>>> item = InventoryItem(name="foo", price=10)
>>> adapter = ItemAdapter(item)
>>> adapter.item is item
True
>>> adapter["name"]
'foo'
>>> adapter["name"] = "bar"
>>> adapter["price"] = 5
>>> item
InventoryItem(name='bar', price=5)
>>>
pydantic
对象
>>> from pydantic import BaseModel
>>> from itemadapter import ItemAdapter
>>> class InventoryItem(BaseModel):
... name: str
... price: int
...
>>> item = InventoryItem(name="foo", price=10)
>>> adapter = ItemAdapter(item)
>>> adapter.item is item
True
>>> adapter["name"]
'foo'
>>> adapter["name"] = "bar"
>>> adapter["price"] = 5
>>> item
InventoryItem(name='bar', price=5)
>>>
变更日志
请参阅完整变更日志
项目详细信息
下载文件
下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装软件包的信息。