__contains__ classmethod?
Peter Otten
__peter__ at web.de
Mon Dec 18 17:39:46 EST 2017
Peter Otten wrote:
> Tim Chase wrote:
>
>> Playing around, I had this (happens to be Py2, but gets the same
>> result in Py3) code
>>
>> class X(object):
>> ONE = "one"
>> TWO = "two"
>> _ALL = frozenset(v for k,v in locals().items() if k.isupper())
>> @classmethod
>> def __contains__(cls, v):
>> return v in cls._ALL
>> print(dir(X))
>> print(X._ALL)
>>
>> Running this gives
>>
>> ['ONE', 'TWO', '_ALL', '__class__', '__contains__', '__delattr__',
>> '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__',
>> '__getattribute__', '__gt__', '__hash__', '__init__', '__le__',
>> '__lt__', '__module__', '__ne__', '__new__', '__reduce__',
>> '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__',
>> '__str__', '__subclasshook__', '__weakref__']
>>
>> And then, depending on whether it's Py2 or Py3, I get either
>>
>> frozenset({'one', 'two'})
>> frozenset(['two', 'one'])
>>
>> Which I expect. Hey, look. There's a __contains__ method. And it
>> has been specified as a @classmethod. So I want to test it:
>>
>> print("one" in X)
>>
>> However that fails with
>>
>> Traceback (most recent call last):
>> File "x.py", line 10, in <module>
>> print("one" in X)
>> TypeError: argument of type 'type' is not iterable
>>
>> My understanding was that "in" makes use of an available __contains__
>> but something seems to preventing Python from finding that.
>>
>> What's going on here?
>
> Like __next__ method is looked up in the class, and for the class X that
> would be its metaclass, type.
Sorry, I realize that this sentence is impossible to understand. Random832
states the problem more clearly.
[Digression] I mentioned the __next__() method specifically because I
remembered that you could override its predecessor next() in the instance.
However, the examples below demonstrate that this behaviour is limited to
classic classes:
>>> def demo(C):
... c = C()
... c.next = lambda: 42
... for x in c:
... print x
... break
...
>>> class A:
... def __iter__(self):
... return self
... def next(self):
... return "class"
...
>>> demo(A)
42
>>> class B(A, object): pass
...
>>> demo(B)
class
> So you have to adjust that:
>
> $ cat contains.py
> class XType(type):
> def __contains__(cls, v):
> print("metaclass")
> return v in cls._ALL
>
>
> class X(metaclass=XType):
> ONE = "one"
> TWO = "two"
> _ALL = frozenset(v for k, v in locals().items() if k.isupper())
>
> @classmethod
> def __contains__(cls, v):
> print("instance")
This should rather be
print("class")
> return v in cls
>
> print("one" in X, "one" in X())
> print("three" in X, "three" in X())
> $ python3 contains.py
> metaclass
> instance
> metaclass
> True True
> metaclass
> instance
> metaclass
> False False
More information about the Python-list
mailing list