homepage

This issue tracker has been migrated to GitHub , and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: __contains__ method behavior
Type: enhancement Stage:
Components: Interpreter Core Versions: Python 3.3
process
Status: closed Resolution: rejected
Dependencies: Superseder:
Assigned To: Nosy List: JBernardo, alex, benjamin.peterson, georg.brandl, pitrou, rhettinger
Priority: normal Keywords:

Created on 2011年12月28日 09:11 by JBernardo, last changed 2022年04月11日 14:57 by admin. This issue is now closed.

Messages (12)
msg150283 - (view) Author: João Bernardo (JBernardo) * Date: 2011年12月28日 09:11
Hi, I'm working on a class which implements the __contains__ method but the way I would like it to work is by generating an object that will be evaluated later.
It'll return a custom object instead of True/False
class C:
 def __contains__(self, x):
 return "I will evaluate this thing later... Don't bother now"
but when I do:
>>> 1 in C()
True
It seems to evaluate the answer with bool!
Reading the docs (http://docs.python.org/py3k/reference/expressions.html#membership-test-details) It says:
"`x in y` is true if and only if `y.__contains__(x)` is true."
It looks like the docs doesn't match the code and the code is trying to mimic the behavior of lists/tuples where "x in y" is the same as
any(x is e or x == e for e in y)
and always yield True or False.
There is a reason why it is that way?
Thanks!
msg150284 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2011年12月28日 09:24
"an object is true" is a short way of saying "bool(obj) is True". So the docs match the behavior.
Returning the actual object instead of True/False from the "in" operator is a feature request.
msg150285 - (view) Author: João Bernardo (JBernardo) * Date: 2011年12月28日 09:29
@Georg Brandl
Oh sorry, now I see... true != True
But still, why is that the default behavior? Shouldn't it use whatever the method returns?
msg150286 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2011年12月28日 10:37
Well, usually what you want *is* a boolean indicating whether the element is in the collection or not.
Being able to overload "in", mostly for metaprogramming purposes, is a request that probably nobody thought of when implementing "in".
msg150296 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2011年12月28日 16:01
I think the idea has some merit. I think it should be well vetted on python-ideas, though. One thing that will certianly weigh against it is that implementation would not be trivial.
msg150303 - (view) Author: João Bernardo (JBernardo) * Date: 2011年12月28日 16:58
I see that every other comparison operator (<, >, <=, >=, ==, !=) except for `is` work the way I expect and is able to return anything.
e.g.
>>> numpy.arange(5) < 3
array([ True, True, True, False, False], dtype=bool)
I didn't checked the code (and probably I'm talking nonsense), but seems like the `in` operator has an extra call to `PyObject_IsTrue` that maybe could be dropped?
Of course it can break code relying on `x in y` being True/False but it would only happen on customized classes.
Another option that won't break code is to add a different method to handle these cases. Something like "__contains_non_bool__", but that'd be a big api change.
msg150304 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2011年12月28日 17:01
2011年12月28日 João Bernardo <report@bugs.python.org>:
>
> João Bernardo <jbvsmo@gmail.com> added the comment:
>
> I see that every other comparison operator (<, >, <=, >=, ==, !=) except for `is` work the way I expect and is able to return anything.
>
> e.g.
>
>>>> numpy.arange(5) < 3
> array([ True, True, True, False, False], dtype=bool)
>
> I didn't checked the code (and probably I'm talking nonsense), but seems like the `in` operator has an extra call to `PyObject_IsTrue` that maybe could be dropped?
I'm not sure what you're referring to, but I doubt that would do the job.
>
> Of course it can break code relying on `x in y` being True/False but it would only happen on customized classes.
>
> Another option that won't break code is to add a different method to handle these cases. Something like "__contains_non_bool__", but that'd be a big api change.
And completely hideous.
msg150307 - (view) Author: João Bernardo (JBernardo) * Date: 2011年12月28日 17:22
Using my poor grep abilities I found that on Objects/typeobject.c
(I replaced some declarations/error checking from the code with ...)
static int
slot_sq_contains(PyObject *self, PyObject *value) {
 ...
 func = lookup_maybe(self, "__contains__", &contains_str);
 if (func != NULL) {
 ...
 res = PyObject_Call(func, args, NULL);
 ...
 if (res != NULL) {
 result = PyObject_IsTrue(res);
 Py_DECREF(res);
 }
 }
 else if (! PyErr_Occurred()) {
 /* Possible results: -1 and 1 */
 result = (int)_PySequence_IterSearch(self, value,
 PY_ITERSEARCH_CONTAINS);
 }
}
 
