web-dev-qa-db-fra.com

Types immuables vs mutables

Je suis confus sur ce qu'est un type immuable. Je sais que l'objet float est considéré comme immuable, avec ce type d'exemple tiré de mon livre:

class RoundFloat(float):
    def __new__(cls, val):
        return float.__new__(cls, round(val, 2))

Est-ce considéré comme étant immuable en raison de la structure/hiérarchie de la classe ?, signifiant float se situe au sommet de la classe et constitue son propre appel de méthode. Similaire à ce type d’exemple (même si mon livre dit dict est mutable):

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Alors que quelque chose de mutable a des méthodes dans la classe, avec ce type d’exemple:

class SortedKeyDict_a(dict):
    def example(self):
        return self.keys()

Aussi, pour la dernière class(SortedKeyDict_a), si je lui passe ce type de set:

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

sans appeler la méthode example, il retourne un dictionnaire. Le SortedKeyDict avec __new__ le marque comme une erreur. J'ai essayé de passer des entiers à la classe RoundFloat avec __new__ et il n'a signalé aucune erreur.

169
user1027217

Quelle? Les flotteurs sont immuables? Mais je ne peux pas faire

x = 5.0
x += 7.0
print x # 12.0

Est-ce que ce "mut" x?

Eh bien, vous reconnaissez que les chaînes sont immuables, non? Mais vous pouvez faire la même chose.

s = 'foo'
s += 'bar'
print s # foobar

La valeur de la variable change, mais elle change en changeant le nom de la variable. Un type mutable peut changer cela, et il peut aussi changer "en place".

Voici la différence.

x = something # immutable type
print x
func(x)
print x # prints the same thing

x = something # mutable type
print x
func(x)
print x # might print something different

x = something # immutable type
y = x
print x
# some statement that operates on y
print x # prints the same thing

x = something # mutable type
y = x
print x
# some statement that operates on y
print x # might print something different

Exemples concrets

x = 'foo'
y = x
print x # foo
y += 'bar'
print x # foo

x = [1, 2, 3]
y = x
print x # [1, 2, 3]
y += [3, 2, 1]
print x # [1, 2, 3, 3, 2, 1]

def func(val):
    val += 'bar'

x = 'foo'
print x # foo
func(x)
print x # foo

def func(val):
    val += [3, 2, 1]

x = [1, 2, 3]
print x # [1, 2, 3]
func(x)
print x # [1, 2, 3, 3, 2, 1]
220
morningstar

Vous devez comprendre que Python représente toutes ses données sous forme d'objets. Certains de ces objets, comme les listes et les dictionnaires, sont modifiables, ce qui signifie que vous pouvez modifier leur contenu sans modifier leur identité. Les autres objets tels que les entiers, les flottants, les chaînes et les nuplets sont des objets qui ne peuvent pas être modifiés. Un moyen facile de comprendre cela est si vous examinez un ID d'objet.

Ci-dessous, vous voyez une chaîne immuable. Vous ne pouvez pas changer son contenu. Il y aura une TypeError si vous essayez de le changer. De même, si nous affectons un nouveau contenu, un nouvel objet est créé à la place du contenu en cours de modification.

>>> s = "abc"
>>>id(s)
4702124
>>> s[0] 
'a'
>>> s[0] = "o"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'str' object does not support item assignment
>>> s = "xyz"
>>>id(s)
4800100
>>> s += "uvw"
>>>id(s)
4800500

Vous pouvez le faire avec une liste et cela ne changera pas l'identité des objets

>>> i = [1,2,3]
>>>id(i)
2146718700
>>> i[0] 
1
>>> i[0] = 7
>>> id(i)
2146718700

Pour en savoir plus sur le modèle de données de Python, vous pouvez consulter la référence du langage Python:

176
sebs

Type immuable commun:

  1. nombres: int(), float(), complex()
  2. séquences immuables: str(), Tuple(), frozenset(), bytes()

