Quand utiliser un dictionnaire vs tuple en Python

31

L'exemple spécifique à l'esprit est une liste de noms de fichiers et leurs tailles. Je ne peux pas décider si chaque élément de la liste doit être de la forme {"filename": "blabla", "size": 123}ou simplement ("blabla", 123). Un dictionnaire me semble plus logique car accéder à la taille, par exemple, file["size"]est plus explicatif que file[1]... mais je n'en suis pas vraiment sûr. Pensées?

clb
la source
En complément, envisagez le déballage du tuple , si vous vous inquiétez de la lisibilité des tuples - fname, file_size = file, où les données sont votre tuple ci-dessus, supprimez-le file[1]et remplacez-le par file_size. Bien sûr, cela repose sur une bonne documentation.
nlsdfnbch
2
Cela dépend de la structure de données que vous créez et comment comptez-vous y accéder? (par nom de fichier? par index? les deux?) S'agit-il simplement d'une variable / structure de données jetable, ou allez-vous éventuellement ajouter d'autres éléments (/ attributs) ainsi que la taille? La structure doit-elle se souvenir d'une commande; voulez-vous trier la liste des tailles, ou y accéder par position (par exemple "top-n plus / plus petits fichiers")? Selon ceux-ci, la «meilleure» réponse pourrait être dict, OrderedDict, namedtuple, plain old list ou une classe personnalisée de votre choix. Besoin de plus de contexte de votre part.
smci

Réponses:

78

J'utiliserais un namedtuple:

from collections import namedtuple
Filesize = namedtuple('Filesize', 'filename size')
file = Filesize(filename="blabla", size=123)

Maintenant, vous pouvez utiliser file.sizeet file.filenamedans votre programme, qui est à mon humble avis la forme la plus lisible. Remarque namedtuplecrée des objets immuables comme les tuples, et ils sont plus légers que les dictionnaires, comme décrit ici .

Doc Brown
la source
1
Merci, bonne idée, jamais entendu parler d'eux avant aujourd'hui (je suis plutôt novice chez Python). Question: que se passe-t-il si quelqu'un ailleurs dans le code définit également la même "classe", peut-être légèrement différemment. par exemple, dans un autre fichier source, le collègue Bob avaitFilesize = namedtuple('Filesize', 'filepath kilobytes')
user949300
Vous pouvez également utiliser le attrsmodule très agréable (peut le trouver pipou simplement le rechercher), qui vous permet d'avoir des commodités syntaxiques très similaires au tuple nommé, mais peut vous donner la mutabilité (mais peut également être rendu immuable). La principale différence fonctionnelle est que les attrsclasses faites ne sont pas comparables aux tuples simples, comme namedtuplele font les s.
mtraceur
3
@DocBrown Python n'a aucun concept de déclarations. class, defEt =tout remplacer simplement les utilisations précédentes. repl.it
Challenger5
@ Challenger5: vous avez raison, mon erreur, donc la bonne réponse est: la dernière définition compte, aucune erreur du runtime Python, mais un comportement toujours similaire à celui de toute autre variable.
Doc Brown
8
Notez qu'il namedtuples'agit essentiellement d'une déclaration abrégée pour un nouveau type avec des attributs immuables. Cela signifie que la réponse est effectivement "Ni un tupleni un dict, mais un object." +1
jpmc26
18

{"nom de fichier": "blabla", "taille": 123}, ou tout simplement ("blabla", 123)

C'est la question séculaire de savoir si encoder votre format / schéma en bande ou hors bande.

Vous échangez de la mémoire pour obtenir la lisibilité et la portabilité qui découlent de l'expression du format des données directement dans les données. Si vous ne le faites pas, sachez que le premier champ est le nom du fichier et le second la taille doit être conservé ailleurs. Cela économise de la mémoire mais cela coûte lisibilité et portabilité. Qu'est-ce qui va coûter plus cher à votre entreprise?

Quant à la question immuable, rappelez-vous immuable ne signifie pas inutile face au changement. Cela signifie que nous devons récupérer plus de mémoire, effectuer la modification dans une copie et utiliser la nouvelle copie. Ce n'est pas gratuit, mais ce n'est souvent pas une rupture. Nous utilisons des chaînes immuables pour changer les choses tout le temps.

Une autre considération est l'extensibilité. Lorsque vous stockez des données uniquement de manière positionnelle, sans coder les informations de format, vous êtes alors condamné à un seul héritage, ce qui n'est rien d'autre que la pratique de concaténer des champs supplémentaires après les champs établis. Je peux définir un 3ème champ pour être la date de création et toujours être compatible avec votre format puisque je définis d'abord la seconde et la même manière.

Cependant, ce que je ne peux pas faire, c'est rassembler deux formats définis indépendamment qui ont des champs qui se chevauchent, d'autres pas, les stocker dans un format et les utiliser pour des choses qui ne connaissent que l'un ou l'autre des formats.

Pour ce faire, je dois encoder les informations de format depuis le début. Je dois dire "ce champ est le nom du fichier". Cela permet un héritage multiple.

Vous êtes probablement habitué à ce que l'héritage ne s'exprime que dans le contexte des objets, mais les mêmes idées fonctionnent pour les formats de données car, eh bien, les objets sont stockés dans des formats de données. C'est exactement le même problème.

Donc, utilisez celui que vous pensez avoir le plus besoin. J'atteins la flexibilité à moins que je puisse indiquer une bonne raison de ne pas le faire.

candied_orange
la source
3
Pour être honnête, je doute que quiconque n'est pas sûr d'utiliser un format intrabande ou hors bande ait des exigences de performance si strictes qu'il lui faudrait utiliser un format hors bande
Alexander - Reinstate Monica
2
@Alexander très vrai. Je préfère enseigner aux gens à ce sujet afin qu'ils comprennent ce qu'ils regardent lorsqu'ils sont confrontés à des solutions hors bande. Les formats binaires le font souvent pour des raisons d'obscurcissement. Tout le monde ne veut pas être portable. En ce qui concerne les performances, si cela est vraiment important, pensez à la compression avant de recourir à la bande hors bande.
candied_orange
N'oubliez pas que OP utilise Python, ils ne sont donc probablement pas trop préoccupés par les performances. La plupart du code de haut niveau doit d'abord être écrit avec la lisibilité à l'esprit; L'optimisation prématurée est la racine de tout Mal.
Dagrooms
@Dagrooms ne déteste pas Python. Il fonctionne bien dans de nombreux cas. Mais sinon, je suis d'accord avec tout ce que vous avez dit. Mon point était de dire "C'est pourquoi les gens font ça. Voici pourquoi vous ne vous en souciez probablement pas".
candied_orange
@CandiedOrange Je ne déteste pas la langue, je l'utilise dans mon travail quotidien. Je n'aime pas la façon dont les gens l'utilisent.
Dagrooms
7

J'utiliserais une classe avec deux propriétés. file.sizeest plus agréable que l'un file[1]ou l' autre file["size"].

Simple, c'est mieux que complexe.

JacquesB
la source
Au cas où quelqu'un se demanderait: Pour générer des JSON, les deux fonctionnent aussi bien: file = Filesize(filename='stuff.txt', size=222)et les filetup = ("stuff.txt", 222)deux génèrent le même JSON: json.dumps(file)et json.dumps(filetup)entraînent:'["stuff.txt", 222]'
Juha Untinen
5

Les noms de fichiers sont-ils uniques? Si tel est le cas, vous pouvez supprimer entièrement la liste et utiliser simplement un dictionnaire pur pour tous les fichiers. par exemple (un site Web hypothétique)

{ 
  "/index.html" : 5467,
  "/about.html" : 3425,
  "/css/main.css" : 9876
}

etc...

Maintenant, vous n'obtenez pas "nom" et "taille", vous utilisez simplement la clé et la valeur, mais souvent c'est plus naturel. YMMV.

Si vous voulez vraiment une "taille" pour plus de clarté, ou si vous avez besoin de plus d'une valeur pour le fichier, alors:

{ 
   "/index.html" : { "size": 5467, "mime_type" : "foo" },
   "/about.html" : { "size": 3425, "mime_type" : "foo" }
   "/css/main.css" : { "size": 9876, "mime_type" : "bar" }
}
user949300
la source
0

En python, le dictionnaire est un objet mutable. De l'autre côté, le tuple est un objet immuable.

si vous devez changer la clé du dictionnaire, paire de valeurs souvent ou à chaque fois. je suggère le dictionnaire à utiliser.

si vous avez des données fixes / statiques, je suggère d'utiliser le tuple.

# dictionary define.
a = {}
a['test'] = 'first value'

# tuple define.
b = ()
b = b+(1,)

# here, we can change dictionary value for key 'test'
a['test'] = 'second'

Mais, impossible de modifier les données de tuple à l'aide de l'opérateur d'affectation.

Approfondir Patel
la source