Types immuables vs mutables

189

Je ne sais pas ce qu'est un type immuable. Je sais que l' floatobjet est considéré comme immuable, avec ce type d'exemple de mon livre:

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

Est-ce considéré comme immuable en raison de la structure / hiérarchie des classes?, La signification floatest au sommet de la classe et est son propre appel de méthode. Similaire à ce type d'exemple (même si mon livre dit qu'il dictest modifiable):

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

Alors que quelque chose de mutable a des méthodes à l'intérieur de la classe, avec ce type d'exemple:

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

Aussi, pour le dernier class(SortedKeyDict_a), si je lui passe ce type d'ensemble:

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

sans appeler la exampleméthode, il renvoie un dictionnaire. Le SortedKeyDictavec le __new__signale comme une erreur. J'ai essayé de passer des nombres entiers à la RoundFloatclasse avec __new__et cela n'a signalé aucune erreur.

utilisateur1027217
la source
Vous pouvez également consulter l' affectation de liste avec [:] et python quand utiliser copy.copy à laquelle j'ai également répondu pour plus d'informations sur la mutabilité.
agf

Réponses:

233

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

x = 5.0
x += 7.0
print x # 12.0

N'est-ce pas "mut" x?

Eh bien, vous êtes d'accord 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 à quoi la variable fait référence. Un type mutable peut changer de cette façon, et il peut également 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]
L'étoile du matin
la source
5
Ce que vous expliquez signifie pour moi: les variables mutables sont passées par référence, les variables immuables sont passées par valeur. Est-ce correct ?
Lorenz Meyer
18
Presque, mais pas exactement. Techniquement, toutes les variables sont passées par référence en Python, mais ont une sémantique qui ressemble plus à une passe par valeur en C. Un contre-exemple à votre analogie est si vous le faites def f(my_list): my_list = [1, 2, 3]. Avec le passage par référence en C, la valeur de l'argument pourrait changer en appelant cette fonction. En Python, cette fonction ne fait rien. def f(my_list): my_list[:] = [1, 2, 3]ferait quelque chose.
morningstar
6
Les types mutables peuvent être modifiés sur place. Les types immuables ne peuvent pas changer sur place. C'est ainsi que Python voit le monde. Cela dépend de la manière dont les variables sont transmises aux fonctions.
ychaouche
13
La principale différence entre la sémantique de Python et la sémantique passe-par-référence C ++ est que l'affectation n'est pas une mutation en Python, mais en C ++. (Mais bien sûr , qui est compliquée par le fait que l' affectation augmentée, comme a += bparfois est la mutation. Et le fait que l' affectation à une partie d'un plus grand objet est parfois synonyme de mutation de cet objet plus grand, tout simplement jamais mutation de la partie , par exemple, a[0] = bne pas muter a[0], mais il mute probablement a… C'est pourquoi il vaut peut-être mieux ne pas essayer de mettre les choses en termes de C ++ et plutôt simplement décrire ce que fait Python dans ses propres termes…)
abarnert
2
J'ai trouvé cette réponse trompeuse car elle n'utilise pas id (), ce qui est essentiel pour comprendre ce que signifie immuable.
pawel_winzig
185

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 changer leur identité. D'autres objets tels que les entiers, les flottants, les chaînes et les tuples sont des objets qui ne peuvent pas être modifiés. Un moyen simple de comprendre cela est de jeter un œil à un ID d'objets.

Ci-dessous, vous voyez une chaîne qui est immuable. Vous ne pouvez pas modifier son contenu. Cela augmentera TypeErrorsi vous essayez de le changer. De plus, si nous attribuons un nouveau contenu, un nouvel objet est créé au lieu 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:

sebs
la source
4
+1 Pour le lien vers les documents Python. Cependant, il m'a fallu un certain temps avant de réaliser qu'aujourd'hui, vous devez différencier Python 2 et 3 - j'ai mis à jour la réponse pour le souligner.
benjamin
106

Type immuable commun:

  1. numéros: 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 jeu: set()
  3. type de mappage: dict()
  4. classes, instances de classe
  5. etc.

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

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)
Spectral
la source
11
Bien expliqué. J'ai aimé le concept de vérification par id(). +1.
Parag Tyagi
4
En fait, l'utilisation de id()est ici trompeuse. Un objet donné aura toujours le même identifiant pendant sa durée de vie, mais différents objets qui existent à des moments différents peuvent avoir le même identifiant en raison du garbage collection.
augurar
36

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

ints et floats sont immuables . Si je fais

a = 1
a += 5

