[IronPython] in operator calls __getitem__ on class that has __len__ and __iter__ defined

Michael Foord fuzzyman at voidspace.org.uk
Fri Nov 21 00:34:11 CET 2008


Michael Foord wrote:
> Dino Viehland wrote:
>> Interesting, the docs would seem to indicate our behavior is correct:
>>
>> For user-defined classes which do not define __contains__() and do 
>> define __getitem__(), x in y is true if and only if there is a 
>> non-negative integer index i such that x == y[i], and all lower 
>> integer indices do not raise IndexError exception. (If any other 
>> exception is raised, it is as if in raised that exception).
>>
>> If and only if is pretty strong language ☺  But we can start looking 
>> for __iter__ after looking for __contains__.
>>   
>

Wait. The definition above defines the conditions for 'in' with 
sequences (integer indexes) when __getitem__ is defined and __contains__ 
isn't. And in fact, the value 'x' is not passed to __getitem__ at all - 
but is *returned* from __getiem__ for an integer index 'i'. Passing 'x' 
to __getitem__ is definitely incorrect. :-)

If a class defines __getitem__ and neither __iter__ nor __contains__ 
then Python will start calling __getitem__ with integers until x is 
returned or an IndexError is raised.

Michael

> It sounds like a doc bug then.
>
> 'in' has different meanings for mapping type containers (in refers to 
> keys) and sequences (in refers to values). For a sequence 'in' 
> naturally and obviously means 'is a value contained in the sequence', 
> and passing the value to __getitem__ clearly gives the wrong behaviour.
>
> For mapping types, passing the value to __getitem__ would be correct - 
> unfortunately there is no way to tell the types of containers apart if 
> they are pure Python classes (except in Python 2.6 where we have 
> Abstract Base Classes). Explicitly implementing __contains__ is always 
> better than relying on the fallback.
>
> Michael
>
>> From: users-bounces at lists.ironpython.com 
>> [mailto:users-bounces at lists.ironpython.com] On Behalf Of Glenn Jones
>> Sent: Thursday, November 20, 2008 7:23 AM
>> To: users at lists.ironpython.com
>> Subject: [IronPython] in operator calls __getitem__ on class that has 
>> __len__ and __iter__ defined
>>
>> Yet another weirdness, but not a blocker for us:
>>
>> With this object:
>>
>> class o(object):
>>   def __iter__(self):
>>     print "iter"
>>     return iter([1, 2, 3])
>>   def __getitem__(self, index):
>>     print "getitem"
>>     return [1, 2, 3][index]
>>   def __len__(self):
>>     print "len"
>>     return 3
>>
>> CPython:
>>  
>>>>> p = o()
>>>>> 1 in p
>>>>>         
>> iter
>> True
>>
>> IronPython 2 source drop 43741:
>>  
>>>>> p = o()
>>>>> 1 in p
>>>>>         
>> getitem
>> True
>>
>> It looks like CPython is treating it like a sequence and IronPython 2 
>> is treating it like a dict.
>>
>> We have worked around this by implementing __contains__
>>
>> Raised as Issue 19678 on CodePlex.
>>
>>
>> PS: How can we format code blocks on CodePlex?
>>
>> Glenn & Orestis
>> _______________________________________________
>> Users mailing list
>> Users at lists.ironpython.com
>> http://lists.ironpython.com/listinfo.cgi/users-ironpython.com
>>   
>
>


-- 
http://www.ironpythoninaction.com/
http://www.voidspace.org.uk/blog





More information about the Ironpython-users mailing list