Pourquoi les tuples peuvent-ils contenir des éléments mutables?


182

Si un tuple est immuable, pourquoi peut-il contenir des éléments mutables?

C'est apparemment une contradiction que lorsqu'un élément mutable tel qu'une liste est modifié, le tuple auquel il appartient reste immuable.

Réponses:


208

C'est une excellente question.

Le point clé est que les tuples n'ont aucun moyen de savoir si les objets qu'ils contiennent sont mutables. La seule chose qui rend un objet mutable est d'avoir une méthode qui modifie ses données. En général, il n'y a aucun moyen de détecter cela.

Une autre idée est que les conteneurs de Python ne contiennent en fait rien. Au lieu de cela, ils conservent des références à d'autres objets. De même, les variables de Python ne sont pas comme des variables dans les langages compilés; au lieu de cela, les noms de variables ne sont que des clés dans un dictionnaire d'espaces de noms où ils sont associés à un objet correspondant. Ned Batchhelder l'explique bien dans son article de blog . Dans tous les cas, les objets ne connaissent que leur nombre de références; ils ne savent pas quelles sont ces références (variables, conteneurs ou composants internes de Python).

Ensemble, ces deux aperçus expliquent votre mystère (pourquoi un tuple immuable "contenant" une liste semble changer lorsque la liste sous-jacente change). En fait, le tuple n'a pas changé (il a toujours les mêmes références à d'autres objets qu'auparavant). Le tuple n'a pas pu changer (car il n'avait pas de méthodes de mutation). Lorsque la liste a changé, le tuple n'a pas été notifié du changement (la liste ne sait pas s'il est référencé par une variable, un tuple ou une autre liste).

Pendant que nous sommes sur le sujet, voici quelques autres réflexions pour vous aider à compléter votre modèle mental de ce que sont les tuples, comment ils fonctionnent et leur utilisation prévue:

  1. Les tuples se caractérisent moins par leur immuabilité que par leur destination.
    Les tuples sont la manière de Python de collecter des informations hétérogènes sous un même toit. Par exemple, s = ('www.python.org', 80) rassemble une chaîne et un nombre afin que la paire hôte / port puisse être transmise en tant que socket, objet composite. Vu sous cet angle, il est parfaitement raisonnable d'avoir des composants mutables.

  2. L'immuabilité va de pair avec une autre propriété, l' habilité . Mais la capacité de hachage n'est pas une propriété absolue. Si l'un des composants du tuple n'est pas hachable, alors le tuple global ne l'est pas non plus. Par exemple, t = ('red', [10, 20, 30])n'est pas hachable.

Le dernier exemple montre un 2-tuple qui contient une chaîne et une liste. Le tuple lui-même n'est pas modifiable (c'est-à-dire qu'il n'a aucune méthode pour changer son contenu). De même, la chaîne est immuable car les chaînes n'ont pas de méthode de mutation. L'objet de liste possède des méthodes de mutation, il peut donc être modifié. Cela montre que la mutabilité est une propriété d'un type d'objet - certains objets ont des méthodes de mutation et d'autres non. Cela ne change pas simplement parce que les objets sont imbriqués.

Souvenez-vous de deux choses. Premièrement, l'immuabilité n'est pas magique - c'est simplement l'absence de méthodes mutantes. Deuxièmement, les objets ne savent pas quelles variables ou quels conteneurs se réfèrent à eux - ils ne connaissent que le nombre de références.

J'espère que cela vous a été utile :-)


N'est-ce pas faux "Les tuples n'ont aucun moyen de savoir si les objets qu'ils contiennent sont mutables". Nous pouvons détecter si la référence implémente une méthode de hachage, alors elle est immuable. Comme le ferait un dic ou un set. Ne serait-ce pas plus une décision de conception pour ce que les tuples sont censés être?
garg10mai

@ garg10may 1) La hachage n'est pas facile à détecter sans appel hash()car tout ce qui hérite de object () est hachable et les sous-classes doivent donc désactiver explicitement le hachage. 2) La hachage ne garantit pas l'immuabilité - il est facile de créer des exemples d'objets hachables qui sont mutables. 3) Les tuples, comme la plupart des conteneurs en Python, n'ont que des références à l'objet sous-jacent - ils n'ont aucun devoir de les inspecter et de faire des inférences à leur sujet.
Raymond Hettinger

173

C'est parce que les tuples ne contiennent pas de listes, de chaînes ou de nombres. Ils contiennent des références à d'autres objets . 1 L'incapacité de modifier la séquence de références qu'un tuple contient ne signifie pas que vous ne pouvez pas muter les objets associés à ces références. 2

1. Objets, valeurs et types (voir: avant-dernier paragraphe)
2. La hiérarchie des types standard (voir: "Séquences immuables")


8
Il y a une ambiguïté dans la question. Cette réponse explique en détail pourquoi il est possible que les tuples contiennent des objets mutables. Cela n'explique pas pourquoi les tuples ont été conçus pour contenir des objets mutables. Je pense que cette dernière est la question la plus pertinente.
senderle

16

