你不能低估的Python數(shù)據(jù)結(jié)構(gòu)Namedtuple(二)

上篇你不能低估的Python數(shù)據(jù)結(jié)構(gòu)Namedtuple(一)講了namedtuple的一些基本用法,本篇繼續(xù)。

namedtuples和數(shù)據(jù)類(Data Class)之間有什么區(qū)別?

功能

在Python 3.7之前,可使用以下任一方法創(chuàng)建一個(gè)簡(jiǎn)單的數(shù)據(jù)容器:

namedtuple

常規(guī)類

第三方庫(kù),attrs

如果您想使用常規(guī)類,那意味著您將必須實(shí)現(xiàn)幾個(gè)方法。例如,常規(guī)類將需要一種__init__方法來(lái)在類實(shí)例化期間設(shè)置屬性。如果您希望該類是可哈希的,則意味著自己實(shí)現(xiàn)一個(gè)__hash__方法。為了比較不同的對(duì)象,還需要__eq__實(shí)現(xiàn)一個(gè)方法。最后,為了簡(jiǎn)化調(diào)試,您需要一種__repr__方法。

?

如果大家在學(xué)習(xí)中遇到困難,想找一個(gè)python學(xué)習(xí)交流環(huán)境,可以加入我們的python裙,裙號(hào)930900780,可領(lǐng)取python學(xué)習(xí)資料,會(huì)節(jié)約很多時(shí)間,減少很多遇到的難題。

讓我們使用常規(guī)類來(lái)實(shí)現(xiàn)下我們的顏色用例。

class Color:

? ? """A regular class that represents a color."""

? ? def __init__(self, r, g, b, alpha=0.0):

? ? ? ? self.r = r

? ? ? ? self.g = g

? ? ? ? self.b = b

? ? ? ? self.alpha = alpha

? ? def __hash__(self):

? ? ? ? return hash((self.r, self.g, self.b, self.alpha))

? ? def __repr__(self):

? ? ? ? return "{0}({1}, {2}, {3}, {4})".format(

? ? ? ? ? ? self.__class__.__name__, self.r, self.g, self.b, self.alpha

? ? ? ? )

? ? def __eq__(self, other):

? ? ? ? if not isinstance(other, Color):

? ? ? ? ? ? return False

? ? ? ? return (

? ? ? ? ? ? self.r == other.r

? ? ? ? ? ? and self.g == other.g

? ? ? ? ? ? and self.b == other.b

? ? ? ? ? ? and self.alpha == other.alpha

? ? ? ? )

復(fù)制代碼

如上,你需要實(shí)現(xiàn)好多方法。您只需要一個(gè)容器來(lái)為您保存數(shù)據(jù),而不必?fù)?dān)心分散注意力的細(xì)節(jié)。同樣,人們偏愛(ài)實(shí)現(xiàn)類的一個(gè)關(guān)鍵區(qū)別是常規(guī)類是可變的。

實(shí)際上,引入數(shù)據(jù)類(Data Class)的PEP將它們稱為“具有默認(rèn)值的可變namedtuple”(譯者注:Data Class python 3.7引入,參考:docs.python.org/zh-cn/3/lib…

現(xiàn)在,讓我們看看如何用數(shù)據(jù)類來(lái)實(shí)現(xiàn)。

from dataclasses import dataclass

...

@dataclass

class Color:

? ? """A regular class that represents a color."""

? ? r: float

? ? g: float

? ? b: float

? ? alpha: float

復(fù)制代碼

哇!就是這么簡(jiǎn)單。由于沒(méi)有__init__,您只需在docstring后面定義屬性即可。此外,必須使用類型提示對(duì)其進(jìn)行注釋。

?

除了可變之外,數(shù)據(jù)類還可以開(kāi)箱即用提供可選字段。假設(shè)我們的Color類不需要alpha字段。然后我們可以設(shè)置為可選。

from dataclasses import dataclass

from typing import Optional

...

@dataclass

class Color:

? ? """A regular class that represents a color."""

? ? r: float

? ? g: float

? ? b: float

? ? alpha: Optional[float]

復(fù)制代碼

我們可以像這樣實(shí)例化它:

>>> blue = Color(r=0, g=0, b=255)

復(fù)制代碼

由于它們是可變的,因此我們可以更改所需的任何字段。我們可以像這樣實(shí)例化它:

>>> blue = Color(r=0, g=0, b=255)

>>> blue.r = 1

>>> # 可以設(shè)置更多的屬性字段

>>> blue.e = 10

復(fù)制代碼

相較之下,namedtuple默認(rèn)情況下沒(méi)有可選字段。要添加它們,我們需要一點(diǎn)技巧和一些元編程。

?

提示:要添加__hash__方法,您需要通過(guò)將設(shè)置unsafe_hash為使其不可變True:

@dataclass(unsafe_hash=True)

class Color:

? ? ...

復(fù)制代碼

另一個(gè)區(qū)別是,拆箱(unpacking)是namedtuples的自帶的功能(first-class citizen)。如果希望數(shù)據(jù)類具有相同的行為,則必須實(shí)現(xiàn)自己。

from dataclasses import dataclass, astuple

...

@dataclass

class Color:

? ? """A regular class that represents a color."""

? ? r: float

? ? g: float

? ? b: float

? ? alpha: float

? ? def __iter__(self):

? ? ? ? yield from dataclasses.astuple(self)

復(fù)制代碼

性能比較

僅比較功能是不夠的,namedtuple和數(shù)據(jù)類在性能上也有所不同。數(shù)據(jù)類基于純Python實(shí)現(xiàn)dict。這使得它們?cè)谠L問(wèn)字段時(shí)更快。另一方面,namedtuples只是常規(guī)的擴(kuò)展tuple。這意味著它們的實(shí)現(xiàn)基于更快的C代碼并具有較小的內(nèi)存占用量。

