簡介
從 Python3.7
版本開始鲁纠,引入了一個新的模塊 dataclasses
,該模塊主要提供了一種數(shù)據類的數(shù)據類的實現(xiàn)方式廷臼。基于 PEP-557實現(xiàn)绝页。 所謂數(shù)據類荠商,類似 Java
語言中的 Bean
。通過一個容器類(class)续誉,繼而使用對象的屬性訪問數(shù)據莱没。
如果你使用過標準庫中的 collections.namedtuple
, 或者 typing.NamedTuple
,dataclasses
是與這兩者類似的酷鸦。
通過 dataclasses
我們可以更加方便的去定義一個數(shù)據類饰躲。并且可以通過原生的方式進行類型檢查。
一個基礎例子:
@dataclass
class InventoryItem:
'''Class for keeping track of an item in inventory.'''
name: str
unit_price: float
quantity_on_hand: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity_on_hand
基礎用法
dataclasses
提供一個模塊級的裝飾器 dataclass
用來將類轉化為數(shù)據類臼隔。該裝飾器的原型定義如下:
@dataclasses.dataclass(*, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
提供的默認參數(shù)用來控制是否生成相應的魔術方法嘹裂。如 repr
為 True
時,將會自動生成 __repr__
方法躬翁。
我們定義一個簡單的數(shù)據類焦蘑,用以實現(xiàn)一個使用對象的屬性存儲實體 Person
數(shù)據:
@dataclasses.dataclass
class Person:
name: str
age: int = 20
該類中定義了兩個屬性 name
和 age
。分別表示名稱和年齡盒发,并且說明 name
屬性是一個字符串例嘱,age
屬性是一個數(shù)字(注意: 因為 Python
編譯器不會對此處的類型進行強制檢查)狡逢,并為 age
屬性設置了默認值 20
。
我們可以這樣去使用:
In [1]: person = Person('ikaros', 24)
In [2]: person.name
Out[2]: 'ikaros'
# 因為默認情況下 `repr` 是自動生成的拼卵,所以我們得到 `person` 的字符串表示奢浑。
In [3]: person
Out[3]: Person(name='ikaros', age=24)
通過使用 field
我們可以對參數(shù)做更多的定制化,如:
@dataclasses.dataclass
class Person:
name: str
age: int = dataclasses.field(default=20, repr=False)
此處我們?yōu)?age
屬性賦予了一個額外的 repr
為 False
的參數(shù)腋腮。該參數(shù)說明雀彼,在調用 __repr__
方法時,不展示 age
屬性:
In [4]: person
Out[4]: Person(name='ikaros')
更多的 field
說明即寡,可以查看 參考文檔徊哑。
實例說明
此處我們通過一個實際的例子展示 dataclasses
的用法.
現(xiàn)有一個數(shù)據實體內部的數(shù)據如下:
{
"id": "20531316728",
"about": "The Facebook Page celebrates how our friends inspire us, support us, and help us discover the world when we connect.",
"birthday": "02/04/2004",
"name": "Facebook",
"username": "facebookapp",
"fan_count": 214643503,
"cover": {
"cover_id": "10158913960541729",
"offset_x": 50,
"offset_y": 50,
"source": "https://scontent.xx.fbcdn.net/v/t1.0-9/s720x720/73087560_10158913960546729_8876113648821469184_o.jpg?_nc_cat=1&_nc_ohc=bAJ1yh0abN4AQkSOGhMpytya2quC_uS0j0BF-XEVlRlgwTfzkL_F0fojQ&_nc_ht=scontent.xx&oh=2964a1a64b6b474e64b06bdb568684da&oe=5E454425",
"id": "10158913960541729"
}
}
我們通過定義一個對應的數(shù)據類來表示該數(shù)據實體:
@dataclass
class Page:
id: str = None
about: str = field(default=None, repr=False)
birthday: str = field(default=None, repr=False)
name: str = None
username: str = None
fan_count: int = field(default=None, repr=False)
cover: dict = field(default=None, repr=False)
將數(shù)據傳入到數(shù)據類中:
# data 為 上述的數(shù)據
In [5]: p = Page(**data)
對數(shù)據進行操作:
# 獲取數(shù)據
In [6]: p.name
Out[6]: 'Facebook'
# 字符串展示
In [7]: p
Out[8]: Page(id='20531316728', name='Facebook', username='facebookapp')
In [9]: p.cover
Out[9]:
{'cover_id': '10158913960541729',
'offset_x': 50,
'offset_y': 50,
'source': 'https://scontent.xx.fbcdn.net/v/t1.0-9/s720x720/73087560_10158913960546729_8876113648821469184_o.jpg?_nc_cat=1&_nc_ohc=bAJ1yh0abN4AQkSOGhMpytya2quC_uS0j0BF-XEVlRlgwTfzkL_F0fojQ&_nc_ht=scontent.xx&oh=2964a1a64b6b474e64b06bdb568684da&oe=5E454425',
'id': '10158913960541729'}
上述完整代碼參見 demo1
我們在上述的代碼發(fā)現(xiàn), 在調用 p.cover
屬性時,返回的是一個字典聪富,在正常的使用時莺丑,我們是想將 cover
屬性也聲明為一個數(shù)據類。則需要對上述的代碼進行修改墩蔓。
添加一個 Cover
的數(shù)據類實現(xiàn):
@dataclass
class Cover:
id: str = None
cover_id: str = None
offset_x: str = field(default=None, repr=False)
offset_y: str = field(default=None, repr=False)
source: str = field(default=None, repr=False)
@dataclass
class Page:
... # 此處不再復制上方的屬性
cover: Cover = field(default=None, repr=False) # 修改 `cover` 屬性
但是這時候梢莽,如果我們按照剛才的初始化方式,cover
屬性不會被識別到奸披。
我們可以通過添加一個額外的初始化的方法用來初始化到 cover
屬性.
def dicts_to_dataclasses(instance):
"""將所有的數(shù)據類屬性都轉化到數(shù)據類中"""
cls = type(instance)
for f in fields(cls):
if not is_dataclass(f.type):
continue
value = getattr(instance, f.name)
if not isinstance(value, dict):
continue
new_value = f.type(**value)
setattr(instance, f.name, new_value)
并且修改上層數(shù)據類 Page
的代碼昏名,添加一個 __post_init__
方法, 該方法會被自動生成的 __init__
方法調用阵面,進而將 Cover
數(shù)據類進行初始化轻局。
@dataclass
class Page:
... # 上方的屬性
def __post_init__(self):
dicts_to_dataclasses(self)
上述完整代碼參見 demo2
此時我們去初始化時,便可以將子數(shù)據類 Cover
也初始化了膜钓。
In [10]: p.cover
Out[10]: Cover(id='10158913960541729', cover_id='10158913960541729')
此外嗽交,dataclasses
還提供了對數(shù)據類到字典的轉化。
In [11]: from dataclasses import asdict
In [12]: asdict(p)
Out[12]:
{'id': '20531316728',
....
}
我們可以對上邊的代碼進行整合一下颂斜。將通用的一些函數(shù)放到一個 base
基類中。
完整代碼參見 demo3
第三方增強庫
上邊我們只是對含有嵌套字典的復雜數(shù)據進行了處理拾枣。事實上沃疮,生產中的數(shù)據的樣式會更加復雜。我們根據需求自行對 dicts_to_dataclasses
函數(shù)進行升級處理梅肤,或者使用第三方庫進行處理司蔬。
此處我們以第三方庫 dataclasses-json
來給出一個示例,詳細代碼參見 demo-with-dataclasses-json