I need to simulate enums in Python, and did it by writing classes like:
class Spam(Enum):
k = 3
EGGS = 0
HAM = 1
BAKEDBEANS = 2
Now I’d like to test if some constant is a valid choice for a particular Enum-derived class, with the following syntax:
if (x in Foo):
print("seems legit")
Therefore I tried to create an “Enum” base class where I override the __contains__ method like this:
class Enum:
"""
Simulates an enum.
"""
k = 0 # overwrite in subclass with number of constants
@classmethod
def __contains__(cls, x):
"""
Test for valid enum constant x:
x in Enum
"""
return (x in range(cls.k))
However, when using the in keyword on the class (like the example above), I get the error:
TypeError: argument of type 'type' is not iterable
Why that? Can I somehow get the syntactic sugar I want?
When you use special syntax like
a in Foo, the__contains__method is looked up on the type ofFoo. However, your__contains__implementation exists onFooitself, not its type.Foo‘s type istype, which doesn’t implement this (or iteration), thus the error.The same situation occurs if you instantiate an object and then, after it is created, add a
__contains__function to the instance variables. That function won’t be called:Yes. As mentioned above, the method is looked up on
Foo‘s type. The type of a class is called a metaclass, so you need a new metaclass that implements__contains__.Try this one:
As you can see, the methods on a metaclass take the metaclass instance — the class — as their first argument. This should make sense. It’s very similar to a classmethod, except that the method lives on the metaclass and not the class.
Inheritance from a class with a custom metaclass also inherits the metaclass, so you can create a base class like so: