Hä? dict() mit mutablen Keys?

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
henning
User
Beiträge: 274
Registriert: Dienstag 26. Juli 2005, 18:37

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
Joghurt
User
Beiträge: 877
Registriert: Dienstag 15. Februar 2005, 15:07

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.
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

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.
Zuletzt geändert von ProgChild am Sonntag 16. Oktober 2005, 13:20, insgesamt 1-mal geändert.
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))
ProgChild
User
Beiträge: 210
Registriert: Samstag 9. April 2005, 10:58
Kontaktdaten:

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'}
Antworten