Question

Considérez ceci :

a = {...} # a is an dict with arbitrary contents
b = a.copy()
  1. Quel rôle mutabilité jeu dans les clés et les valeurs des dicts?
  2. Comment puis-je assurer que les modifications aux clés ou des valeurs d'un dict ne sont pas répercutées dans l'autre?
  3. Comment est-ce lié à la hashable contrainte du clés dict?
  4. Y at-il des différences de comportement entre Python 2.x et 3.x Python?

Comment puis-je vérifier si un type est muable en Python?

Était-ce utile?

La solution

1) Les clés ne doivent pas être mutable, sauf si vous avez une classe définie par l'utilisateur qui est hashable mais aussi mutable. C'est tout ce qui est forcé sur vous. Cependant, en utilisant un hashable, objet mutable comme une clé dict pourrait être une mauvaise idée.

2) en ne partageant pas les valeurs entre les deux dicts. Il est OK pour partager les clés, car ils doivent être immuables. Copie du dictionnaire, dans le sens du module copy, est sans aucun doute en toute sécurité. Appelant le constructeur dict fonctionne ici aussi: b = dict(a). Vous pouvez également utiliser des valeurs immuables.

3) Tous les types intégrés sont immuables hashable. Tous les types intégrés mutables ne sont pas indexables. Pour un objet à hashable, il doit avoir le même hachage sur sa durée de vie, même si elle a subi une mutation.

4) Non pas que je suis au courant; Je décris 2.x.

Un type est mutable si elle n'est pas immuable. Un type est immuable si elle est un type intégré immuable: str, int, long, bool, float, tuple, et probablement quelques autres que je oublie. types définis par l'utilisateur sont toujours muable.

Un objet est mutable si elle n'est pas immuable. Un objet est immuable si elle consiste, récursive, seulement des sous-objets immuables typées. Ainsi, un tuple de listes est mutable; vous ne pouvez pas remplacer les éléments du tuple, mais vous pouvez les modifier via l'interface de la liste, en changeant les données d'ensemble.

Autres conseils

Il n'y a pas en fait une telle chose comme mutabilité ou immuabilité au niveau de la langue en Python. Certains objets ne fournissent aucun moyen de les changer (par exemple, des chaînes et tuples.), Et sont donc efficacement immuable, mais il est purement conceptuelle; il n'y a pas de propriété au niveau de la langue indiquant cela, ni à votre code ni à Python.

immutabilité n'est pas réellement pertinent pour dicts; il est parfaitement bien d'utiliser des valeurs mutables comme clés. Ce qui importe est la comparaison et le hachage: l'objet doit toujours rester égal à lui-même. Par exemple:

class example(object):
    def __init__(self, a):
        self.value = a
    def __eq__(self, rhs):
        return self.value == rhs.value
    def __hash__(self):
        return hash(self.value)

a = example(1)
d = {a: "first"}
a.data = 2
print d[example(1)]

Ici, example est pas immuable; nous modifions avec a.data = 2. Et pourtant, nous l'utilisons comme clé d'un hachage sans aucun problème. Pourquoi? La propriété que nous changeons n'a pas d'effet sur l'égalité:. Le hachage est inchangé, et example(1) est toujours égal à example(1), en ignorant toutes les autres propriétés

L'utilisation la plus courante de c'est la mise en cache et memoization:. Ayant une propriété mise en mémoire cache ou non ne change pas logiquement l'objet, et n'a généralement pas d'effet sur l'égalité

