Heim  >  Artikel  >  Backend-Entwicklung  >  Python-Datenstruktur: ein unterschätztes Namedtuple (2)

Python-Datenstruktur: ein unterschätztes Namedtuple (2)

coldplay.xixi
coldplay.xixinach vorne
2020-10-20 17:10:303332Durchsuche

Die Spalte

Python-Video-Tutorial führt Sie weiterhin zum Verständnis der Namedtuple-Datenstruktur von Python.

Python-Datenstruktur: ein unterschätztes Namedtuple (2)

Teil 1Python-Datenstruktur: Ein unterschätztes Namedtuple (1)In diesem Artikel wird über einige grundlegende Verwendungsmöglichkeiten von Namedtuple gesprochen.

Was ist der Unterschied zwischen benannten Tupeln und Datenklassen?

Funktionen

Vor Python 3.7 konnte ein einfacher Datencontainer mit einer der folgenden Methoden erstellt werden:

  • namedtuple
  • Allgemeine Klassen
  • Bibliotheken von Drittanbietern, attrsattrs

如果您想使用常规类,那意味着您将必须实现几个方法。例如,常规类将需要一种__init__方法来在类实例化期间设置属性。如果您希望该类是可哈希的,则意味着自己实现一个__hash__方法。为了比较不同的对象,还需要__eq__实现一个方法。最后,为了简化调试,您需要一种__repr__方法。

让我们使用常规类来实现下我们的颜色用例。

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
        )复制代码

如上,你需要实现好多方法。您只需要一个容器来为您保存数据,而不必担心分散注意力的细节。同样,人们偏爱实现类的一个关键区别是常规类是可变的。

实际上,引入数据类(Data Class)的PEP将它们称为“具有默认值的可变namedtuple”(译者注:Data Class python 3.7引入,参考:docs.python.org/zh-cn/3/lib…

现在,让我们看看如何用数据类来实现。

from dataclasses import dataclass
...@dataclassclass Color:
    """A regular class that represents a color."""
    r: float
    g: float
    b: float
    alpha: float复制代码

哇!就是这么简单。由于没有__init__,您只需在docstring后面定义属性即可。此外,必须使用类型提示对其进行注释。

除了可变之外,数据类还可以开箱即用提供可选字段。假设我们的Color类不需要alpha字段。然后我们可以设置为可选。

from dataclasses import dataclassfrom typing import Optional
...@dataclassclass Color:
    """A regular class that represents a color."""
    r: float
    g: float
    b: float
    alpha: Optional[float]复制代码

我们可以像这样实例化它:

>>> blue = Color(r=0, g=0, b=255)复制代码

由于它们是可变的,因此我们可以更改所需的任何字段。我们可以像这样实例化它:

>>> blue = Color(r=0, g=0, b=255)
>>> blue.r = 1
>>> # 可以设置更多的属性字段
>>> blue.e = 10复制代码

相较之下,namedtuple默认情况下没有可选字段。要添加它们,我们需要一点技巧和一些元编程。

提示:要添加__hash__方法,您需要通过将设置unsafe_hash为使其不可变True

@dataclass(unsafe_hash=True)class Color:
    ...复制代码

另一个区别是,拆箱(unpacking)是namedtuples的自带的功能(first-class citizen)。如果希望数据类具有相同的行为,则必须实现自己。

from dataclasses import dataclass, astuple
...@dataclassclass Color:
    """A regular class that represents a color."""
    r: float
    g: float
    b: float
    alpha: float    def __iter__(self):
        yield from dataclasses.astuple(self)复制代码

性能比较

仅比较功能是不够的,namedtuple和数据类在性能上也有所不同。数据类基于纯Python实现dict。这使得它们在访问字段时更快。另一方面,namedtuples只是常规的扩展tuple。这意味着它们的实现基于更快的C代码并具有较小的内存占用量。

为了证明这一点,请考虑在Python 3.8.5上进行此实验。

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.r36.8 ns ± 0.109 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [12]: %timeit color_cls.r38.4 ns ± 0.112 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [15]: sys.getsizeof(color_tup)
Out[15]: 72In [16]: sys.getsizeof(color_cls) + sys.getsizeof(vars(color_cls))
Out[16]: 152复制代码

如上,数据类在中访问字段的速度稍快一些,但是它们比nametuple占用更多的内存空间。

如何将类型提示添加到 namedtuple

数据类默认使用类型提示。我们也可以将它们放在namedtuples上。通过导入Namedtuple注释类型并从中继承,我们可以对Color元组进行注释。

from typing import NamedTuple
...class Color(NamedTuple):
    """A namedtuple that represents a color."""
    r: float
    g: float
    b: float
    alpha: float复制代码

另一个可能未引起注意的细节是,这种方式还允许我们使用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.复制代码

如何将可选的默认值添加到 namedtuple

在上一节中,我们了解了数据类可以具有可选值。另外,我提到要模仿上的相同行为,namedtuple需要进行一些技巧修改操作。事实证明,我们可以使用继承,如下例所示。

from collections import namedtupleclass 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)复制代码

