Behavior of auto in Enum and Flag.
Ethan Furman
ethan at stoneleaf.us
Mon Apr 3 04:03:05 EDT 2017
On 04/02/2017 09:49 PM, Oren Ben-Kiki wrote:
> The current behavior of `auto` is to pick a value which is one plus the
> previous value.
Starting with 1 if no previous value exists.
> It would probably be better if `auto` instead picked a value that is not
> used by any named member (either the minimal unused value, or the minimal
> higher than the previous value). That is, in this simple case:
>
> class MyEnum(Enum):
> FOO = 1
> BAR = auto()
> BAZ = 2
>
> It would be far better for BAR to get the value 3 rather than today's value
> 2.
Possibly.
> After all, `auto` is supposed to be used when:
>
> "If the exact value is unimportant you may use auto instances and an
> appropriate value will be chosen for you."
>
> Choosing a value that conflicts with BAZ in above cases doesn't seem
> "appropriate" for a value that is "unimportant".
MyEnum.BAR exists, and works. It just happens to be the same as MyEnum.BAZ -- which did not exist when the value for
BAR was chosen.
> The docs also state "Care must be taken if you mix auto with other values."
> - fair enough. But:
>
> First, why require "care" if the code can take care of the issue for us?
>
> Second, the docs don't go into further detail about what exactly to avoid.
> In particular, the docs do not state that the automatic value will only
> take into account the previous values, and will ignore following values.
Python code is executed top-down. First FOO, then BAR, then BAZ. It is not saved up and executed later in random
order. Or, put another way, the value was appropriate when it was chosen -- it is not the fault of auto() that the user
chose a conflicting value (hence why care should be taken).
> However, this restriction is baked into the current implementation:
> It is not possible to just override `_generate_next_value_` to skip past
> named values which were not seen yet, because the implementation only
> passes it the list of previous values.
In other words, the currently existing values, because the future values don't exist yet.
> I propose that:
>
> 1. The documentation will be more explicit about the way `auto` behaves in
> the presence of following values.
I can do that.
> 2. The default behavior of `auto` would avoid generating a conflict with
> following values.
I could do that, but I'm not convinced it's necessary, plus there would be backwards compatibility constraints at this
point.
> 3. Whether `auto` chooses (A) the minimal unused value higher than the
> previous value, or (B) the minimal overall unused value, or (C) some other
> strategy, would depend on the specific implementation.
This might work for you (untested):
def _generate_next_value_(name, start, count, previous_values):
if not count:
return start or 1
previous_values.sort()
last_value = previous_values[-1]
if last_value < 1000:
return 1001
else:
return last_value + 1
> 3. To allow for this, the implementation will include a
> `_generate_auto_value_` which will take both the list of previous ("last")
> values (including auto values) and also a second list of the following
> ("next") values (excluding auto values).
No, I'm not interested in doing that. I currently have that kind of code in aenum[1] for 2.7 compatibility, and it's a
nightmare to maintain.
--
~Ethan~
[1] https://pypi.python.org/pypi/aenum
Advanced Enumerations
NamedConstants
NamedTuple (metaclass based namedtuple -- no code eval)
plus a bunch of other goodies
More information about the Python-list
mailing list