Il pointe le nom avers un 1quelque part en mémoire sur la première ligne. Sur la deuxième ligne, il recherche que 1, ajoute 5, obtient 6alors des points aà ce 6mémoire - il n'a pas changé l' 1une 6de quelque façon. La même logique s'applique aux exemples suivants, en utilisant d'autres types immuables :

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

Pour les types mutables , je peux faire quelque chose qui change réellement la valeur où elle est stockée en mémoire . Avec:

d = [1, 2, 3]

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

e = d

Je signale simplement eles mêmeslist d points. Je peux alors faire:

e += [4, 5]

Et la liste sur laquelle eet dpointe à la fois sera mise à jour pour indiquer également les emplacements 4et5 en mémoire.

Si je retourne à un type immuable et que je le fais avec un tuple:

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

Ensuite, fne pointe toujours que vers l' originaltuple - vous avez indiqué gun tout nouveautuple .

Maintenant, avec votre exemple de

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

Où tu passes

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

(qui est un tuplede tuples) car val, vous obtenez une erreur parce que tuples n'avez pas de .clear()méthode - vous devrez passer dict(d)comme valpour que cela fonctionne, auquel cas vous obtiendrez un vide SortedKeyDicten conséquence.

agf
la source
2
C'est une très bonne explication. J'ai adoré cette question et beaucoup de (nouvelles) perspectives intéressantes pour l'expliquer.
Échec scientifique
25

Si vous arrivez à Python depuis un autre langage (sauf un qui ressemble beaucoup à Python, comme Ruby) et que vous insistez pour le comprendre en termes de cet autre langage, voici où les gens sont généralement 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 fera muter l'objet stocké dans a. (Et s'il n'y avait aucun objet stocké dansa , c'est une erreur.)

En Python, a = 2ne fait rien à tout ce qui était stocké a; cela signifie simplement qu'il 2est maintenant stocké à la aplace. (Et s'il n'y avait pas d'objet stocké a, c'est très bien.)


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

Une variable dans un langage comme C ++ est un emplacement typé en mémoire. Si aest un int, cela signifie qu'il y a 4 octets quelque part que le compilateur sait qu'il est censé être interprété comme un fichier int. Ainsi, lorsque vous le faites a = 2, cela change ce qui est stocké dans ces 4 octets de mémoire de 0, 0, 0, 1à0, 0, 0, 2 . S'il y a une autre variable int ailleurs, elle a ses propres 4 octets.

