Reputation: 26160
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:
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?
Upvotes: 3
Views: 102
Reputation: 1474
How about named tuples. http://docs.python.org/library/collections.html#collections.namedtuple
Upvotes: 0
Reputation: 27321
Have you looked at http://code.activestate.com/recipes/413486/
It allows you to write code like this:
>>> from enum import enum
>>> FLAVORS = enum('VANILLA', 'CHOCOLATE', 'STRAWBERRY')
>>> print FLAVORS
enum (VANILLA, CHOCOLATE, STRAWBERRY)
>>> print FLAVORS.VANILLA
VANILLA
>>> f = FLAVORS.CHOCOLATE
>>> f in FLAVORS
True
>>> for f in FLAVORS:
... print f
...
VANILLA
CHOCOLATE
STRAWBERRY
Upvotes: 2
Reputation: 168
Besides function/method decorators, you have also class decorators (at least they were there in python 2.4), see http://www.python.org/dev/peps/pep-3129/
Anyway, I'm not sure this is what you need.
I would go with the factory class that gets (type, values) as args.
One note: if your code is just like this, and AbstractEnumeratedConstantsGroup doesn't add any functionality, why don't you just use tuples?
You could define a constants.py module with the following content:
FLAVOURS = ('VANILLA', 'CHOCOLATE', 'STRAWBERRY')
PETS = ('DOG', 'CAT', 'FISH')
and then use those tuples from your code:
import constants
for pet in constants.PETS:
print 'I have a ', pet
Upvotes: 0
Reputation: 531798
As an intermediate step, you could do
FLAVORS = type("FLAVORS",
(AbstractEnumeratedConstantsGroup,),
{_constants = ('VANILLA', 'CHOCOLATE', 'STRAWBERRY')})()
which leads to a fake decorator
def enumerated_constants_group(*args):
AECG=AbstractEnumeratedConstantsGroup
t = type(args[0], (AECG,), dict(_constants=args[1:]))
return t()
FLAVORS = enumerated_constants_group('FLAVORS', 'VANILLA', 'CHOCOLATE', 'STRAWBERRY')
Upvotes: 0
Reputation: 251438
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.
Upvotes: 3