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.
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]
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:
Type immuable commun:
int()
, float()
, complex()
str()
, Tuple()
, frozenset()
, bytes()
Type mutable commun (presque tout le reste):
list()
, bytearray()
set()
dict()
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)
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é.
int
s et float
s 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 list
d
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 Tuple
s 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.
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?
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.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.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.
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.
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.
Voici les objets de liste dans python qui sont de type mutable:
list
Dictionary
Set
bytearray
user defined classes
Voici les objets de liste dans python de type immuable:
int
float
decimal
complex
bool
string
Tuple
range
frozenset
bytes
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
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é.)
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:
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?
Que peut-on?
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?
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 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.
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.
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
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
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