Une variable dans un langage comme Python est un nom pour un objet qui a sa propre vie. Il y a un objet pour le nombre 1et un autre objet pour le nombre 2. Et ce an'est pas 4 octets de mémoire qui sont représentés par un int, c'est juste un nom qui pointe vers l' 1objet. Cela n'a pas de sens a = 2de transformer le chiffre 1 en chiffre 2 (cela donnerait à tout programmeur Python beaucoup trop de pouvoir pour changer le fonctionnement fondamental de l'univers); ce qu'il fait à la place, c'est simplement faire aoublier l' 1objet et pointer vers l' 2objet à la place.


Alors, si l'affectation n'est pas une mutation, qu'est - ce qu'une mutation?

  • Appel d'une méthode documentée pour muter, comme 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 = bou a[0] = b. Les types immuables ne permettent pas l'affectation d'attributs ou d'éléments, les types mutables autorisent généralement l'un ou l'autre.
  • Parfois en utilisant une affectation augmentée, comme a += b, parfois non. Les types mutables font généralement muter la valeur; les types immuables ne le font jamais et vous en donnent une copie à la place (ils calculent a + b, puis attribuent le résultat à a).

Mais si l'affectation n'est pas une mutation, comment est l'assignation à une partie de la mutation d'objet? C'est là que ça devient délicat. a[0] = bne pas muter a[0](encore une fois, contrairement à C ++), mais il fait mutera (contrairement à C ++, sauf indirectement).

Tout cela est pourquoi il est probablement préférable de ne pas essayer de mettre la sémantique de Python en termes d'un langage auquel vous êtes habitué, et plutôt d'apprendre la sémantique de Python selon leurs propres termes.

Abarnert
la source
2
Dites a = «salut». a [0] = 'f' aura 'print a' print out 'fi' (Ai-je raison pour l'instant?), alors quand vous dites qu'il ne mute pas un [0], plutôt a, qu'est-ce que cela signifie ? Un [n] a-t-il également sa propre place maintenant, et changer sa valeur le renvoie à une valeur différente?
Daniel Springer
19

Différence entre les objets mutables et immuables

Définitions

Objet mutable : objet qui peut être modifié après sa création.
Objet immuable : objet qui ne peut pas être modifié après sa création.

En python, si vous modifiez la valeur de l'objet immuable, cela créera un nouvel objet.

Objets mutables

Voici les objets en Python qui sont de type mutable:

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

Objets immuables

Voici les objets en Python qui sont 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éponse

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

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

Production

"Hello World"

Dans l'exemple ci-dessus, la chaîne a été créée une fois en tant que "Hello" puis changée en "Hello World". Cela implique que la chaîne est du type mutable. Mais ce n'est pas lorsque nous vérifions son identité pour voir si elle est d'un 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"

Production

String is Immutable

Preuve 2 :

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

Production

TypeError 'str' object does not support item assignment

Questions : Tuple est-il un type immuable?
Réponse : oui , ça l'est. Preuve 1 :

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

Production

'tuple' object does not support item assignment
Anand Tripathi
la source
In [46]: a = "Hello" In [47]: id (a) Out [47]: 140071263880128 In [48]: a = a.replace ("H", "g") In [49]: a Sortie [49]: 'gello' Entrée [50]: id (a) Sortie [50]: 140071263881040
Argus Malware
voudriez-vous prouver votre problème d'attribution d'article à mon exemple ci-dessus
Argus Malware
l'attribution d'élément n'est pas problématique dans les types immuables. Dans votre cas, vous modifiez la chaîne a mais en mémoire, elle est affectée à une nouvelle variable. L'affectation des éléments dans mon cas ne changera pas la mémoire de la variable comme dans le cas d'une liste ou d'un dictionnaire. si vous effectuez un remplacement, vous créez une nouvelle variable ne modifiant pas la variable existante
Anand Tripathi
@ArgusMalware dans votre cas, deux id sont égaux à cause du premier recyclé par GC, donc le second réutilise la mémoire.
Cologler
18

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

Les types définis par l'utilisateur (c'est-à-dire les classes) sont généralement modifiables. Il existe quelques exceptions, telles que de simples sous-classes d'un type immuable. D' autres types immuables comprennent certains types intégrés tels que int, float, tupleetstr , ainsi que des classes Python mises en œuvre en C.

Une explication générale du chapitre "Modèle de données" dans la référence du langage Python " :

La valeur de certains objets peut changer. On dit que les objets dont la valeur peut changer sont mutables; les objets dont la valeur est immuable une fois qu'ils sont 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; cependant, le conteneur est toujours considéré comme immuable, car la collection d'objets qu'il contient ne peut pas être modifiée. Ainsi, 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 tuples sont immuables, tandis que les dictionnaires et les listes sont mutables.

Taleinat
la source
+1 Notez cependant que seuls certains types d'extensions (vous voudrez peut-être revoir votre définition de cela, tous les types intégrés de Python sont implémentés en C) sont immuables. D'autres (la plupart, j'ose dire) sont parfaitement mutables.
@delnan Qu'appelez-vous "types d'extensions" ?
eyquem
@eyquem: J'ai mal utilisé le terme "types d'extensions" dans ma réponse, et delnan y faisait référence. Après son commentaire, j'ai révisé ma réponse et évité d'utiliser ce terme.
taleinat
10

Un objet mutable doit avoir au moins une méthode capable de muter l'objet. Par exemple, l' listobjet a la appendméthode, qui mute réellement 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 floatn'a pas de méthode pour muter un objet flottant. Tu peux faire:

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

mais le = opérande n'est pas une méthode. Il fait juste une liaison entre la variable et ce qui est à sa droite, rien d'autre. Il ne change ni ne crée d'objets. C'est une déclaration de ce que la variable pointera, depuis maintenant.

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

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

Dans les deux cas, le = suffit de faire la liaison. Cela ne change pas et ne crée pas d'objets.

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

Maintenant, si best un flottant et que vous affectez a = b, les deux variables pointent vers le même objet, mais en réalité les variables ne peuvent pas communiquer entre elles, car l'objet est inmutable, et si vous le faites b += 1, bpointez maintenant vers un nouvel objet, et aest pointant toujours vers l'ancien et je ne peux pas savoir quoib pointe.

mais si cc'est, disons, un list, et que vous assignez a = c, maintenant aet cpouvez "communiquer", parce que listc'est mutable, et si vous le faites c.append('msg'), alors simplement vérifier que avous obtenez le message.

(À propos, chaque objet a un numéro d'identification unique associé, avec lequel vous pouvez obtenir id(x). Ainsi, vous pouvez vérifier si un objet est le même ou ne pas vérifier si son identifiant unique a changé.)

Nadapez
la source
5

Une classe est immuable si chaque objet de cette classe a une valeur fixe à l' instanciation qui ne peut pas ENSUITE être changé

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

Exemple:

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

vous vous attendiez à ce que cela fonctionne et affiche bonjour le monde, mais cela générera 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

il vous faudra changer le tout stringpour que ça marche:

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

consultez ce tableau:

entrez la description de l'image ici

la source

Mina Gabriel
la source
Comment modifier les composants d'une chaîne python d'une manière plus concise que celle que vous avez montrée ci-dessus?
Luke Davis
@LukeDavis Vous pouvez le faire my_string = 'h' + my_string[1:]. Cela générera une nouvelle chaîne appelée my_string, et l'original my_string a disparu (imprimez id(my_string)pour voir cela). Bien sûr, ce n'est pas très flexible, pour le cas plus général, vous pouvez convertir en liste et retour:l = list(my_string) l[0] = 'h' my_string = ''.join(l)
danio
4

Il me semble que vous vous battez avec la question de savoir ce que signifie réellement mutable / immuable . Voici donc une explication simple:

Nous avons d'abord besoin d'une fondation sur laquelle fonder l'explication.

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

Par exemple, vous ne pensez pas à des nombres comme 0x110, 0xaf0278297319 ou similaires, mais à la place vous pensez à des nombres comme 6 ou des chaînes comme "Hello, world". Néanmoins, ces nombres ou chaînes sont 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 programmons pas avec des valeurs réelles mais avec des interprétations de valeurs binaires réelles.

Maintenant, nous avons des interprétations qui ne doivent pas être changées pour des raisons de logique et d'autres "choses intéressantes" alors qu'il y a des interprétations qui pourraient bien être modifiées. Par exemple, pensez à la simulation d'une ville, c'est-à-dire à un programme où il y a de nombreux objets virtuels 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 le peuvent. Ainsi, ils sont mutables: ils peuvent être modifiés sans devenir un objet "complètement" différent.

Pensez maintenant aux 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, comme incrémenter la valeur de six par un, est-ce toujours un six? Bien sûr que non. Ainsi, tout entier est immuable.

Donc: si un changement dans un objet virtuel signifie qu'il devient en fait un autre objet virtuel, alors il est appelé immuable.

Remarques finales:

(1) Ne mélangez jamais votre expérience du monde réel 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 lesquels ne le peuvent pas.

Ainsi, bien que vous puissiez maintenant comprendre la différence de signification, vous devez toujours apprendre l'implémentation réelle pour chaque langage de programmation. ... En effet, il pourrait y avoir un but d'un langage où un 6 peut être coupé pour devenir un 7. Là encore ce serait assez fou ou intéressant, comme des simulations d'univers parallèles. ^^

(2) Cette explication n'est certainement pas scientifique, elle est destinée à vous aider à comprendre la différence entre mutable et immuable.

Markus Alpers
la source
4

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 à ce sujet? Il y a des moments où la mutation est indésirable et le comportement de python à cet égard peut sembler contre-intuitif pour les codeurs entrant dans d'autres langages.

Selon un article utile de @ mina-gabriel:

Analyser ce qui précède et combiner avec un message de @ arrakëën:

Qu'est-ce qui ne peut pas changer de façon inattendue?

  • les scalaires (types de variables stockant une seule valeur) ne changent pas de manière inattendue
    • exemples numériques: int (), float (), complex ()
  • il existe des "séquences mutables":
    • str (), tuple (), frozenset (), octets ()

Ce qui peut?

  • liste comme des objets (listes, dictionnaires, ensembles, bytearray ())
  • un article ici dit également les classes et les instances de classe, mais cela peut dépendre de ce dont la classe hérite et / ou de la manière dont elle est construite.

par "de manière inattendue", je veux dire que les programmeurs d'autres langages pourraient 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 empêche de remplir accidentellement votre code avec plusieurs copies de grandes structures de données gourmandes en mémoire. Mais quand cela n'est pas souhaitable, comment pouvons-nous le contourner?

Avec les listes, la solution simple est d'en créer une nouvelle comme ceci:

liste2 = liste (liste1)

avec d'autres structures ... la solution peut être plus délicate. Une méthode consiste à parcourir les éléments 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 ensuite il y a des commentaires indiquant que ces tests ne sont pas une preuve complète
  • object.function () est une méthode de l'objet original mais seulement certains d'entre eux mutent. S'ils ne retournent rien, ils le font 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.
  • sorted () 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 à travailler sur 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: Code de structure de données persistante Python écrit pour être utilisé à la place des structures de données de base lorsque la mutation n'est pas souhaitable

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

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

TMWP
la source
2

Une façon de penser la différence:

Les affectations aux objets immuables en python peuvent être considérées comme des copies profondes, tandis que les affectations aux objets mutables sont superficielles

Aditya Sihag
la source
1
Ceci est une erreur. Toutes les affectations en Python sont faites par référence. Il n'y a pas de copie impliquée.
augurar
2

La réponse la plus simple:

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

Exemple:

>>>x = 5

Créera une valeur 5 référencée par x

x -> 5

>>>y = x

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

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

>>>x = x + y

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

Dans l'instruction, l'expression sur RHS donnera la valeur 10 et lorsqu'elle est affectée à LHS (x), x sera reconstruite à 10. Alors maintenant

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

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

Amit Upadhyay
la source
-1

Je n'ai pas lu toutes les réponses, mais la réponse sélectionnée n'est pas correcte et je pense que l'auteur a une idée que pouvoir réaffecter une variable signifie que quel que soit le type de données est mutable. Ce n'est pas le cas. La mutabilité concerne le passage par référence plutôt que le passage par valeur.

Disons que vous avez créé une liste

a = [1,2]

Si vous deviez dire:

b = a
b[1] = 3

Même si vous avez réaffecté une valeur sur B, il réaffectera également la valeur sur a. C'est parce que lorsque vous attribuez "b = a". Vous transmettez la "référence" à l'objet plutôt qu'une copie de la valeur. Ce n'est pas le cas avec les chaînes, les flottants, etc. Cela rend la liste, les dictionnaires et les likes mutables, mais les booléens, les flottants, etc. immuables.

Scott
la source
-2

En Python, il existe un moyen simple de savoir:

Immuable:

    >>> s='asd'
    >>> s is 'asd'
    True
    >>> s=None
    >>> s is None
    True
    >>> s=123
    >>> s is 123
    True

Mutable:

>>> s={}
>>> s is {}
False
>>> {} is {}
Flase
>>> s=[1,2]
>>> s is [1,2]
False
>>> s=(1,2)
>>> s is (1,2)
False

Et:

>>> s=abs
>>> s is abs
True

Je pense donc que la fonction intégrée est également immuable en Python.

Mais je ne comprends vraiment pas comment fonctionne float:

>>> s=12.3
>>> s is 12.3
False
>>> 12.3 is 12.3
True
>>> s == 12.3
True
>>> id(12.3)
140241478380112
>>> id(s)
140241478380256
>>> s=12.3
>>> id(s)
140241478380112
>>> id(12.3)
140241478380256
>>> id(12.3)
140241478380256

Il est tellement bizarre.

Jason Yan
la source
Mais ce n'est clairement pas valable. Parce que les tuples sont immuables. Tapez x = (1, 2)puis essayez de muter x, ce n'est pas possible. Une façon que j'ai trouvée pour vérifier la mutabilité est que hashcela fonctionne au moins pour les objets intégrés. hash(1) hash('a') hash((1, 2)) hash(True)tout fonctionne, et hash([]) hash({}) hash({1, 2})tout ne fonctionne pas.
point
@semicolon Pour les classes définies par l'utilisateur, hash()cela fonctionnera si l'objet définit une __hash__()méthode, même si les classes définies par l'utilisateur sont généralement modifiables.
augurar
1
@augurar Je veux dire oui, mais rien en Python ne garantira quoi que ce soit, car Python n'a pas de véritable typage statique ou de garanties formelles. Mais la hashméthode est toujours assez bonne, car les objets mutables ne devraient généralement pas avoir de __hash__()méthode, car en faire des clés dans un dictionnaire est tout simplement dangereux.
point
1
@augurar et point-virgule (ou autres s'ils le savent): __hash __ () solution ... est-ce que le créateur d'une classe personnalisée doit l'ajouter pour qu'elle soit là? Si tel est le cas, la règle est que si l’objet existe, il doit être immuable; s'il n'existe pas, nous ne pouvons pas le dire car le créateur a peut-être simplement laissé si désactivé.
TMWP
-2

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
Ravi Gurnatham
la source
1
Absolument incorrect. L'affectation ne crée jamais de copie . Veuillez lire nedbatchelder.com/text/names.html Dans le premier cas, x=10est simplement une autre mission , alors qu'il x[2] = 5appelle une méthode mutator. intles objets manquent simplement de méthodes de mutation , mais la sémantique de l'affectation de python ne dépend pas du type
juanpa.arrivillaga