(je vais arrêter ici -. S'il vous plaît ne demande pas cinq questions à la fois)

Il y a MutableSequence, MutableSet, MutableMapping dans le module collections . Ce qui peut être utilisé pour vérifier la mutabilité des types déjà créés.

issubclass(TYPE, (MutableSequence, MutableSet, MutableMapping))

Si vous voulez utiliser sur les types définis par l'utilisateur, le type doit être héritée soit d'un d'entre eux ou enregistré en tant que sous-classe virtuelle.

class x(MutableSequence):
    ...

ou

class x:
    ...

abc.ABCMeta.register(MutableSequence,x)

Il n'y a vraiment pas de garantie qu'un type qui est hashable est également immuable, mais à tout le moins, la mise en œuvre correctement __hash__ exige que le type est immuable, en ce qui concerne son propre hachage, et à l'égalité. Ce n'est pas appliquée d'une manière particulière.

Cependant, nous sommes tous les adultes. Il serait imprudent de mettre en œuvre __hash__ à moins que vous vouliez dire vraiment il. En gros, cela revient tout simplement à dire que si un type peut effectivement être utilisé comme clé de dictionnaire, il est destiné à être utilisé de cette façon.

Si vous cherchez quelque chose qui est comme un dict, mais aussi immuable, puis

  1. dict clés doit être indexables, ce qui implique qu'ils ont une immuable hachage valeur. dict valeurs peut ou non être mutable; Toutefois, si elles sont mutable cela a un impact à votre deuxième question.

  2. « Les modifications des clés » ne sont pas répercutées entre les deux dicts. Les modifications apportées à des valeurs immuables, telles que les chaînes ne seront pas également pris en compte. Les modifications apportées à des objets mutables, tels que les classes définies par l'utilisateur seront pris en compte parce que l'objet est stockée par identifiant (à savoir de référence).

    class T(object):
      def __init__(self, v):
        self.v = v
    
    
    t1 = T(5)
    
    
    d1 = {'a': t1}
    d2 = d1.copy()
    
    
    d2['a'].v = 7
    d1['a'].v   # = 7
    
    
    d2['a'] = T(2)
    d2['a'].v   # = 2
    d1['a'].v   # = 7
    
    
    import copy
    d3 = copy.deepcopy(d2) # perform a "deep copy"
    d3['a'].v = 12
    d3['a'].v   # = 12
    d2['a'].v   # = 2
    
  3. Je pense que cela est expliqué par les deux premières réponses.

  4. Pas que je sache à cet égard.

quelques réflexions supplémentaires :

Il y a deux choses à savoir pour comprendre le comportement de touches : Les clés doivent être hashable (ce qui signifie qu'ils mettent en œuvre object.__hash__(self) ) et ils doivent aussi être « comparable » (ce qui signifie qu'ils mettent en œuvre quelque chose comme object.__cmp__(self) ). Un à emporter des documents importants: par défaut, les fonctions de hachage d'objets définis par l'utilisateur retour id() .

Prenons cet exemple:

class K(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y

k1 = K(1, 2)
d1 = {k1: 3}
d1[k1] # outputs 3
k1.x = 5
d1[k1] # KeyError!  The key's hash has changed!
k2 = K(2, 1)
d1[k2] # KeyError!  The key's hash is right, but the keys aren't equal.
k1.x = 1
d1[k1] # outputs 3

class NewK(object):
  def __init__(self, x, y):
     self.x = x
     self.y = y
  def __hash__(self):
     return self.x + self.y
  def __cmp__(self, other):
     return self.x - other.x

nk1 = NewK(3, 4)
nd1 = {nk1: 5}
nd1[nk1] # outputs 5
nk2 = NewK(3, 7)
nk1 == nk2 # True!
nd1[nk2] # KeyError! The keys' hashes differ.
hash(nk1) == hash(nk2) # False
nk2.y = 4
nd1[nk2] # outputs 5

# Where this can cause issues:
nd1.keys()[0].x = 5
nd1[nk1] # KeyError! nk1 is no longer in the dict!
id(nd1.keys()[0]) == id(nk1)  # Yikes. True?!
nd1.keys()[0].x = 3
nd1[nk1]  # outputs 5
id(nd1.keys()[0]) == id(nk1)  # True!

Les valeurs sont beaucoup plus faciles à comprendre, les références stocke dict aux objets. Lisez les sections sur hashable. Des choses comme les chaînes sont immuables, si vous les « changement », le dict vous l'avez changé dans les références maintenant un nouvel objet. Les objets qui sont mutables peuvent être « modifiés en place », d'où la valeur des deux dicts changera.

d1 = {1: 'a'}
d2 = d1.copy()
id(d1[1]) == id(d2[1]) # True
d2[1] = 'z'
id(d1[1]) == id(d2[1]) # False

# the examples in section 2 above have more examples of this.

Quoi qu'il en soit, voici les principaux points de tout cela:

  • Pour touches , il ne peut pas être mutabilité , mais hashability et la comparabilité , que vous vous souciez.
  • Vous souciez mutabilité des valeurs, parce que par définition, la valeur d'un objet mutable peut être modifié sans changer la référence.

Je ne pense pas qu'il y ait une façon générale à l'épreuve ou l'autre de ces points. Les tests d'aptitude dépendra de votre cas d'utilisation. Par exemple, il peut être suffisant pour vérifier qu'un objet ou ne met pas en œuvre les fonctions __hash__ et la comparaison (__eq__ ou __cmp__). Comme sage, vous pourriez être en mesure de « vérifier » la méthode de __setattr__ d'un objet d'une certaine façon de déterminer si elle est mutable.

Dicts sont des ensembles non ordonnés de clé: paires de valeurs. Les clés doivent être immuable, et par conséquent hashable. Pour déterminer si un objet est hashable, vous pouvez utiliser la fonction hash():

>>> hash(1)
1
>>> hash('a')
12416037344
>>> hash([1,2,3])
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> hash((1,2,3))
2528502973977326415
>>> hash({1: 1})
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'dict'

Les valeurs, d'autre part, peut être un objet. Si vous devez vérifier si un objet est immuable, alors j'utiliser hash().

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top