satwikkansal / wtfpython

What the f*ck Python? 😱
Do What The F*ck You Want To Public License
35.7k stars 2.65k forks source link

Deep Down, explain or remove example #301

Open merriam opened 1 year ago

merriam commented 1 year ago

This example:

    >>> hash(WTF()) == hash(WTF()) # hashes _should_ be different as well
    True
  1. Is not explained.
  2. Has result contrary to comment.
  3. May not be explainable.

It may not be explainable because the documentation of __hash__ is borked. It never discusses what a hash of an instance entails. It also adds the bizarre restriction to user defined hash and equality functions that "x==y implies ... x is y" so all custom hash functions and custom equality must incorporate id(self) and provide no more or less information than id(self) or cause issues as in "Disorder within Order".

The code snippet works simply because the WTF() on both sides of an equality are likely to have the same id (memory reuse). And that the hash happens to return the same value for the same instance type at the same location.

Consider this example showing that the contents are ignored:

    >>> hash(a)
    8768377079588
    >>> a.new_field = 42
    >>> hash(a)
    8768377079588

Therefore, I recommend removing this example and just relating to a later wtff. (f is for flying).

merriam commented 1 year ago

In retrospect, maybe the example could be expanded and explained.

>> class WTF(): pass
>> class More_WTF():
  ..    def __init__(i):
  ..         self.i = i
>> hash(WTF()) == hash(More_WTF(42))
True

The WTF() is created, used by hash(), then discarded. The memory location is then reused for More_WTF(). hash() on an instance hashes only its id().

satwikkansal commented 1 year ago

Thanks for starting the discussion, I think

  1. It's because of memory reuse, which is mentioned in the explanation.
  2. When you say the result is contrary, do you mean hash(WTF()) == hash(WTF()) returns False?
  3. I agree that the example set-up is contrived, and even overlaps with others, but it was done on purpose. If more people think that's not a constructive way, I can change that in the next revision. Otherwise, I do realise that the explanation can be improved so will do that anyway.
>> class WTF(): pass
>> class More_WTF():
  ..    def __init__(i):
  ..         self.i = i
>> hash(WTF()) == hash(More_WTF(42))
True

And yeah, I agree this is a simpler way to convey it, so I'll consider this too :)

merriam commented 1 year ago

Thank you for thinking about5 this.

The issues is 'equivalence'. By default, instances use is. To make them compare, you add an __eq__, which is supposed to implement a == b in such a way as:

satwikkansal commented 1 year ago

a == a, a == b implies b == a, a ==b and b==c implies a == c. In Disorder within Order, you implement a "permissive equality" across types, where a ==b and b == c does NOT imply a == c. Once that rule breaks, sets and dictionaries fail to work.

I think thats's a good way to explain it.

Also, thanks for sharing your notes :)