I don't know if I'm in the right place, but the function returns `int` and evaluates the result to 1 or 0 if __contains__ is found.
I also don't know what SQSLOT means, but unlike the other operators (which are defined as TPSLOT), `slot_sq_contains` is a function returning "int" while `slot_tp_richcompare` returns "PyObject *".
Why is that defined that way?
msg150309 - (view) Author: Georg Brandl (georg.brandl) * (Python committer) Date: 2011年12月28日 18:08
It's defined that way because it's a slot returning a bool, so it doesn't need to return anything except for 0 or 1.
Changing this to return a PyObject would mean that every extension module (i.e. module written in C) that defines a custom __contains__ would need to be adapted. That is the non-trivial implementation that Benjamin was talking about.
msg150314 - (view) Author: Raymond Hettinger (rhettinger) * (Python committer) Date: 2011年12月28日 20:25
-1 on this proposal. It has everyone paying a price for a questionable feature that would benefit very few.
msg150315 - (view) Author: Alex Gaynor (alex) * (Python committer) Date: 2011年12月28日 20:31
For what it's worth I proposed this on -ideas a while ago, the sticking points were what does `not in` do (no one had an answer anyone was happy with for this), and do we need a way to override it from the other perspective (e.g. if I want to do `SpecialObj() in [1, 2, 3]`, is there a way to do that?).
msg150321 - (view) Author: João Bernardo (JBernardo) * Date: 2011年12月28日 22:33
The problem with `not in` is because it must evaluate the result. It's not just another operator like "==" and "!=".
Looks like we're suffering from premature optimization and now it would break a lot of code to make it good.
For my application, I created a different method to generate the object (Not as good as I wanted, but there's no option right now):
`MyClass().has(1)` instead of `1 in MyClass()`
So, if no one comes up with a better idea, this issue should be closed.
Thanks
History
Date User Action Args
2022年04月11日 14:57:25adminsetgithub: 57876
2011年12月28日 22:35:01benjamin.petersonsetstatus: open -> closed
resolution: rejected
2011年12月28日 22:33:51JBernardosetmessages: + msg150321
2011年12月28日 20:31:28alexsetnosy: + alex
messages: + msg150315
2011年12月28日 20:25:09rhettingersetnosy: + rhettinger
messages: + msg150314
2011年12月28日 18:08:10georg.brandlsetmessages: + msg150309
2011年12月28日 17:22:54JBernardosetmessages: + msg150307
2011年12月28日 17:01:13benjamin.petersonsetmessages: + msg150304
2011年12月28日 16:58:28JBernardosetmessages: + msg150303
components: - Documentation
2011年12月28日 16:01:38benjamin.petersonsetmessages: + msg150296
2011年12月28日 10:37:10georg.brandlsetnosy: + pitrou, benjamin.peterson, - docs@python

messages: + msg150286
versions: - Python 3.2
2011年12月28日 09:34:24JBernardosettype: behavior -> enhancement
2011年12月28日 09:29:19JBernardosettype: enhancement -> behavior
messages: + msg150285
versions: + Python 3.2
2011年12月28日 09:24:37georg.brandlsetversions: - Python 3.2
nosy: + georg.brandl

messages: + msg150284

assignee: docs@python ->
type: behavior -> enhancement
2011年12月28日 09:11:10JBernardocreate

AltStyle によって変換されたページ (->オリジナル) /