Seite 1 von 1

Hä? dict() mit mutablen Keys?

Verfasst: Samstag 15. Oktober 2005, 11:30
von henning
Moin mal wieder!
Wie wir ja alle Wissen, darf man für ein dict() aus nachvollziehbaren Gründen nur immutable Objekte verwenden (tuples, strings oder numerische Werte z.B.)

Durch Zufall habe ich jetzt aber rausgefunden, dass ich sehr wohl Klasseninstanzen als keys hineinstecken kann (Python 2.3.5) und nach einem ersten Versuch auch alles wunderbar funktioniert!

Was ist davon zu halten? Ich würde mal raten, dass der Hash aus der RAM-Adresse des Objekts berechnet wird, meint ihr, ich darf mich darauf verlassen und das so verwenden?

Hier ein bisschen Code mit Ausgabe:

Code: Alles auswählen

class Mutable:
  def __init__(self, start=42):
    self.mutator = start
  def mutate(self):
    self.mutator += 1

a = Mutable(-56)
b = Mutable(12)

d = {a: "This is a", b: "This is b"}
print d
a.mutate()
d[Mutable(88)] = "This is c"
b.mutate()
print d

a.something = 46
b.something_else = 1237
print d

# Borg a and b together, now they even share state!
b.__dict__ = a.__dict__ 
print d

del b
print d

Code: Alles auswählen

{<__main__.Mutable instance at 0xb7ded7cc>: 'This is b', <__main__.Mutable instance at 0xb7ded82c>: 'This is a'}
{<__main__.Mutable instance at 0xb7ded7cc>: 'This is b', <__main__.Mutable instance at 0xb7ded82c>: 'This is a', <__main__.Mutable instance at 0xb7ded8ec>: 'This is c'}
{<__main__.Mutable instance at 0xb7ded7cc>: 'This is b', <__main__.Mutable instance at 0xb7ded82c>: 'This is a', <__main__.Mutable instance at 0xb7ded8ec>: 'This is c'}
{<__main__.Mutable instance at 0xb7ded7cc>: 'This is b', <__main__.Mutable instance at 0xb7ded82c>: 'This is a', <__main__.Mutable instance at 0xb7ded8ec>: 'This is c'}
{<__main__.Mutable instance at 0xb7ded7cc>: 'This is b', <__main__.Mutable instance at 0xb7ded82c>: 'This is a', <__main__.Mutable instance at 0xb7ded8ec>: 'This is c'}
This is a

Verfasst: Samstag 15. Oktober 2005, 14:15
von Joghurt
Nun, da die Instanzen von der Hash referenziert werden, werden sie nicht freigegeben und belegen deshalb Speicher; deshalb ist ihr id auch eindeutig, solange sie existieren.

Ich würde jedoch davon abraten, es zu nutzen, das ganze sieht sehr wackelig aus.

Re: Hä? dict() mit mutablen Keys?

Verfasst: Samstag 15. Oktober 2005, 19:41
von ProgChild
henning hat geschrieben:Was ist davon zu halten? Ich würde mal raten, dass der Hash aus der RAM-Adresse des Objekts berechnet wird, meint ihr, ich darf mich darauf verlassen und das so verwenden?
Du kannst dich darauf verlassen, wenn du weist, was du machst. Wenn du eine eigene Klasse schreibst, dann ergibt ein Vergleich (a == b) zwei Klasseninstanzen nur dann "Wahr", wenn es sich um die selbe Klasseninstanz, also um die selbe Speicheraddresse handelt.

Code: Alles auswählen

