-
Notifications
You must be signed in to change notification settings - Fork 2.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update docs: mention setting __hash__ = None
for custom __eq__
method
#2287
Conversation
I thought the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great to me! Thanks, @ixje! :-)
Just some docs, so I'll give the others involved in #2191 a few days to check, and merge if there are no objections :-) |
One thing I've noticed here, though I don't know if anybody else cares, are long lines - significantly longer than the rest of the document. |
I would say it's fine but a bit hard to grasp from first read. Can this be shortened to something like this: A class that overrides ? |
Wording is perfectly fine. I was thinking more like what is just above the block you changed - a single sentence broken into two lines.
So just change a few spaces into new lines + indentation. |
My point was that So phrase
describes class with the bug. |
I actually think @sizmailov is right here. I don't think that for |
That's worth a discussion, but if that's the case, that it's strictly incorrect, then we should probably add something to pybind11, and find a way to do so that convinces @wjakob.
That's not really true, though. The default hash will be based on object identity, so any sane >>> class X:
... def __eq__(self, other):
... # Fine, yes, normally, you'd compare `self.value == other.value` or so.
... # The important point is that `__eq__` is less restrictive than `is`.
... return True
...
>>> x = X()
>>> x in {x}
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'X'
>>> X.__hash__ = object.__hash__
>>> x = X()
>>> x in {x}
True The weird or wrong situation happens the other way around:
|
__hash__ = None
for custom __eq__
method
Here is the quote from python doc:
|
Yes, true. I'm not saying it might not be a problem, but your example of For the record, adding this (untested) code in the right place ( if ((name == "__eq__" || strcmp(name, "__eq__") == 0) && !attr("__dict__").contains("__hash__")) {
attr("__hash__") = py::none();
} |
Probably you are right, not exactly in this form, it should be a bit more complex example, like Once again, At the moment |
This will still have the same
It might, actually, no? Because almost always™ (?), |
Nvm, @sizmailov is correct, since this won't speed up other things than |
It turns out that there is only one function to patch, the For benchmark purposes I've copied It seems like the obtained data is not reliable. 99% of time My best guess is that python runtime is responsibile for this discrepancy. (And as always there is a chance that my benchmarks are flawed) Here is one of the outputs:
CPU scaling is disabled, I've tried to run on (almost) idle system |
Thank you for looking at performance @sizmailov. While you are at it: does the size of the |
By the way, one thing worth investigating would be to factor out the whole middle part
into a non-templated function (maybe in the |
Here are the numbers
extracted functioninline void add_class_method(object& klass, const char *name_, const cpp_function &cf) {
klass.attr(cf.name()) = cf;
if (strcmp(name_, "__eq__") == 0 && !klass.attr("__dict__").contains("__hash__")) {
klass.attr("__hash__") = none();
}
} |
Not bad, a 5% size decrease. A good reminder how much we are paying for code that is generic but ends up being templated over and over again. I would be happy with this change (with extra function), please go ahead and put it into the PR. Super-minor: indentation is wrong (4 spaces), and can you call it "cls" or something like that? (it's also called |
|
||
.. note:: | ||
|
||
A class that overrides ``__eq__()`` and does not define ``__hash__()`` will have its ``__hash__()`` implicitly set. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: When a class overrides __eq__()
and does not define __hash__()
, pybind11 will automatically add a __hash__()
. (I think this rewording makes it more clear that this behavior is something pybind11 does.)
Great job, @sizmailov! Thank you so much for investigating and getting us some numbers! :-)
On the long run, this might even be a customization point for users? |
closing in favour of #2291 |
close #2191