為了證明這一點(diǎn),請(qǐng)考慮在Python 3.8.5上進(jìn)行此實(shí)驗(yàn)。

In [6]: import sys

In [7]: ColorTuple = namedtuple("Color", "r g b alpha")

In [8]: @dataclass

? ...: class ColorClass:

? ...:? ? """A regular class that represents a color."""

? ...:? ? r: float

? ...:? ? g: float

? ...:? ? b: float

? ...:? ? alpha: float

? ...:

In [9]: color_tup = ColorTuple(r=50, g=205, b=50, alpha=1.0)

In [10]: color_cls = ColorClass(r=50, g=205, b=50, alpha=1.0)

In [11]: %timeit color_tup.r

36.8 ns ± 0.109 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [12]: %timeit color_cls.r

38.4 ns ± 0.112 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [15]: sys.getsizeof(color_tup)

Out[15]: 72

In [16]: sys.getsizeof(color_cls) + sys.getsizeof(vars(color_cls))

Out[16]: 152

復(fù)制代碼

如上,數(shù)據(jù)類在中訪問(wèn)字段的速度稍快一些,但是它們比nametuple占用更多的內(nèi)存空間。

如何將類型提示添加到 namedtuple

數(shù)據(jù)類默認(rèn)使用類型提示。我們也可以將它們放在namedtuples上。通過(guò)導(dǎo)入Namedtuple注釋類型并從中繼承,我們可以對(duì)Color元組進(jìn)行注釋。

from typing import NamedTuple

...

class Color(NamedTuple):

? ? """A namedtuple that represents a color."""

? ? r: float

? ? g: float

? ? b: float

? ? alpha: float

復(fù)制代碼

另一個(gè)可能未引起注意的細(xì)節(jié)是,這種方式還允許我們使用docstring。如果輸入,help(Color)我們將能夠看到它們。

Help on class Color in module __main__:

class Color(builtins.tuple)

|? Color(r: float, g: float, b: float, alpha: Union[float, NoneType])

|?

|? A namedtuple that represents a color.

|?

|? Method resolution order:

|? ? ? Color

|? ? ? builtins.tuple

|? ? ? builtins.object

|?

|? Methods defined here:

|?

|? __getnewargs__(self)

|? ? ? Return self as a plain tuple.? Used by copy and pickle.

|?

|? __repr__(self)

|? ? ? Return a nicely formatted representation string

|?

|? _asdict(self)

|? ? ? Return a new dict which maps field names to their values.

復(fù)制代碼

如何將可選的默認(rèn)值添加到 namedtuple

在上一節(jié)中,我們了解了數(shù)據(jù)類可以具有可選值。另外,我提到要模仿上的相同行為,namedtuple需要進(jìn)行一些技巧修改操作。事實(shí)證明,我們可以使用繼承,如下例所示。

from collections import namedtuple

class Color(namedtuple("Color", "r g b alpha")):

? ? __slots__ = ()

? ? def __new__(cls, r, g, b, alpha=None):

? ? ? ? return super().__new__(cls, r, g, b, alpha)

>>> c = Color(r=0, g=0, b=0)

>>> c

Color(r=0, g=0, b=0, alpha=None)

復(fù)制代碼

結(jié)論

元組是一個(gè)非常強(qiáng)大的數(shù)據(jù)結(jié)構(gòu)。它們使我們的代碼更清潔,更可靠。盡管與新的數(shù)據(jù)類競(jìng)爭(zhēng)激烈,但他們?nèi)杂写罅康膱?chǎng)景可用。在本教程中,我們學(xué)習(xí)了使用namedtuples的幾種方法,希望您可以使用它們。

最后多說(shuō)一句,小編是一名python開(kāi)發(fā)工程師,這里有我自己整理的一套最新的python系統(tǒng)學(xué)習(xí)教程。想要這些資料的可以進(jìn)q裙930900780領(lǐng)取。

本文章素材來(lái)源于網(wǎng)絡(luò),如有侵權(quán)請(qǐng)聯(lián)系刪除。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容