<div dir="ltr">On 19 February 2013 06:51, Jean-Michel Pichavant <span dir="ltr"><<a href="mailto:jeanmichel@sequans.com" target="_blank">jeanmichel@sequans.com</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote">
<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Greetings,<br>
<br>
I opened something like a month ago a thread about hash functions and how I could write classes which instances can be safely used as dictionary keys.<br>
I though I had it but when I read back my code, I think I wrote yet another bug.<br>
<br>
Consider the following simple (buggy) class, python 2.5<br>
<br>
class FooSet(object):<br>
    """Define an algorithm set, containing pdcch/pdsch (or none)."""<br>
    def __init__(self, pdcch, pdsch):<br>
        self.pdcch = bool(pdcch)<br>
        self.pdsch = bool(pdsch)<br>
    # __hash__ and __eq__ allow to use the object as a dictionary key<br>
    def __hash__(self):<br>
        return hash((self.pdcch, self.pdsch))<br>
    def __eq__(self, other):<br>
        return hash(self) == hash(other)<br>
<br>
Can you confirm that using the hash function for testing equality is a very bad idea ?<br></blockquote><div><br></div><div style>Yes - it is a *very* bad idea. A hash by definition can produce collisions, since you are taking much larger amount of data and are trying to represent it in a smaller amount of space. It's effectively lossy compression - you can never reliably get the original back.</div>
<div style> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
One obvious solution would be:<br>
<br>
def __eq__(self, other):<br>
    return self.pdsch = other.pdsch and self.pdcch == other.pdcch<br></blockquote><div><br></div><div style>This is a correct and the simplest way to do it.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">

But I was looking for a "standard" solution, that I could use for basically all my container classes<br>
<br>
So I came up with these ones:<br>
<br>
def __hash__(self):<br>
    return hash(tuple(vars(self).values()))<br>
def __eq__(self, other):<br>
    return vars(self) == vars(other)<br>
<br>
But I'm not sure about vars(self).values(), I don't really care about the order of the values, but I need to be sure that for 2 equal dictionaries, they will both return their values in the same order.<br>
And that's the point, I'm not sure at all.<br></blockquote><div><br></div><div style>You cannot rely on this. Dictionaries are unordered, and the order that items are added affects the order that the elements will be iterated over. You could sort the vars by name (thus giving the stable order you need) but there's another flaw - vars() contains more than just the attributes you set.</div>
<div><br></div><div><div>>>> class A():</div><div>...     pass</div><div>...</div></div><div><div>>>> vars(A)</div><div>mappingproxy({'__qualname__': 'A', '__dict__': <attribute '__dict__' of 'A' objects>, '__module__':</div>
<div>'__main__', '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None})</div></div><div><br></div><div style>So by using vars you are preventing instances of subclasses of your class from comparing equal to each other (or to instances of the base class).</div>
<div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">
Additionally,  If I'm making things much more complicated than they need to be, let me know.<br></blockquote><div><br></div><div style>You are. There are ways to achieve what you want, but it requires a lot more setup and discipline. The simplest way is probably to have a _equal_fields() method that subclasses override, returning a tuple of the attributes that should be hashed. Then in __hash__() and __eq__ you iterate over the returned tuple, get the value for each attribute and either hash or compare.</div>
<div style><br></div><div style>Of course, you have to take into account in __eq__ that the other instance may not have the same attributes (e.g. self is a subclass that uses extra attributes in its __hash__ and __eq__).</div>
<div style><br></div><div style>Tim Delaney</div></div></div></div>