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 -- https://mail.python.org/mailman/listinfo/python-list