web-dev-qa-db-fra.com

Utilisation de la classe Python comme conteneur de données

Parfois, il est logique de regrouper les données liées. J'ai tendance à le faire avec un dicton, par exemple,

self.group = dict(a=1, b=2, c=3)
print self.group['a']

Un de mes collègues préfère créer une classe

class groupClass(object):
    def __init__(a, b, c):
        self.a = a
        self.b = b
        self.c = c
self.group = groupClass(1, 2, 3)
print self.group.a

Notez que nous ne définissons aucune méthode de classe.

J'aime utiliser un dict car j'aime minimiser le nombre de lignes de code. Mon collègue pense que le code est plus lisible si vous utilisez une classe et qu'il sera plus facile d'ajouter des méthodes à la classe à l'avenir.

Lequel tu préfères, et pourquoi?

47
gaefan

Si vous ne définissez vraiment aucune méthode de classe, un dict ou un namedtuple a beaucoup plus de sens, à mon avis. Simple + intégré c'est bien! À chacun, cependant.

43
Joe Kington

Contexte

R. Hettinger a présenté un résumé des conteneurs de données alternatifs basés sur des attributs lors de la réunion de vacances 2017 de SF Python. Voir son Tweet et son jeu de diapositives . Il a également donné un talk à PyCon 2018 sur les dataclasses.

D'autres types de conteneurs de données sont mentionnés dans ce article et principalement dans Python 3 (voir les liens ci-dessous).

Voici une discussion sur la liste de diffusion python-ideas sur l'ajout de recordclass à la bibliothèque standard.

Options

Alternatives dans la bibliothèque standard

Options externes

  • records : mutable namedtuple (voir aussi recordclass )
  • grappes : ajouter l'accès aux attributs aux dict (inspiration pour SimpleNamedspace)
  • box : envelopper les dict avec recherche de style point fonctionnalité
  • attrdict : accéder aux éléments d'un mappage en tant que clés ou attributs
  • champs : supprimer le passe-partout des classes de conteneurs.
  • namedlist : conteneurs mutables de type Tuple avec des valeurs par défaut d'E. Smith

Lequel?

Le choix de l'option à utiliser dépend de la situation (voir les exemples ci-dessous). Habituellement, un dictionnaire mutable à l'ancienne ou un tuple nommé immuable est assez bon. Les classes de données sont le plus récent ajout (Python 3.7a) offrant à la fois la mutabilité et immuabilité en option , avec la promesse d'une réduction du nombre de points d'inspiration inspirée par le projet attrs .


Exemples

import typing as typ
import collections as ct
import dataclasses as dc


# Problem: You want a simple container to hold personal data.
# Solution: Try a NamedTuple.
>>> class Person(typ.NamedTuple):
...     name: str
...     age: int
>>> a = Person("bob", 30)
>>> a
Person(name='bob', age=30)
# Problem: You need to change age each year, but namedtuples are immutable. 
# Solution: Use assignable attributes of a traditional class.
>>> class Person:
...     def __init__(self, name, age):
...         self.name = name
...         self.age = age
>>> b = Person("bob", 30)
>>> b.age = 31
>>> b
<__main__.Person at 0x4e27128>
# Problem: You lost the pretty repr and want to add comparison features.
# Solution: Use included repr and eq features from the new dataclasses.
>>> @dc.dataclass(eq=True)
... class Person:
...     name: str
...     age: int
>>> c = Person("bob", 30)
>>> c.age = 31
>>> c
Person(name='bob', age=31)
>>> d = Person("dan", 31)
>>> c != d
True
26
pylang

Je préfère suivre YAGNI et utiliser un dict.

8

Il y a une nouvelle proposition qui vise à implémenter exactement ce que vous cherchez, appelée classes de données . Jetez un coup d'oeil.

Utiliser une classe plutôt qu'un dict est une question de préférence. Personnellement, je préfère utiliser un dict lorsque les clés ne sont pas connues a priori. (En tant que conteneur de mappage).

