I can see multiple roadblocks to making this work, and it may well be impossible, but I thought I’d at least ask…
So, I have a class AbstractEnumeratedConstantsGroup that, unsurprisingly, when subclassed creates an object that represents a group of enumerated constants (ex FLAVORS with values ('VANILLA', 'CHOCOLATE', 'STRAWBERRY') where FLAVORS[0] returns 'VANILLA', FLAVORS[1] returns 'CHOCOLATE', FLAVORS.VANILLA returns 0, FLAVORS.CHOCOLATE returns 1, etc) it uses a metaclass and some other footwork so that all that is required to create a new constants group is:
class FLAVORS(AbstractEnumeratedConstantsGroup):
_constants = ('VANILLA', 'CHOCOLATE', 'STRAWBERRY')
FLAVORS = FLAVORS()
I’d like to reduce it further to:
@enumerated_constants_group
FLAVORS = ('VANILLA', 'CHOCOLATE', 'STRAWBERRY')
The problems I don’t know how to get past are:
-
1. No good way to get the label name for the object from the defining scope from the decorator code
2. Can’t decorate something that isn’t a class, method, or function
I’ve considered using a factory/builder, but what I don’t like about that is that it requires duplication of the group name in the code, or that the groups have unhelpful __name__ values (which are used in __repr__ and __str__). Ex:
FLAVOR = enumerated_constants_group('FLAVOR', ('VANILLA', 'CHOCOLATE', 'STRAWBERRY'))
or
FLAVOR = enumerated_constants_group(('VANILLA', 'CHOCOLATE', 'STRAWBERRY'))
# FLAVOR.__name__ becomes some unfriendly machine-generated string
As an alternative to the decorator idea, would it be possible to reference the calling scope (through introspection or from implicitly passing it to the function) from a factory method such that calling the method would insert the newly created object in to the calling namespace with the given name? Ex:
enumerated_constants_group('FLAVOR', ('VANILLA', 'CHOCOLATE', 'STRAWBERRY'))
# I could now reference FLAVOR in any arbitrary scope that the method was called from
Is there anything I can do to achieve what I want? Are there other approaches I haven’t thought of?
There is no general way to affect what happens when you assign to a bare name (as in
FLAVORS = ('VANILLA', 'CHOCOLATE', 'STRAWBERRY')). So what you want is not possible.That said, you could write a metaclass that replaces the returned class with an instance of itself, so that you don’t need your last
FLAVORS = FLAVORS(...)line in your first example. The class definition itself would suffice to create the object.