Type mutable commun (presque tout le reste):

  1. séquences mutables: list(), bytearray()
  2. type de set: set()
  3. type de mappage: dict()
  4. classes, instances de classe
  5. etc.

Une astuce pour vérifier rapidement si un type est mutable ou non, consiste à utiliser la fonction intégrée id().

Exemples, en utilisant un entier,

>>> i = 1
>>> id(i)
***704
>>> i += 1
>>> i
2
>>> id(i)
***736 (different from ***704)

en utilisant sur la liste,

>>> a = [1]
>>> id(a)
***416
>>> a.append(2)
>>> a
[1, 2]
>>> id(a)
***416 (same with the above id)
100
Mr. Pei

Tout d'abord, le fait qu'une classe ait des méthodes ou quelle que soit sa structure n'a rien à voir avec la mutabilité.

ints et floats sont immuables. Si je fais

a = 1
a += 5

Il pointe le nom a sur un 1 quelque part en mémoire sur la première ligne. Sur la deuxième ligne, il recherche que 1, ajoute 5, obtient 6, puis pointe a vers ce 6 en mémoire - il ne l'a pas été. t changer le 1 en 6 de quelque façon que ce soit. La même logique s'applique aux exemples suivants, en utilisant d'autres types immuable:

b = 'some string'
b += 'some other string'
c = ('some', 'Tuple')
c += ('some', 'other', 'Tuple')

Pour les types mutable, je peux faire ce qui est réellement changer la valeur où il est stocké en mémoire. Avec:

d = [1, 2, 3]

J'ai créé une liste des emplacements de 1, 2 et 3 en mémoire. Si je fais alors

e = d

Je pointe juste e sur le même listd pointe sur. Je peux alors faire:

e += [4, 5]

Et la liste des points e et d sera mise à jour pour avoir également les emplacements de 4 et 5 en mémoire.

Si je reviens à un type immuable et que je le fais avec un Tuple:

f = (1, 2, 3)
g = f
g += (4, 5)

Ensuite, f ne pointe toujours que sur le original Tuple - vous avez indiqué g à un entièrement nouveau Tuple =.

Maintenant, avec votre exemple de

class SortedKeyDict(dict):
    def __new__(cls, val):
        return dict.__new__(cls, val.clear())

Où vous passez

d = (('zheng-cai', 67), ('hui-jun', 68),('xin-yi', 2))

(qui est un Tuple sur tuples) en tant que val, vous obtenez une erreur car Tuples n'a pas de méthode .clear() - vous ' Pour que cela fonctionne, vous devez passer dict(d) comme val, auquel cas vous obtiendrez un SortedKeyDict vide.

33
agf

Si vous venez de passer à Python depuis une autre langue (à l'exception d'une langue qui ressemble beaucoup à Python, comme Ruby) et que vous insistez pour la comprendre en termes de cette autre langue, voici où les gens sont en général confus:

>>> a = 1
>>> a = 2 # I thought int was immutable, but I just changed it?!

En Python, l’affectation n’est pas une mutation en Python.

En C++, si vous écrivez a = 2, vous appelez a.operator=(2), ce qui mutera l'objet stocké dans a. (Et si était aucun objet stocké dans a, c'est une erreur.)

En Python, a = 2 ne fait rien à ce qui était stocké dans a; cela signifie simplement que 2 est maintenant stocké dans a à la place. (Et si était pas d'objet stocké dans a, c'est bon.)


En fin de compte, cela fait partie d'une distinction encore plus profonde.

Une variable dans un langage tel que C++ est un emplacement typé en mémoire. Si a est un int, cela signifie que le compilateur sait que quelque 4 octets sont censés être interprétés comme un int. Ainsi, lorsque vous exécutez a = 2, le contenu de ces 4 octets de mémoire passe de 0, 0, 0, 1 à 0, 0, 0, 2. S'il existe une autre variable int ailleurs, elle a ses propres 4 octets.