Tout d'abord, le mot «immuable» peut signifier beaucoup de choses différentes pour différentes personnes. J'aime particulièrement la façon dont Eric Lippert a catégorisé l'immuabilité dans son article de blog . Là, il énumère ces types d'immuabilité:

  • Immuabilité Realio-trulio
  • Immuabilité en écriture unique
  • Immuabilité de popsicle
  • Immuabilité peu profonde vs profonde
  • Façades immuables
  • Immuabilité observationnelle

Ceux-ci peuvent être combinés de différentes manières pour créer encore plus de types d'immuabilité, et je suis sûr que d'autres existent. Le type d'immuabilité qui vous semble intéressé par l'immuabilité profonde (également appelée transitive), dans laquelle les objets immuables ne peuvent contenir que d'autres objets immuables.

Le point clé de ceci est que l'immuabilité profonde n'est que l'un des nombreux types d'immuabilité. Vous pouvez adopter celui que vous préférez, à condition que vous soyez conscient que votre notion d '«immuable» diffère probablement de la notion d' «immuable» de quelqu'un d'autre.


Quel genre d'immuabilité les tuples Python ont-ils?
qazwsx

3
Les tuples Python ont une immuabilité superficielle (aka non transitive).
Ken Wayne VanderLinde

16

Si je comprends bien, cette question doit être reformulée comme une question sur les décisions de conception: pourquoi les concepteurs de Python ont-ils choisi de créer un type de séquence immuable pouvant contenir des objets mutables?

Pour répondre à cette question, nous devons réfléchir au but des tuples : ils servent de séquences rapides et polyvalentes . Dans cet esprit, il devient assez évident pourquoi les tuples sont immuables mais peuvent contenir des objets mutables. En être témoin:

  1. Les tuples sont rapides et économes en mémoire: les tuples sont plus rapides à créer que les listes car ils sont immuables. L'immuabilité signifie que les tuples peuvent être créés en tant que constantes et chargés en tant que tels, en utilisant un pliage constant . Cela signifie aussi qu'ils sont de créer plus rapidement et plus efficace de la mémoire , car il n'y a pas besoin de surutilisation, etc. Ils sont un peu plus lent que les listes d'accès de l' élément aléatoire, mais plus rapide encore une fois pour le déballage (au moins sur ma machine). Si les tuples étaient mutables, ils ne seraient pas aussi rapides à des fins comme celles-ci.

  2. Les tuples sont à usage général : les tuples doivent pouvoir contenir n'importe quel type d'objet. Ils sont habitués (rapidement) à faire des choses comme des listes d'arguments de longueur variable (via l' *opérateur dans les définitions de fonctions). Si les tuples ne pouvaient pas contenir d'objets mutables, ils seraient inutiles pour de telles choses. Python devrait utiliser des listes, ce qui ralentirait probablement les choses et serait certainement moins efficace en mémoire.

Ainsi, vous voyez, pour remplir leur objectif, les tuples doivent être immuables, mais doivent également pouvoir contenir des objets mutables. Si les concepteurs de Python voulaient créer un objet immuable garantissant que tous les objets qu'il «contient» sont également immuables, ils devraient créer un troisième type de séquence. Le gain ne vaut pas la complexité supplémentaire.


12

Vous ne pouvez pas modifier le idde ses éléments. Il contiendra donc toujours les mêmes éléments.

$ python
>>> t = (1, [2, 3])
>>> id(t[1])
12371368
>>> t[1].append(4)
>>> id(t[1])
12371368

C'est la démonstration la plus appropriée des exemples ci-dessus. Le tuple a des références à ces objets qui ne changent pas, bien que le fait d'avoir au plus un composant mutable rend le tuple entier nonchalable.
piepi

5

Je vais prendre quelques précautions ici et dire que la partie pertinente ici est que même si vous pouvez modifier le contenu d'une liste, ou l'état d'un objet, contenu dans un tuple, ce que vous ne pouvez pas changer, c'est que l'objet ou la liste est là. Si vous aviez quelque chose qui dépendait du fait que la chose [3] était une liste, même vide, alors je pourrais voir que cela est utile.


3

Une des raisons est qu'il n'y a pas de moyen général en Python de convertir un type mutable en un type immuable (voir le PEP 351 rejeté et la discussion liée pour savoir pourquoi il a été rejeté). Ainsi, il serait impossible de mettre divers types d'objets dans des tuples s'il avait cette restriction, y compris à peu près n'importe quel objet non hachable créé par l'utilisateur.

La seule raison pour laquelle les dictionnaires et les ensembles ont cette restriction est qu'ils exigent que les objets soient hachables, car ils sont implémentés en interne sous forme de tables de hachage. Mais notez que, ironiquement, les dictionnaires et les ensembles eux-mêmes ne sont pas immuables (ou hachables). Les tuples n'utilisent pas le hachage d'un objet, donc sa mutabilité n'a pas d'importance.


2

Un tuple est immuable dans le sens où le tuple lui-même ne peut pas s'étendre ou se réduire, non pas que tous les éléments qu'ils contiennent sont immuables. Sinon, les tuples sont ternes.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.