Python 2.4.2 (#1, Oct 15 2005, 20:02:37) 
[GCC 3.3.6 (Gentoo 3.3.6, ssp-3.3.6-1.0, pie-8.7.8)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> class c:
...     pass
... 
>>> a = c()
>>> b = c()
>>> a == b
False
>>> b = a
>>> a == b
True
>>>
Wenn du willst, dass der Inhalt der beiden Klasseninstanzen verglichen wird, musst du deinen eigenen Vergleichsoperator schreiben.

Re: Hä? dict() mit mutablen Keys?

Verfasst: Sonntag 16. Oktober 2005, 00:56
von BlackJack
henning hat geschrieben:Moin mal wieder!
Wie wir ja alle Wissen, darf man für ein dict() aus nachvollziehbaren Gründen nur immutable Objekte verwenden (tuples, strings oder numerische Werte z.B.)

Durch Zufall habe ich jetzt aber rausgefunden, dass ich sehr wohl Klasseninstanzen als keys hineinstecken kann (Python 2.3.5) und nach einem ersten Versuch auch alles wunderbar funktioniert!

Was ist davon zu halten? Ich würde mal raten, dass der Hash aus der RAM-Adresse des Objekts berechnet wird, meint ihr, ich darf mich darauf verlassen und das so verwenden?
Wenn Du etwas als Key verwenden willst, dann musst Du sicherstellen, das der Vergleich mit ``==`` immer mit dem Hash-Wert korrespondiert. Du darfst bei zwei verschiedenen Objekten, bei denen ``==`` wahr ergibt keine zwei verschiedenen Hashwerte bekommen.

Wenn Du weder die Vergleiche noch die Hash-Funktion überschreibst, dann kannst Du ohne weiteres die Objekte als Schlüssel benutzen. Sowohl Vergleich als auch Hashwert basieren dann auf der Objektidentität. Wenn Du den Vergleich überschreibst, dann sollte die Hash-Funktion über die Objekte berechnet werden die beim Vergleich eine Rolle spielen.

Code: Alles auswählen

class Person:
    def __init__(self, name, surname, hobbies):
        self.name = name
        self.surname = surname
        self.hobbies = hobbies
    
    def __cmp__(self, other)
        return cmp(self.surname, other.surname) or cmp(self.name, other.name)
    
    def __hash__(self):
        return hash((self.surname, self.name))

Re: Hä? dict() mit mutablen Keys?

Verfasst: Sonntag 16. Oktober 2005, 13:33
von ProgChild
Ich muss mich korregieren. Bei Dicts wird wirklich __hash__ benutzt... :roll:
henning hat geschrieben:Was ist davon zu halten? Ich würde mal raten, dass der Hash aus der RAM-Adresse des Objekts berechnet wird, meint ihr, ich darf mich darauf verlassen und das so verwenden?

Ich schätzte, dass die Speicheraddresse der Klasseninstanz als Hash Wert ist. Das ist okay so und sollte sicher sein, da die Addresse sich wärend der Exsistenz der Instanz nicht ändert.
henning hat geschrieben:Wie wir ja alle Wissen, darf man für ein dict() aus nachvollziehbaren Gründen nur immutable Objekte verwenden (tuples, strings oder numerische Werte z.B.)
Wenn du aber die Funktion __hash__ definierst, dann must du sicherstellen, dass dein Objekt Immuteable ist und sich der Hash Wert, wärend der Exsistenz einer Instanz nicht verändert.

Alles nachzulesen hier unter dem Stichwort __hash__.
Link hat geschrieben:If a class defines mutable objects and implements a __cmp__() or __eq__() method, it should not implement __hash__(), since the dictionary implementation requires that a key's hash value is immutable (if the object's hash value changes, it will be in the wrong hash bucket).

Code: Alles auswählen

#!/usr/bin/env python
    # -*- encoding: latin1 -*-
    d = {}
    
    class c:
-       def __init__( self, val =  3 ):
|-          self.val = val
    
    a = c()
    d[a] = "Nummer 1"
    
    print d
    
    a.val = 7
    # Hier müsste sich der Hash Wert geändert haben, wenn etwas
    # anderes als die reine Speicheraddresse zum Vergleich
    # benutzt würde...
    
    b = c()
    d[b] = "Nummer 2"
    
    print d[a]
    print d[b]
    
    print a
    print b
    
    print d
Ausgabe bei mir:
{<__main__.c instance at 0xb7c06e0c>: 'Nummer 1'}
Nummer 1
Nummer 2
<__main__.c instance at 0xb7c06e0c>
<__main__.c instance at 0xb7c0c30c>
{<__main__.c instance at 0xb7c0c30c>: 'Nummer 2', <__main__.c instance at 0xb7c06e0c>: 'Nummer 1'}