Une variable dans un langage tel que Python est le nom d'un objet doté d'une vie propre. Il y a un objet pour le nombre 1 et un autre objet pour le nombre 2. Et a n'est pas 4 octets de mémoire qui sont représentés par un int, c'est simplement un nom qui pointe sur l'objet 1. Il n’a pas de sens pour a = 2 de transformer le nombre 1 en un nombre 2 (cela donnerait à tout programmeur Python beaucoup trop de pouvoir pour changer le fonctionnement fondamental de l’univers); Au lieu de cela, il fait simplement a oublier l'objet 1 et pointer sur l'objet 2.


Donc, si l’affectation n’est pas une mutation, qu'est-ce que est une mutation?

  • Appeler une méthode documentée pour muter, telle que a.append(b). (Notez que ces méthodes retournent presque toujours None). Les types immuables n'ont pas de telles méthodes, les types mutables en ont généralement.
  • Assigner à une partie de l'objet, comme a.spam = b ou a[0] = b. Les types immuables ne permettent pas l'attribution d'attributs ou d'éléments, les types mutables autorisent généralement l'un ou l'autre.
  • Parfois, en utilisant l'affectation augmentée, comme a += b, parfois pas. Les types mutables mutent généralement la valeur; les types immuables ne le font jamais et vous en donnent une copie (ils calculent a + b, puis affectent le résultat à a).

Mais si l'attribution n'est pas une mutation, comment l'assignation à une partie de la mutation d'objet? C'est là que ça devient difficile. a[0] = b fait pas mute a[0] (encore une fois, contrairement au C++), mais fait mute a (contrairement au C++, sauf indirectement) .

C'est pour cette raison qu'il vaut probablement mieux pas d'essayer de mettre la sémantique de Python en termes de langage auquel vous êtes habitué et d'apprendre la sémantique de Python selon ses propres termes.

20
abarnert

Qu'un objet soit modifiable ou non dépend de son type. Cela ne dépend pas de la présence de certaines méthodes, ni de la structure de la hiérarchie de classes.

Les types définis par l'utilisateur (c'est-à-dire les classes) sont généralement mutables. Il existe quelques exceptions, telles que de simples sous-classes d'un type immuable. D'autres types immuables incluent des types intégrés tels que int, float, Tuple et str, ainsi que certaines classes Python implémentées en C .

Une explication générale tirée de le chapitre "Modèle de données" du Python Référence du langage " :

La valeur de certains objets peut changer. Les objets dont la valeur peut changer sont dits mutables; les objets dont la valeur est immuable une fois créés sont appelés immuables.

