Reputation: 4084
I want something like this:
class Fruits:
APPLE = 'APPLE'
ORANGE = 'ORANGE'
MANGO = 'MANGO'
# I want to access members easily
print(Fruits.APPLE)
# I want to list all members
print(list(Fruits))
# I want to check if a string belongs to the set
print('POTATO' in Fruits)
I want plain string constants, grouped into a class with:
Python enums do not seem to be suitable, because:
.value
Is this implementable in Python? I suppose I might need some magic methods or metaclasses for this?
Upvotes: 2
Views: 1250
Reputation: 3920
A very basic solution with a meta-class:
class EnumClass(type):
def __iter__(cls):
for key in cls.__dict__:
if not key.startswith('__'):
yield cls.__dict__[key]
def __contains__(cls, item):
return item in cls.__dict__ and not item.startswith('__')
class Fruits(metaclass=EnumClass):
APPLE = 'APPLE'
ORANGE = 'ORANGE'
MANGO = 'MANGO'
# I want to access members easily
print(Fruits.APPLE)
# I want to list all members
print(list(Fruits))
# I want to check if a string belongs to the set
print('POTATO' in Fruits)
print('APPLE' in Fruits)
It's very simplistic and has flaws (e.g., you cannot add methods in Fruits) but it's ok for the use cases you mention.
Upvotes: 1
Reputation: 4365
If you want to implement yourself you can do so with a metaclass
and an __iter__
method.
class Meta(type):
def __new__(cls, name, bases, dct):
# this just iterates through the class dict and removes
# all the dunder methods
cls.members = [v for k, v in dct.items() if not k.startswith('__') and not callable(v)]
return super().__new__(cls, name, bases, dct)
# giving your class an __iter__ method gives you membership checking
# and the ability to easily convert to another iterable
def __iter__(cls):
yield from cls.members
class Fruits(metaclass=Meta):
APPLE = 'APPLE'
ORANGE = 'ORANGE'
MANGO = 'MANGO'
print(Fruits.APPLE)
print('APPLE' in Fruits)
print(list(Fruits))
Upvotes: 2
Reputation: 110561
You want Python "enums":
In [7]: from enum import Enum
In [8]: class Fruits(Enum):
...: APPLE = 'APPLE'
...: ORANGE = 'ORANGE'
...: MANGO = 'MANGO'
...:
In [9]: print (Fruits.APPLE)
Fruits.APPLE
In [10]: list(Fruits)
Out[10]: [<Fruits.APPLE: 'APPLE'>, <Fruits.ORANGE: 'ORANGE'>, <Fruits.MANGO: 'MANGO'>]
For containment checking, though, you can't use the string directly -
evaluating "APPLE" in Fruits
directly would raise a TypeError, but this is possible:
In [18]: "APPLE" in Fruits.__members__
Out[18]: True
# or:
In [15]: Fruits("APPLE")
Out[15]: <Fruits.APPLE: 'APPLE'>
And, as for the part of your questions that goes:
Is this implementable in Python? I suppose I might need some magic methods or metaclasses for this?
Yes, it is. And yes, it needs both - but it was all done back by Python 3.4
Upvotes: 3