Je ne sais pas ce qu'est un type immuable. Je sais que l' float
objet 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 float
est 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 dict
est 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 example
méthode, il renvoie un dictionnaire. Le SortedKeyDict
avec le __new__
signale comme une erreur. J'ai essayé de passer des nombres entiers à la RoundFloat
classe avec __new__
et cela n'a signalé aucune erreur.
la source
Réponses:
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]
la source
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.a += b
parfois 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] = b
ne pas mutera[0]
, mais il mute probablementa
… 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…)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
TypeError
si 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:
la source
Type immuable commun:
int()
,float()
,complex()
str()
,tuple()
,frozenset()
,bytes()
Type mutable commun (presque tout le reste):
list()
,bytearray()
set()
dict()
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)
la source
id()
. +1.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.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é.
int
s etfloat
s sont immuables . Si je faisa = 1 a += 5
Il pointe le nom
a
vers un1
quelque part en mémoire sur la première ligne. Sur la deuxième ligne, il recherche que1
, ajoute5
, obtient6
alors des pointsa
à ce6
mémoire - il n'a pas changé l'1
une6
de 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
,2
et3
en mémoire. Si je fais alorsJe signale simplement
e
les mêmeslist
d
points. Je peux alors faire:e += [4, 5]
Et la liste sur laquelle
e
etd
pointe à la fois sera mise à jour pour indiquer également les emplacements4
et5
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,
f
ne pointe toujours que vers l' originaltuple
- vous avez indiquég
un 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
tuple
detuples
) carval
, vous obtenez une erreur parce quetuple
s n'avez pas de.clear()
méthode - vous devrez passerdict(d)
commeval
pour que cela fonctionne, auquel cas vous obtiendrez un videSortedKeyDict
en conséquence.la source
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 appeleza.operator=(2)
, ce qui fera muter l'objet stocké dansa
. (Et s'il n'y avait aucun objet stocké dansa
, c'est une erreur.)En Python,
a = 2
ne fait rien à tout ce qui était stockéa
; cela signifie simplement qu'il2
est maintenant stocké à laa
place. (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
a
est unint
, cela signifie qu'il y a 4 octets quelque part que le compilateur sait qu'il est censé être interprété comme un fichierint
. Ainsi, lorsque vous le faitesa = 2
, cela change ce qui est stocké dans ces 4 octets de mémoire de0, 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
1
et un autre objet pour le nombre2
. Et cea
n'est pas 4 octets de mémoire qui sont représentés par unint
, c'est juste un nom qui pointe vers l'1
objet. Cela n'a pas de sensa = 2
de 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 fairea
oublier l'1
objet et pointer vers l'2
objet à la place.Alors, si l'affectation n'est pas une mutation, qu'est - ce qu'une mutation?
a.append(b)
. (Notez que ces méthodes retournent presque toujoursNone
). Les types immuables n'ont pas de telles méthodes, les types mutables en ont généralement.a.spam = b
oua[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.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 calculenta + 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] = b
ne pas mutera[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.
la source
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:
list
Dictionary
Set
bytearray
user defined classes
Objets immuables
Voici les objets en Python qui sont de type immuable:
int
float
decimal
complex
bool
string
tuple
range
frozenset
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
la source
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
,tuple
etstr
, 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 source
Un objet mutable doit avoir au moins une méthode capable de muter l'objet. Par exemple, l'
list
objet a laappend
mé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
float
n'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.1
l'=
opérande lie la variable à un nouveau flottant, qui est créé avec le résultat de5 + 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 passeDans 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 la1.0
partie de la ligne. En fait, lorsque vous écrivez,1.0
il s'agit d'un raccourci pourfloat(1.0)
un appel de constructeur renvoyant un objet float. (C'est la raison pour laquelle si vous tapez1.0
et appuyez sur Entrée, vous obtenez le "écho"1.0
imprimé ci-dessous; c'est la valeur de retour de la fonction constructeur que vous avez appelée)Maintenant, si
b
est un flottant et que vous affecteza = 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 faitesb += 1
,b
pointez maintenant vers un nouvel objet, eta
est pointant toujours vers l'ancien et je ne peux pas savoir quoib
pointe.mais si
c
c'est, disons, unlist
, et que vous assigneza = c
, maintenanta
etc
pouvez "communiquer", parce quelist
c'est mutable, et si vous le faitesc.append('msg')
, alors simplement vérifier quea
vous 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é.)la source
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
string
pour que ça marche:my_string = "Hello World" my_string = "hello world" print my_string #hello world
consultez ce tableau:
la source
la source
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 (imprimezid(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)
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.
la source
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?
Ce qui peut?
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?
Approches non standard (au cas où cela serait utile): trouvé ceci sur github publié sous une licence MIT:
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.
la source
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
la source
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
Cette déclaration fera y faire référence à 5 de x
x -------------> 5 <----------- 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
la source
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.
la source
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.
la source
x = (1, 2)
puis essayez de muterx
, ce n'est pas possible. Une façon que j'ai trouvée pour vérifier la mutabilité est quehash
cela fonctionne au moins pour les objets intégrés.hash(1)
hash('a')
hash((1, 2))
hash(True)
tout fonctionne, ethash([])
hash({})
hash({1, 2})
tout ne fonctionne pas.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.hash
mé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.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
la source
x=10
est simplement une autre mission , alors qu'ilx[2] = 5
appelle une méthode mutator.int
les objets manquent simplement de méthodes de mutation , mais la sémantique de l'affectation de python ne dépend pas du type