(La valeur d'un objet conteneur immuable qui contient une référence à un objet mutable peut changer lorsque la valeur de ce dernier est modifiée; toutefois, le conteneur est toujours considéré comme immuable, car la collection d'objets qu'il contient ne peut pas être modifiée. Par conséquent, l'immuabilité n'est pas strictement la même chose que d'avoir une valeur immuable, c'est plus subtil.)

La mutabilité d’un objet est déterminée par son type; par exemple, les nombres, les chaînes et les n-uplets sont immuables, alors que les dictionnaires et les listes sont mutables.

18
taleinat

Différence entre objet mutable et immuable

Définitions

Objet modifiable: Objet pouvant être modifié après l'avoir créé.
Objet immuable: Objet qui ne peut pas être modifié après l'avoir créé.

Dans python tentera de modifier la valeur de l'objet immuable qu'il donnera au nouvel objet.

Objets Mutable

Voici les objets de liste dans python qui sont de type mutable:

  1. list
  2. Dictionary
  3. Set
  4. bytearray
  5. user defined classes

Objets immuables

Voici les objets de liste dans python de type immuable:

  1. int
  2. float
  3. decimal
  4. complex
  5. bool
  6. string
  7. Tuple
  8. range
  9. frozenset
  10. bytes

Quelques questions sans réponses

Questions: La chaîne est-elle un type immuable?
Réponse: oui oui, mais pouvez-vous expliquer ceci: preuve 1:

a = "Hello"
a +=" World"
print a

sortie

"Hello World"

Dans l'exemple ci-dessus, la chaîne créée une fois sous le nom "Hello" a finalement été remplacée par "Hello World". Cela implique que la chaîne est du type mutable. Mais ce n’est pas que nous pouvons vérifier son identité et vérifier si elle est de type mutable ou non.

a = "Hello"
identity_a = id(a)
a += " World"
new_identity_a = id(a)
if identity_a != new_identity_a:
    print "String is Immutable"

sortie

String is Immutable

Preuve 2:

a = "Hello World"
a[0] = "M"

sortie

TypeError 'str' object does not support item assignment

Questions: Tuple est-il un type immuable?
Réponse: oui c'est preuve 1:

Tuple_a = (1,)
Tuple_a[0] = (2,)
print a

sortie

'Tuple' object does not support item assignment
11
anand tripathi

Un objet mutable doit avoir au moins une méthode capable de muter l'objet. Par exemple, l'objet list a la méthode append, qui va en réalité transformer l'objet:

>>> a = [1,2,3]
>>> a.append('hello') # `a` has mutated but is still the same object
>>> a
[1, 2, 3, 'hello']

mais la classe float n'a pas de méthode pour muter un objet float. Tu peux faire:

>>> b = 5.0 
>>> b = b + 0.1
>>> b
5.1

mais l'opérande = n'est pas une méthode. Il ne fait qu’un lien entre la variable et ce qui est à sa droite, rien d’autre. Il ne change jamais ou crée des objets. C'est une déclaration de ce que la variable indiquera, à partir de maintenant.

Quand vous faites b = b + 0.1 l'opérande = lie la variable à un nouveau float, qui est créé avec le résultat de 5 + 0.1.

Lorsque vous affectez une variable à un objet existant, modifiable ou non, l'opérande = lie la variable à cet objet. Et rien ne se passe plus

Dans les deux cas, le = crée simplement la liaison. Cela ne change ni ne crée d'objets.

Quand vous faites a = 1.0, l'opérande = n'est pas ce qui crée le float, mais la partie 1.0 de la ligne. En fait, lorsque vous écrivez 1.0, il s'agit d'un raccourci pour float(1.0) un appel du constructeur renvoyant un objet float. (C'est la raison pour laquelle si vous tapez 1.0 et appuyez sur Entrée, vous obtenez "echo" 1.0 imprimé ci-dessous; il s'agit de la valeur de retour de la fonction constructeur que vous avez appelée).

Maintenant, si b est un float et que vous affectez a = b, les deux variables pointent sur le même objet, mais en réalité, les variables ne peuvent pas communiquer entre elles, car l'objet est immuable, et si vous le faites b += 1, maintenant b pointe vers un nouvel objet et a pointe toujours vers l'ancien et ne peut pas savoir ce que b désigne.

mais si c est, par exemple, un list et que vous affectez a = c, maintenant a et c pouvez "communiquer", car list est modifiable, et si vous faites c.append('msg'), alors en vérifiant simplement a vous obtenez le message.

(Au fait, chaque objet a un numéro d'identifiant unique associé à _, que vous pouvez obtenir avec id(x). Vous pouvez donc vérifier si un objet est identique ou ne vérifie pas si son identifiant unique a changé.)

11
nadapez

Une classe est immuable si chaque objet de cette classe a une valeur fixe lors de l'instanciation qui ne peut pas ENSUITE soit modifié

Dans un autre mot, changez la valeur entière de cette variable (name) ou laissez-la tranquille.

Exemple:

my_string = "Hello world" 
my_string[0] = "h"
print my_string 

vous vous attendiez à ce que cela fonctionne et à imprimer bonjour le monde , mais cela produira l'erreur suivante:

Traceback (most recent call last):
File "test.py", line 4, in <module>
my_string[0] = "h"
TypeError: 'str' object does not support item assignment

L'interprète dit: je ne peux pas changer le premier caractère de cette chaîne

vous devrez changer tout le string pour le faire fonctionner:

my_string = "Hello World" 
my_string = "hello world"
print my_string #hello world

vérifier ce tableau:

enter image description here

source

5
Mina Gabriel

Le but de cette réponse est de créer un endroit unique pour trouver toutes les bonnes idées sur la façon de savoir si vous avez affaire à une mutation/non-mutation (immuable/mutable) et, si possible, que faire? Il y a des moments où la mutation est indésirable et le comportement de python à cet égard peut sembler contre-intuitif aux développeurs venant d'autres langages.

Selon un article utile de @ mina-gabriel:

Analyser ce qui précède et combiner w/a post de @ arrakëën:

Qu'est-ce qui ne peut pas changer de manière inattendue?

  • les scalaires (types de variables stockant une seule valeur) ne changent pas de manière inattendue
    • exemples numériques: int (), float (), complex ()
  • il y a quelques "séquences mutables":
    • str (), Tuple (), frozenset (), bytes ()

Que peut-on?

  • liste comme des objets (listes, dictionnaires, ensembles, bytearray ())
  • une publication sur ici indique également des classes et des instances de classe, mais cela peut dépendre de ce dont la classe hérite et/ou de la façon dont elle est construite.

par "de façon inattendue", je veux dire que les programmeurs d'autres langages peuvent ne pas s'attendre à ce comportement (à l'exception de Ruby et peut-être de quelques autres langages "de type Python").

Ajout à cette discussion:

Ce comportement est un avantage lorsqu'il vous évite de remplir accidentellement votre code avec plusieurs copies de grandes structures de données consommant de la mémoire. Mais quand cela n'est pas souhaitable, comment pouvons-nous le contourner?

Avec les listes, la solution simple consiste à en créer un nouveau comme ceci:

list2 = list (list1)

avec d'autres structures ... la solution peut être plus délicate. Une solution consiste à parcourir les éléments en boucle et à les ajouter à une nouvelle structure de données vide (du même type).

les fonctions peuvent muter l'original lorsque vous passez dans des structures mutables. Comment dire?

  • Il y a quelques tests donnés sur d'autres commentaires sur ce fil, mais il y a aussi des commentaires indiquant que ces tests ne sont pas une preuve complète
  • object.function () est une méthode de l'objet original mais seules certaines d'entre elles mutent. S'ils ne renvoient rien, ils le feront probablement. On s’attendrait à ce que .append () mute sans le tester étant donné son nom. .union () renvoie l'union de set1.union (set2) et ne mute pas. En cas de doute, la fonction peut être vérifiée pour une valeur de retour. Si return = None, il ne mute pas.
  • trié () peut être une solution de contournement dans certains cas. Puisqu'il renvoie une version triée de l'original, il peut vous permettre de stocker une copie non mutée avant de commencer à utiliser l'original d'une autre manière. Cependant, cette option suppose que vous ne vous souciez pas de l'ordre des éléments d'origine (si vous le faites, vous devez trouver un autre moyen). En revanche, .sort () mute l'original (comme on pouvait s'y attendre).

Approches non standard (au cas où cela serait utile): Trouvé ceci sur github publié sous une licence MIT:

  • dépôt github sous: tobgu nommé: pyrsistent
  • Qu'est-ce que c'est: Python code de structure de données persistante écrit pour être utilisé à la place des structures de données principales lorsque la mutation est indésirable

Pour les classes personnalisées, @semicolon suggère de vérifier s'il existe une fonction __hash__ car les objets mutables ne doivent généralement pas avoir de fonction __hash__().

C'est tout ce que j'ai amassé sur ce sujet pour le moment. D'autres idées, corrections, etc. sont les bienvenues. Merci.

4
TMWP

Il me semblerait que vous vous battez pour savoir ce que signifie réellement mutable/immuable. Alors voici une explication simple:

D'abord, nous avons besoin d'une fondation sur laquelle nous fonderons l'explication.

Pensez donc à tout ce que vous programmez en tant qu'objet virtuel, enregistré dans une mémoire d'ordinateur sous la forme d'une séquence de nombres binaires. (N'essayez pas d'imaginer ceci trop dur, cependant. ^^) Maintenant, dans la plupart des langages informatiques, vous ne travaillerez pas directement avec ces nombres binaires, mais plutôt vous utiliserez une interprétation des nombres binaires.

Par exemple. vous ne pensez pas à des nombres comme 0x110, 0xaf0278297319 ou similaire, mais à des chiffres comme 6 ou à des chaînes comme "Hello, world". Néanmoins, ces nombres ou chaînes ne sont pas une interprétation d'un nombre binaire dans la mémoire de l'ordinateur. La même chose est vraie pour toute valeur d'une variable.

En bref: Nous ne pas programme avec actuel valeurs mais avec interprétations des valeurs binaires réelles.

Nous avons maintenant des interprétations qui ne doivent pas être changées pour des raisons de logique et autres "choses intéressantes", alors que certaines interprétations peuvent bien être changées. Par exemple, pensez à la simulation d’une ville, c’est-à-dire à un programme comportant de nombreux objets virtuels et dont certains sont des maisons. Maintenant, ces objets virtuels (les maisons) peuvent-ils être modifiés et peuvent-ils toujours être considérés comme les mêmes maisons? Bien sûr, ils peuvent. Ainsi, ils sont mutables: ils peuvent être modifiés sans devenir un objet "complètement" différent.

Pensez maintenant aux nombres entiers: ce sont aussi des objets virtuels (séquences de nombres binaires dans la mémoire d'un ordinateur). Donc, si nous changeons l’un d’eux, par exemple en augmentant la valeur six par un, est-ce toujours un six? Bien sûr que non. Ainsi tout entier est immuable.

Donc: Si tout changement dans un objet virtuel signifie qu'il devient en réalité un autre objet virtuel, il est appelé immuable.

Remarques finales:

(1) Ne mélangez jamais votre expérience réelle de mutable et immuable avec la programmation dans un certain langage:

Chaque langage de programmation a sa propre définition sur laquelle les objets peuvent être mis en sourdine et ceux qui ne le peuvent pas.

Ainsi, bien que vous puissiez maintenant comprendre la différence de sens, vous devez toujours apprendre la mise en oeuvre réelle de chaque langage de programmation. ... En effet, il pourrait y avoir un but dans un langage où un 6 peut être assourdi pour devenir un 7. Là encore, cela pourrait être quelque chose de fou ou d'intéressant, comme des simulations d'univers parallèles. ^^

(2) Cette explication n’est certainement pas scientifique, elle a pour but de vous aider à comprendre la différence entre mutable et immuable.

4
Markus Alpers

Une façon de penser à la différence:

Les assignations à des objets immuables dans python peuvent être considérées comme des copies complètes, alors que les assignations à des objets mutables sont superficielles

3
Aditya Sihag

La réponse la plus simple:

Une variable mutable est une variable dont la valeur peut changer à la place, alors que dans une variable immuable, le changement de valeur ne se produira pas à la place. La modification d'une variable immuable reconstruira la même variable.

Exemple:

>>>x = 5

Va créer une valeur 5 référencée par x

x -> 5

>>>y = x

Cette déclaration fera y référence à 5 de x

x -------------> 5 <----------- y

>>>x = x + y

As x étant un entier (type immuable) a été reconstruit.

Dans l'instruction, l'expression sur RHS donnera la valeur 10 et, lorsque celle-ci sera affectée à LHS (x), x sera reconstruit à 10. Alors maintenant

x ---------> 10

y ---------> 5

2
Amit Upadhyay

Pour les objets immuables, l’affectation crée une nouvelle copie des valeurs, par exemple.

x=7
y=x
print(x,y)
x=10 # so for immutable objects this creates a new copy so that it doesnot 
#effect the value of y
print(x,y)

Pour les objets mutables, l'affectation ne crée pas une autre copie des valeurs. Par exemple,

x=[1,2,3,4]
print(x)
y=x #for immutable objects assignment doesn't create new copy 
x[2]=5
print(x,y) # both x&y holds the same list
0
Ravi Gurnatham