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