结论

元组是一个非常强大的数据结构。它们使我们的代码更清洁,更可靠。尽管与新的数据类竞争激烈,但他们仍有大量的场景可用。在本教程中,我们学习了使用namedtuples ul>

Wenn Sie eine reguläre Klasse verwenden möchten, müssen Sie mehrere Methoden implementieren. Beispielsweise würde eine reguläre Klasse eine __init__-Methode benötigen, um Eigenschaften während der Klasseninstanziierung festzulegen. Wenn Sie möchten, dass die Klasse hashbar ist, müssen Sie selbst eine __hash__-Methode implementieren. Um verschiedene Objekte vergleichen zu können, müssen Sie außerdem eine Methode __eq__ implementieren. Um das Debuggen zu vereinfachen, benötigen Sie schließlich eine __repr__-Methode.

Lassen Sie uns reguläre Klassen verwenden, um unseren Farbanwendungsfall zu implementieren. rrreeeWie oben müssen Sie viele Methoden implementieren. Sie benötigen lediglich einen Container, der Ihre Daten für Sie speichert, ohne sich um störende Details kümmern zu müssen. Auch hier besteht ein wesentlicher Unterschied, warum Menschen Implementierungsklassen bevorzugen, darin, dass reguläre Klassen veränderbar sind. Tatsächlich bezeichnet der PEP, der die Datenklasse eingeführt hat, sie als „veränderliches benanntes Tupel mit Standardwert“ (Anmerkung des Übersetzers: Datenklasse eingeführt in Python 3.7, Referenz: docs.python .org/zh- cn/3/lib…Jetzt sehen wir uns an, wie man es mit Datenklassen macht.

rrreee
Wow! Es ist so einfach, da es keinen __init__ gibt. Sie definieren das Attribut einfach nach dem Dokumentstring. Zusätzlich zur Veränderlichkeit kann die Klasse auch optionale Felder bereitstellen. Dann ist kein Alphafeld erforderlich Wir können es optional machen 🎜rrreee🎜 Wir können es so instanziieren: 🎜rrreee🎜 Da sie veränderbar sind, können wir alle gewünschten Felder ändern. Transformieren: 🎜rrreee🎜 Im Gegensatz dazu ist dies bei namedtuple nicht der Fall Um sie hinzuzufügen, benötigen wir einen kleinen Trick und etwas Metaprogrammierung. 🎜🎜Tipp: Um die Methode __hash hinzuzufügen, müssen Sie sie unveränderlich machen. code> zu True: 🎜rrreee🎜Ein weiterer Unterschied besteht darin, dass das Entpacken für die Namedtuples-Funktionalität automatisch erfolgt (First-Class-Bürger). Wenn Sie möchten, dass Datenklassen das gleiche Verhalten haben, Sie müssen es selbst implementieren 🎜rrreee

Nur die Vergleichsfunktion reicht nicht aus, benannte Tupel und Datenklassen unterscheiden sich auch in der Leistung dict, wodurch sie beim Zugriff auf Felder schneller werden. Die Implementierung basiert auf schnellerem C-Code und hat einen geringeren Speicherbedarf. Um dies zu beweisen, sollten Sie dieses Experiment auf Python 3.8.5 ausführen rrreee🎜Wie oben greift die Datenklasse etwas schneller zu, aber sie beanspruchen mehr Speicherplatz als Nametuple. 🎜

So fügen Sie Typhinweise zu Namedtuple hinzu Geben Sie Hinweise standardmäßig ein. Indem wir den Annotationstyp Namedtuple importieren und von ihm erben, können wir Farbtupel mit Anmerkungen versehen 🎜rrreee🎜 Ein weiteres Detail, das uns möglicherweise nicht aufgefallen ist Docstrings verwenden. Wenn wir help(Color) eingeben, können wir sie sehen. 🎜rrreee

So fügen Sie einem benannten Tupel einen optionalen Standardwert hinzu🎜🎜Im vorherigen Abschnitt haben wir gelernt, dass Datenklassen optionale Werte haben können. Außerdem habe ich erwähnt, dass zum Nachahmen desselben Verhaltens auf namedtuple einige knifflige Änderungen erforderlich sind. Es stellt sich heraus, dass wir die Vererbung wie im folgenden Beispiel verwenden können. 🎜rrreee

Fazit🎜🎜Tupel sind eine sehr leistungsfähige Datenstruktur. Sie machen unseren Code sauberer und zuverlässiger. Trotz der harten Konkurrenz mit den neuen Datenklassen steht ihnen immer noch eine große Anzahl von Szenarien zur Verfügung. In diesem Tutorial haben wir verschiedene Möglichkeiten zur Verwendung von namedtuples kennengelernt und hoffen, dass Sie diese verwenden können. 🎜🎜🎜🎜Verwandte kostenlose Lernempfehlungen: 🎜🎜🎜Python-Video-Tutorial🎜🎜🎜🎜

Das obige ist der detaillierte Inhalt vonPython-Datenstruktur: ein unterschätztes Namedtuple (2). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:juejin.im. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen