web-dev-qa-db-fra.com

Pourquoi avons-nous besoin de tuples dans Python (ou tout type de données immuable)?

J'ai lu plusieurs python tutoriels (Dive Into Python, pour un), et la référence du langage sur Python.org - je ne vois pas pourquoi le langage a besoin de tuples.

Les tuples n'ont aucune méthode par rapport à une liste ou un ensemble, et si je dois convertir un tuple en un ensemble ou une liste pour pouvoir les trier, quel est l'intérêt d'utiliser un tuple en premier lieu?

Immutabilité?

Pourquoi quiconque se soucie-t-il si une variable vit à un endroit différent de la mémoire par rapport à sa répartition initiale? Toute cette affaire d'immuabilité en Python semble être surestimée.

En C/C++ si j'alloue un pointeur et pointe vers une mémoire valide, peu m'importe où l'adresse est située tant qu'elle n'est pas nulle avant de l'utiliser.

Chaque fois que je référence cette variable, je n'ai pas besoin de savoir si le pointeur pointe toujours vers l'adresse d'origine ou non. Je vérifie simplement null et l'utilise (ou non).

En Python, lorsque j'alloue une chaîne (ou un tuple), l'affecte à x, puis modifie la chaîne, pourquoi est-ce que je me soucie si c'est l'objet d'origine? Tant que la variable pointe vers mes données, c'est tout ce qui compte.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

x fait toujours référence aux données que je veux, pourquoi quelqu'un doit-il se soucier si son identifiant est identique ou différent?

136
pyNewGuy
  1. les objets immuables peuvent permettre une optimisation substantielle; c'est probablement pourquoi les chaînes sont également immuables en Java, développées assez séparément mais à peu près en même temps que Python, et à peu près tout est immuable dans les langages vraiment fonctionnels.

  2. dans Python en particulier, seuls les immuables peuvent être hachables (et, par conséquent, les membres d'ensembles ou de clés dans les dictionnaires). Encore une fois, cela permet une optimisation, mais bien plus que "substantielle" (conception des tables de hachage décentes stockant des objets complètement mutables sont un cauchemar - soit vous prenez des copies de tout dès que vous le hachez, soit le cauchemar de vérifier si le hachage de l'objet a changé depuis la dernière fois que vous y avez fait référence fait dresser sa tête laide).

Exemple de problème d'optimisation:

$ python -mtimeit '["fee", "fie", "fo", "fum"]'
1000000 loops, best of 3: 0.432 usec per loop
$ python -mtimeit '("fee", "fie", "fo", "fum")'
10000000 loops, best of 3: 0.0563 usec per loop
118
Alex Martelli

Aucune des réponses ci-dessus n'indique le vrai problème des tuples vs listes, que beaucoup de nouveaux utilisateurs de Python ne semblent pas comprendre complètement.

Les tuples et les listes ont des objectifs différents. Les listes stockent des données homogènes. Vous pouvez et devez avoir une liste comme celle-ci:

["Bob", "Joe", "John", "Sam"]

L'utilisation correcte des listes est due au fait que ce sont tous des types de données homogènes, en particulier les noms des personnes. Mais prenez une liste comme celle-ci:

["Billy", "Bob", "Joe", 42]

Cette liste est le nom complet d'une personne et son âge. Ce n'est pas un type de données. La bonne façon de stocker ces informations est soit dans un tuple, soit dans un objet. Disons que nous en avons quelques-uns:

[("Billy", "Bob", "Joe", 42), ("Robert", "", "Smith", 31)]

L'immuabilité et la mutabilité des tuples et des listes ne sont pas la principale différence. Une liste est une liste du même type d'éléments: fichiers, noms, objets. Les tuples sont un regroupement de différents types d'objets. Ils ont des utilisations différentes, et de nombreux codeurs Python abusent des listes pour ce à quoi les tuples sont destinés.

S'il vous plait, ne le faites pas.


Modifier:

Je pense que ce billet de blog explique pourquoi je pense cela mieux que moi: http://news.e-scribe.com/397

40
Grant Paul

si je dois convertir un Tuple en un ensemble ou une liste pour pouvoir les trier, quel est l'intérêt d'utiliser un Tuple en premier lieu?

Dans ce cas particulier, il n'y a probablement pas de point. Ce n'est pas un problème, car ce n'est pas l'un des cas où vous envisagez d'utiliser un tuple.

Comme vous le faites remarquer, les tuples sont immuables. Les raisons d'avoir des types immuables s'appliquent aux tuples:

  • efficacité de copie: plutôt que de copier un objet immuable, vous pouvez l'aliaser (lier une variable à une référence)
  • efficacité de la comparaison: lorsque vous utilisez la copie par référence, vous pouvez comparer deux variables en comparant l'emplacement, plutôt que le contenu
  • interning: vous devez stocker au plus une copie de toute valeur immuable
  • il n'est pas nécessaire de synchroniser l'accès aux objets immuables dans le code simultané
  • exactitude const: certaines valeurs ne devraient pas être autorisées à changer. C'est (pour moi) la principale raison des types immuables.

Notez qu'une implémentation particulière de Python peut ne pas utiliser toutes les fonctionnalités ci-dessus.

Les clés du dictionnaire doivent être immuables, sinon la modification des propriétés d'un objet clé peut invalider les invariants de la structure de données sous-jacente. Les tuples peuvent ainsi potentiellement être utilisés comme clés. Ceci est une conséquence de l'exactitude constante.

Voir aussi " Introducing tuples ", from Dive Into Python .

22
outis

Parfois, nous aimons utiliser des objets comme clés de dictionnaire

Pour ce que ça vaut, les tuples récemment (2.6+) ont augmenté les méthodes index() et count()

15
John La Rooy

J'ai toujours trouvé que le fait d'avoir deux types complètement séparés pour la même structure de données de base (tableaux) était une conception maladroite, mais pas un vrai problème dans la pratique. (Chaque langue a ses verrues, Python inclus, mais ce n'est pas important.)

Pourquoi quiconque se soucie-t-il si une variable vit à un endroit différent de la mémoire par rapport à sa répartition initiale? Toute cette affaire d'immuabilité en Python semble être surestimée.

Ce sont des choses différentes. La mutabilité n'est pas liée à l'endroit où elle est stockée en mémoire; cela signifie que ce qu'il pointe ne peut pas changer.

Les objets Python ne peuvent pas changer d'emplacement après avoir été créés, modifiables ou non. (Plus précisément, la valeur de id () ne peut pas changer - même chose, en pratique.) Le stockage interne des objets mutables peut changer, mais c'est un détail d'implémentation caché.

>>> x='hello'
>>> id(x)
1234567
>>> x='good bye'
>>> id(x)
5432167

Cela ne modifie pas ("mute") la variable; il crée une nouvelle variable avec le même nom et rejette l'ancienne. Comparer à une opération de mutation:

>>> a = [1,2,3]
>>> id(a)
3084599212L
>>> a[1] = 5
>>> a
[1, 5, 3]
>>> id(a)
3084599212L

Comme d'autres l'ont souligné, cela permet d'utiliser des tableaux comme clés de dictionnaires et d'autres structures de données qui ont besoin d'immuabilité.

Notez que les clés des dictionnaires ne doivent pas être complètement immuables. Seule la partie de celle-ci utilisée comme clé doit être immuable; pour certaines utilisations, il s'agit d'une distinction importante. Par exemple, vous pourriez avoir une classe représentant un utilisateur, qui compare l'égalité et un hachage par le nom d'utilisateur unique. Vous pouvez alors accrocher d'autres données modifiables sur la classe - "l'utilisateur est connecté", etc. Comme cela n'affecte pas l'égalité ou le hachage, il est possible et parfaitement valide de l'utiliser comme clé dans un dictionnaire. Ce n'est pas trop souvent nécessaire en Python; Je le signale simplement car plusieurs personnes ont affirmé que les clés doivent être "immuables", ce qui n'est que partiellement correct. Cependant, je l'ai utilisé plusieurs fois avec des cartes et des ensembles C++.

9
Glenn Maynard

Comme grignoteur l'a proposé dans un commentaire, Guido avait un avis qui n'est pas entièrement accepté/apprécié: "les listes sont pour des données homogènes, les tuples sont pour des données hétérogènes". Bien sûr, de nombreux opposants ont interprété cela comme signifiant que tous les éléments d'une liste devraient être du même type.

J'aime le voir différemment, un peu comme les autres ont aussi dans le passé:

blue= 0, 0, 255
alist= ["red", "green", blue]

Notez que je considère alist comme homogène, même si type (alist [1])! = Type (alist [2]).

Si je peux changer l'ordre des éléments et que je n'aurai pas de problèmes dans mon code (en dehors des hypothèses, par exemple "il doit être trié"), alors une liste doit être utilisée. Sinon (comme dans le Tuple blue ci-dessus), alors je devrais utiliser un Tuple.

7
tzot

Ils sont importants car ils garantissent à l'appelant que l'objet qu'ils passent ne sera pas muté. Si tu fais ça:

a = [1,1,1]
doWork(a)

L'appelant n'a aucune garantie de la valeur de a après l'appel. cependant,

a = (1,1,1)
doWorK(a)

Maintenant, en tant qu'appelant ou en tant que lecteur de ce code, vous savez que a est le même. Pour ce scénario, vous pouvez toujours faire une copie de la liste et la transmettre, mais maintenant vous perdez des cycles au lieu d'utiliser une construction de langage qui a plus de sens sémantique.

6
Matthew Manela

Votre question (et commentaires de suivi) se concentre sur la question de savoir si l'id () change pendant une affectation. Se concentrer sur cet effet de suivi de la différence entre le remplacement d'objet immuable et la modification d'objet mutable plutôt que la différence elle-même n'est peut-être pas la meilleure approche.

Avant de continuer, assurez-vous que le comportement illustré ci-dessous correspond à ce que vous attendez de Python.

>>> a1 = [1]
>>> a2 = a1
>>> print a2[0]
1
>>> a1[0] = 2
>>> print a2[0]
2

Dans ce cas, le contenu de a2 a été modifié, même si seul a1 a été affecté à une nouvelle valeur. Contrairement à ce qui suit:

>>> a1 = (1,)
>>> a2 = a1
>>> print a2[0]
1
>>> a1 = (2,)
>>> print a2[0]
1

Dans ce dernier cas, nous avons remplacé la liste entière, plutôt que de mettre à jour son contenu. Avec les types immuables tels que les tuples, c'est le seul comportement autorisé.

Pourquoi est-ce important? Disons que vous avez un dict:

>>> t1 = (1,2)
>>> d1 = { t1 : 'three' }
>>> print d1
{(1,2): 'three'}
>>> t1[0] = 0  ## results in a TypeError, as tuples cannot be modified
>>> t1 = (2,3) ## creates a new Tuple, does not modify the old one
>>> print d1   ## as seen here, the dict is still intact
{(1,2): 'three'}

En utilisant un tuple, le dictionnaire est sûr d'avoir ses clés changées "hors de dessous" en éléments qui sont hachés à une valeur différente. Ceci est essentiel pour permettre une mise en œuvre efficace.

1
Charles Duffy

vous pouvez voir ici pour une discussion à ce sujet

1
ghostdog74