L'utilisation d'une classe pour stocker des données signifie que vous pouvez fournir de la documentation sur les attributs de classe.

Personnellement, la principale raison pour laquelle j'utilise une classe est peut-être d'utiliser la fonction de saisie automatique des IDE! (techniquement une raison boiteuse, mais très utile dans la pratique)

7
nesdis

Soit dit en passant, je pense que Python 3.7 implémenté @ dataclass est le moyen le plus simple et le plus efficace pour implémenter des classes en tant que conteneurs de données.

@dataclass
class Data:
    a: list
    b: str    #default variables go after non default variables
    c: bool = False

def func():
    return A(a="hello")

print(func())

La sortie serait: hello

Elle est trop similaire à Scala comme classe de cas et la façon la plus simple d'utiliser une classe comme conteneur.

5
james.bondu

Votre chemin est meilleur. N'essayez pas d'anticiper trop l'avenir car vous ne réussirez probablement pas.

Cependant, il peut parfois être judicieux d'utiliser quelque chose comme une structure C , par exemple si vous souhaitez identifier différents types plutôt que d'utiliser des dict pour tout.

5
Muhammad Alkarouri

Vous pouvez combiner ensemble les avantages de dict et class, en utilisant une classe wrapper héritée de dict. Vous n'avez pas besoin d'écrire du code passe-partout, et en même temps, vous pouvez utiliser la notation par points.

class ObjDict(dict):
    def __getattr__(self,attr):
        return self[attr]
    def __setattr__(self,attr,value):
        self[attr]=value

self.group = ObjDict(a=1, b=2, c=3)
print self.group.a
3
agdk26

Je ne suis pas d'accord que le code est plus lisible en utilisant une classe sans méthodes. Vous attendez généralement des fonctionnalités d'une classe, pas seulement des données.

Donc, j'irais pour un dict jusqu'à ce que le besoin de fonctionnalité se fasse sentir, puis le constructeur de la classe pourrait recevoir un dict :-)

2
Vinko Vrsalovic

Qu'en est-il de Prodict :

group = Prodict(a=1, b=2, c=3)
group.d = 4

Et si vous voulez que la conversion de type automatique et le code automatique soient complets (intelli-sense):

class Person(Prodict):
    name: str
    email: str
    rate: int

john = Person(name='John', email='[email protected]')
john.rate = 7
john.age = 35  # dynamic
1
Ramazan Polat

Dans une langue qui le prend en charge, j'utiliserais un struct. Un dictionnaire serait le plus proche d'une structure en Python, du moins pour autant que je le vois.

Sans oublier, vous pouvez quand même ajouter une méthode à un dictionnaire si vous vraiment le vouliez;)

1
Brian S

Un dicton est évidemment approprié pour cette situation. Il a été conçu spécifiquement pour ce cas d'utilisation. À moins que vous n'utilisiez réellement la classe en tant que classe, il ne sert à rien de réinventer la roue et de surcharger/gaspiller l'espace supplémentaire d'une classe qui agit comme un mauvais dictionnaire (pas de fonctionnalités de dictionnaire).

1
jeffcook2150

Si l'on ne se soucie pas de l'empreinte mémoire, alors dict , namedtuple , dataclass ou juste une classe avec __slots__ Sont de bons choix.

Mais si l'on doit créer des millions d'objets avec quelques attributs, il existe une solution basée sur la bibliothèque recordclass :

from recordclass import make_dataclass
C = make_dataclass("C", ('a', 'b', 'c'))
c = C(1, 2, 3)

Même chose avec une définition de classe:

from recordclass import dataobject
class C(dataobject):
    a:int
    b:int
    c:int    
c = C(1, 2, 3)

Il a une empreinte mémoire minimale = sizeof(PyObject_HEAD) + 3*sizeof(PyObject*) octets.

Pour comparaison, les variantes basées sur __slots__ Nécessitent sizeof(PyGC_Head) + sizeof(PyObject_HEAD) + 3*sizeof(PyObject*) octets.

0
intellimath