Python中的數(shù)據(jù)類(lèi)dataclass解讀
1. 為什么需要數(shù)據(jù)類(lèi)
1.1 ??內(nèi)置數(shù)據(jù)類(lèi)型的局限
假設(shè)我們現(xiàn)在遇到一個(gè)場(chǎng)景, 需要一個(gè)數(shù)據(jù)對(duì)象來(lái)保存一些運(yùn)動(dòng)員信息.
可以選擇使用基本的數(shù)據(jù)類(lèi)型tuple或者dict實(shí)現(xiàn). 如:創(chuàng)建一個(gè)球員jordan, 信息包括球員姓名, 號(hào)碼, 位置, 年齡.
使用tuple
In [1]: jordan = ('Micheal Jordan', 23, 'PG', 29) In [2]: jordan[0] Out[2]: 'Micheal Jordan'
劣勢(shì):創(chuàng)建和取值基于位置, 需要記住坐標(biāo)對(duì)應(yīng)的信息.
使用dict
In [3]: jordan = {'name': 'Micheal Jordan', 'number': 23, 'position': 'PG', 'age': 29} In [4]: jordan['position'] Out[4]: 'PG'
使用字典之后, 獲取信息時(shí)會(huì)相對(duì)直觀(guān), 但是相較于字典的括號(hào)語(yǔ)法 jordan["position"] 我們更希望可以用類(lèi)似獲取屬性一樣使用 jordan.postion.
劣勢(shì):無(wú)法對(duì)數(shù)據(jù)屬性名進(jìn)行控制,
少值或者錯(cuò)值,如, jordan = {'name': 'Micheal Jordan', 'NUMBER': 23} 一樣可以創(chuàng)建成功.
1.2 ??使用命名元組 namedtuple
為了解決這種問(wèn)題, python 中的 collections 模塊提供一個(gè)命名元組, 可以使用點(diǎn)表示法和字段名稱(chēng)訪(fǎng)問(wèn)給定命名元組中的值.
使用namedtuple代碼如下
In [5]: from collections import namedtuple In [6]: Player = namedtuple('Player', ['name', 'number', 'position', 'age', 'grade']) In [7]: jordan = Player('Micheal Jordan', 23, 'PG', 29, 'S+') In [8]: jordan Out[8]: Player(name='Micheal Jordan', number=23, position='PG', age=29, grade='S+')
使用namedtuple之后
① 可以使用 '.'語(yǔ)法獲取數(shù)據(jù)的屬性, 可以限制數(shù)據(jù)的屬性名稱(chēng),
② 創(chuàng)建對(duì)象時(shí)數(shù)據(jù)不匹配會(huì)報(bào)錯(cuò).
In [9]: jordan.number Out[9]: 23 In [10]: bryant = Player('Kobe Bryant', 24, 'PG') --------------------------------------------------------------------------- TypeError: __new__() missing 2 required positional arguments: 'age' and 'grade'
1.2.1 namedtuple的不足
對(duì)于一些字段比較少的數(shù)據(jù)結(jié)構(gòu), namedtuple是一個(gè)非常好的解決方案.
但面對(duì)一些復(fù)雜的數(shù)據(jù)的時(shí)候, 需要更多的功能時(shí), namedtuple就無(wú)法滿(mǎn)足了.
In [11]: bryant = Player('Kobe Bryant', 24, 'PG', 22, 'S') In [12]: bryant.age=23 --------------------------------------------------------------------------- AttributeError: can't set attribute
劣勢(shì):
① 數(shù)據(jù)無(wú)法修改
② 無(wú)法自定義數(shù)據(jù)比較, 沒(méi)有默認(rèn)值, 沒(méi)有函數(shù)支持.
1.3 自定義類(lèi) Class
為了支持?jǐn)?shù)據(jù)修改, 默認(rèn)值, 比較等功能. 更加好一些的方法是, 使用自定義類(lèi)來(lái)實(shí)現(xiàn)數(shù)據(jù)類(lèi).
一個(gè)最簡(jiǎn)單的數(shù)據(jù)類(lèi)代碼如下:
In [13]: class Player: ...: def __init__(self, name, number, position, age, grade): ...: self.name = name ...: self.number = number ...: self.position = position ...: self.age = age ...: self.grade = grade In [14]: bryant = Player(name='Kobe Bryant', number=24, position='PG', age=22, grade='S') In [15]: jordan = Player('Micheal Jordan', 23, 'PG', 29, 'S+')
可以使用位置參數(shù)或者鍵值參數(shù)創(chuàng)建對(duì)象
In [16]: bryant.position='SF' In [17]: bryant.position Out[17]: 'SF'
可以看到, 數(shù)據(jù)類(lèi)可以支持對(duì)屬性的修改,
In [18]: bryant Out[18]: <__main__.Player at 0x29446d401c8>
問(wèn)題①:目前的實(shí)現(xiàn) 對(duì)于對(duì)象的描述不太友好,
In [19]: jordan > bryant --------------------------------------------------------------------------- TypeError: '>' not supported between instances of 'Player' and 'Player'
問(wèn)題②:數(shù)據(jù)還不支持比較.
為了解決上面兩個(gè)問(wèn)題,可以通過(guò)實(shí)現(xiàn) __repr__ 方法來(lái)自定義描述, 實(shí)現(xiàn) __gt__ 方法來(lái)支持比較的功能.
更新代碼如下:
In [20]: class Player: ...: def __init__(self, name, number, position, age, grade): ...: self.name = name ...: self.number = number ...: self.position = position ...: self.age = age ...: self.grade = grade ...: def __repr__(self): ...: return f'Player: \n {self.name}\t #{self.number}\t @{self.position}\t <{self.grade}>' ...: def __eq__(self, other): ...: return self.age == other.age ...: def __gt__(self, other): ...: return self.age > other.age ...: def swing(self, pos): ...: self.position = pos In [21]: jordan = Player('Micheal Jordan', 23, 'PG', 29, 'S+') In [22]: bryant = Player('Kobe Bryant', 24, 'PG', 22, 'S') In [23]: jordan Out[23]: Player: Micheal Jordan #23 @PG <S+> In [24]: jordan > bryant Out[24]: True In [25]: jordan.swing('SF') In [26]: jordan Out[26]: Player: Micheal Jordan #23 @SF <S+>
可以看到數(shù)據(jù)對(duì)象有了更直觀(guān)的描述, 支持了對(duì)比 (若要支持 >= 的對(duì)比, 還需要自定義 __ge__方法). 還可以自定義方法swing來(lái)改變球員打的位置.
劣勢(shì):
① __init__方法中重復(fù)代碼 (示例中每個(gè)屬性都需要寫(xiě)3遍)
② 需要自己實(shí)現(xiàn)__repr__方法, 和比較方法__eq__, __gt__等
1.4 ??數(shù)據(jù)類(lèi) dataclass
主角出場(chǎng)了, 數(shù)據(jù)類(lèi)是Python3.7 開(kāi)始引入的一個(gè)新功能, 數(shù)據(jù)類(lèi)提供了開(kāi)箱即用的方法來(lái)創(chuàng)建自定義數(shù)據(jù), 可以直接實(shí)例化、打印和比較數(shù)據(jù)類(lèi)實(shí)例.
In [1]: from dataclasses import dataclass In [2]: @dataclass ...: class Player: ...: name: str ...: number: int ...: position: str ...: age: int ...: grade: str In [3]: james = Player('Lebron James', 23, 'SF', 25, 'S') In [4]: james Out[4]: Player(name='Lebron James', number=23, position='SF', age=25, grade='S')
2. dataclass 的使用
2.1 類(lèi)型提示和默認(rèn)值
dataclass 可以認(rèn)為是提供了一個(gè)簡(jiǎn)寫(xiě)__init__方法的語(yǔ)法糖.
類(lèi)型注釋是必填項(xiàng) (不限制數(shù)據(jù)類(lèi)型時(shí), 添加typing.Any為類(lèi)型注釋), 默認(rèn)值的傳遞方式和__init__方法的參數(shù)格式一致.
In [1]: from dataclasses import dataclass In [2]: from typing import Any In [3]: @dataclass ...: class Data: ...: name: Any ...: value: Any = 42
2.2 數(shù)據(jù)嵌套
數(shù)據(jù)類(lèi)可以嵌套為其他數(shù)據(jù)類(lèi)的字段, 可以簡(jiǎn)單創(chuàng)建一個(gè)有2個(gè)隊(duì)員的球隊(duì).lal包含兩名球員 james和davis
In [1]: from dataclasses import dataclass In [2]: from typing import List In [3]: @dataclass ...: class Player: ...: name: str ...: number: int ...: position: str ...: age: int ...: grade: str In [4]: @dataclass ...: class Team: ...: name: str ...: players: List[Player] In [5]: james = Player('Lebron James', 23, 'SF', 25, 'S') In [6]: davis = Player('Anthony Davis', 3, 'PF', 21, 'S-') In [7]: lal = Team('Los Angeles Lakers', [james, davis]) In [8]: lal Out[8]: Team(name='Los Angeles Lakers', players=[Player(name='Lebron James', number=23, position='SF', age=25, grade='S'), Player(name='Anthony Davis', number=3, position='PF', age=21, grade='S-')])
2.3 dataclasses中的field
當(dāng)我們嘗試使用可變的數(shù)據(jù)類(lèi)型, 給數(shù)據(jù)類(lèi)中做默認(rèn)值時(shí), 觸發(fā)了python中的大坑之一 使用可變默認(rèn)參數(shù), 導(dǎo)致多個(gè)實(shí)例公用一個(gè)數(shù)據(jù)從而引發(fā)bug.
dataclass 默認(rèn)阻止使用可變數(shù)據(jù)做默認(rèn)值
In [9]: @dataclass ...: class Team: ...: name: str ...: players: List[Player] = [james] --------------------------------------------------------------------------- ValueError: mutable default <class 'list'> for field players is not allowed: use default_factory
就像錯(cuò)誤提示中的, 處理此種場(chǎng)景時(shí), 需要使用 field 中的 default_factory .
In [10]: from dataclasses import field In [11]: @dataclass ...: class Team: ...: name: str ...: players: List[Player] = field(default_factory=lambda :[james]) In [12]: nyk = Team('New York Knicks') In [13]: nyk Out[13]: Team(name='New York Knicks', players=[Player(name='Lebron James', number=23, position='SF', age=25, grade='S')])
參數(shù) | 描述 | 默認(rèn)值 |
default | 字段的默認(rèn)值 | |
default_factory | 返回字段初始值的函數(shù) | |
init | 是否在.__init__()方法中使用字段 | True |
repr | 是否在.__repr__()方法中使用字段 | True |
compare | 是否在比較對(duì)象時(shí), 包括該字段 | True |
hash | 計(jì)算hash時(shí), 是否包括字段 | True |
metadata | 包含字段信息的映射 |
2.4 不可變數(shù)據(jù)類(lèi)
要使數(shù)據(jù)類(lèi)不可變,需要在創(chuàng)建類(lèi)時(shí)設(shè)置frozen=True。
In [1]: from dataclasses import dataclass In [2]: from typing import Any In [3]: @dataclass(frozen=True) ...: class Data: ...: name: Any ...: value: Any = 42 In [4]: data = Data('myname', 99) In [4]: data.name = 'other' --------------------------------------------------------------------------- FrozenInstanceError: cannot assign to field 'name'
總結(jié)
dataclass 提供一個(gè)簡(jiǎn)便的方式創(chuàng)建數(shù)據(jù)類(lèi), 默認(rèn)實(shí)現(xiàn)__init__(), __repr__(), __eq__()方法.
dataclass支持?jǐn)?shù)據(jù)類(lèi)型的嵌套
支持將數(shù)據(jù)設(shè)置為不可變
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
在Django model中設(shè)置多個(gè)字段聯(lián)合唯一約束的實(shí)例
今天小編就為大家分享一篇在Django model中設(shè)置多個(gè)字段聯(lián)合唯一約束的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-07-07python驗(yàn)證碼識(shí)別教程之滑動(dòng)驗(yàn)證碼
這篇文章主要給大家介紹了關(guān)于python驗(yàn)證碼識(shí)別教程之滑動(dòng)驗(yàn)證碼的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-06-06Python 實(shí)現(xiàn)try重新執(zhí)行
今天小編就為大家分享一篇Python 實(shí)現(xiàn)try重新執(zhí)行,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-12-12python tkinter之頂層菜單、彈出菜單實(shí)例
這篇文章主要介紹了python tkinter之頂層菜單、彈出菜單實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03利用python中集合的唯一性實(shí)現(xiàn)去重
集合,用{ }表示,集合中所有元素具有唯一性。這篇文章給大家介紹利用python中集合的唯一性實(shí)現(xiàn)去重,感興趣的朋友一起看看吧2020-02-02