Pythonデータ構造(2):Namedtupleが過小評価されている

Pythonビデオチュートリアルの列には、Pythonデータ構造のnamedtupleが引き続き表示されます。

Pythonデータ構造の最後の部分:過小評価されているNamedtuple(1)は、namedtupleの基本的な使用法について説明しましたが、この記事は続きます。

名前付きタプルとデータクラスの違いは何ですか?
関数Python3.7
より前では、次のいずれかの方法を使用して、単純なデータコンテナを作成できます。

namedtuple
レギュラークラス
サードパーティライブラリ、attrs
レギュラークラスを使用する場合は、いくつかのメソッドを実装する必要があることを意味します。たとえば、通常のクラスでは、クラスのインスタンス化中に属性を設定するために__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

        )复制代码

上記のように、多くのメソッドを実装する必要があります。詳細を気にすることなく、データを保持するためのコンテナのみが必要です。同様に、実装クラスに対する人々の好みの主な違いは、通常のクラスは変更可能であるということです。

実際、データクラスを導入したPEPは、それらを「デフォルト値を持つ変数namedtuples」と呼んでいました(翻訳者注:データクラス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クラスにアルファフィールドが必要ないとします。次に、それをオプションとして設定できます。

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:

    ...复制代码

もう1つの違いは、開梱は名前付きタプルの第一級市民であるということです。データクラスに同じ動作を持たせたい場合は、自分で実装する必要があります。

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

パフォーマンスの比較
関数だけを比較するだけでは不十分であり、名前付きタプルとデータクラスもパフォーマンスが異なります。データクラスは、純粋なPythonに基づいてdictを実装します。これにより、フィールドにアクセスする際の速度が向上します。一方、namedtupleは、通常の拡張タプルです。これは、それらの実装がより高速なCコードに基づいており、メモリフットプリントが小さいことを意味します。

この点を証明するために、Python3.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よりも多くのメモリスペースを消費します。

名前付きtuple
データクラスに型ヒントを追加する方法は、デフォルトで型ヒントを使用します。名前付きのタプルに配置することもできます。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.复制代码

指定されたtupleにオプションのデフォルト値を追加する方法
前のセクションでは、データクラスがオプションの値を持つことができることを学びました。さらに、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を使用するいくつかの方法を学び、それらを使用できることを願っています。
この記事は、php中国語のWebサイトあるPythonビデオチュートリアルからのものです:https//www.php.cn/course/list/30.html

おすすめ

転載: blog.csdn.net/Anna_xuan/article/details/109472190