__contains__ classmethod?

Peter Otten __peter__ at web.de
Mon Dec 18 16:48:56 EST 2017


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. 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")
        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