web-dev-qa-db-fra.com

Classes de données vs typage. Cas d'utilisation principaux de NamedTuple

Longue histoire courte

PEP-557 a introduit les classes de données dans Python bibliothèque standard, qui peut essentiellement remplir le même rôle que collections.namedtuple et typing.NamedTuple. Et maintenant, je me demande comment séparer les cas d'utilisation dans lesquels namedtuple est toujours une meilleure solution.

Avantages des classes de données par rapport à NamedTuple

Bien sûr, tout le crédit va à dataclass si nous avons besoin de:

  • objets mutables
  • soutien à l'héritage
  • property décorateurs, attributs gérables
  • définitions de méthode générées immédiatement ou définitions de méthode personnalisables

Les avantages des classes de données sont brièvement expliqués dans le même PEP: Pourquoi ne pas simplement utiliser namedtuple .

Q: Dans quels cas nommé tuple est-il toujours un meilleur choix?

Mais que diriez-vous d'une question opposée pour les couples nommés: pourquoi ne pas simplement utiliser la classe de données? Je suppose que namedtuple est mieux du point de vue des performances, mais n'a encore trouvé aucune confirmation à ce sujet.

Exemple

Prenons la situation suivante:

Nous allons stocker les dimensions des pages dans un petit conteneur avec des champs définis statiquement, taper des indications et un accès nommé. Aucun autre hachage, comparaison, etc. n'est nécessaire.

Approche NamedTuple:

from typing import NamedTuple

PageDimensions = NamedTuple("PageDimensions", [('width', int), ('height', int)])

Approche DataClass:

from dataclasses import dataclass

@dataclass
class PageDimensions:
    width: int
    height: int

Quelle solution est préférable et pourquoi?

P.S. la question n'est pas un doublon de celui-là de quelque manière que ce soit, car ici je pose des questions sur les cas dans lesquels namedtuple est mieux, pas sur la différence (J'ai vérifié les documents et les sources avant de demander)

15
Oleh Rybalchenko

Cela dépend de vos besoins. Chacun d'eux a ses propres avantages.

Voici une bonne explication des Dataclasses sur PyCon 2018 Raymond Hettinger - Dataclasses: Le générateur de code pour terminer tous les générateurs de code

Dans Dataclass, toute implémentation est écrite en Python, comme dans Namedtuple, tous ces comportements sont gratuits car Namedtuple est hérité de Tuple. Et la structure de Tuple est écrite en C, c'est pourquoi les méthodes standard sont plus rapides dans Namedtuple (hachage, comparaison, etc.).

Mais Dataclass est basé sur dict comme Namedtuple basé sur Tuple. Selon cela, vous avez des avantages et des inconvénients à utiliser ces structures. Par exemple, l'utilisation de l'espace est plus petite dans NamedTuple, mais l'accès au temps est plus rapide dans Dataclass.

Veuillez voir mon expérience:

In [33]: a = PageDimensionsDC(width=10, height=10)

In [34]: sys.getsizeof(a) + sys.getsizeof(vars(a))
Out[34]: 168

In [35]: %timeit a.width
43.2 ns ± 1.05 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

In [36]: a = PageDimensionsNT(width=10, height=10)

In [37]: sys.getsizeof(a)
Out[37]: 64

In [38]: %timeit a.width
63.6 ns ± 1.33 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

Mais avec l'augmentation du nombre d'attributs du temps d'accès NamedTuple reste le même petit, car pour chaque attribut, il crée une propriété avec le nom de l'attribut. Par exemple, pour notre cas, la partie de l'espace de noms de la nouvelle classe ressemblera à:

from operator import itemgetter

class_namespace = {
...
    'width': property(itemgetter(0, doc="Alias for field number 0")),
    'height': property(itemgetter(0, doc="Alias for field number 1"))**
}

Dans quels cas nommé tuple est-il toujours un meilleur choix?

Lorsque votre structure de données doit/peut être immuable, lavable, itérable, déballable, comparable, vous pouvez utiliser NamedTuple. Si vous avez besoin de quelque chose de plus compliqué, par exemple, une possibilité d'héritage pour votre structure de données, utilisez alors Dataclass.

7

Dans la programmation en général, tout ce qui PEUT être immuable DEVRAIT être immuable. Nous gagnons deux choses:

  1. Plus facile à lire le programme - nous n'avons pas à nous soucier du changement des valeurs, une fois instancié, il ne changera jamais (namedtuple)
  2. Moins de risques de bugs étranges

C'est pourquoi, si les données sont immuables, vous devez utiliser un tuple nommé au lieu d'une classe de données

Je l'ai écrit dans le commentaire, mais je vais le mentionner ici: vous avez certainement raison qu'il y a un chevauchement, en particulier avec frozen=True dans les classes de données - mais il y a toujours des fonctionnalités telles que le déballage appartenant aux nommés et qui sont toujours immuables - je doute qu'ils suppriment les nommés en tant que tels

4
maor10