Behavior of auto in Enum and Flag.

Oren Ben-Kiki python-oren at ben-kiki.org
Mon Apr 3 04:53:51 EDT 2017


On Mon, Apr 3, 2017 at 11:03 AM, Ethan Furman <ethan at stoneleaf.us> wrote:

> 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).


This is not to say that there's no possible workaround for this - the code
could pretty easily defer invocation of _generate_next_macro_ until after
the whole class was seen. It would still happen in order (since members are
an ordered dictionary these days).

So it is a matter of conflicting values - what would be more "Pythonic":
treating auto as executed immediately, or avoiding conflicts between auto
and explicit values.


> 1. The documentation will be more explicit about the way `auto` behaves in
> the presence of following value



> I can do that.
>

Barring changing the way auto works, that would be best ("explicit is
better than implicit" and all 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.


"Necessity" depends on the judgement call above.

As for backward compatibility, the docs are pretty clear about "use auto
when you don't care about the value"... and Enum is pretty new, so there's
not _that_ much code that relies on "implementation specific" details.

*If* backward compatibility is an issue here, then the docs might as well
specify "previous value plus 1, or 1 if this is the first value" as the
"standard" behavior, and be done.

This has the advantage of being deterministic and explicit, so people would
be justified in relying on it. It would still have to be accompanied by
saying "auto() can only consider previous values, not following ones".


> 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


This assumes no following enum values have values > 1000 (or some
predetermined constant), which doesn't work in my particular case, or in
the general case. But yes, this might solve the problem for some people.


> 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.
>

Understood. Another alternative would be to have something like
_generate_next_value_ex_ with the additional argument (similar to
__reduce_ex__), which isn't ideal either.

Assuming you buy into my "necessity" claim, that is...

Thanks,

Oren.


More information about the Python-list mailing list