Behavior of auto in Enum and Flag.
ethan at stoneleaf.us
Mon Apr 3 12:29:50 EDT 2017
On 04/03/2017 01:53 AM, Oren Ben-Kiki wrote:
> On Mon, Apr 3, 2017 at 11:03 AM, Ethan Furman 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).
Possible, yes. Easy, no.
> 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.
Since most things execute immediately, that is the pattern I chose.
>>> 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 ;-)
> 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.
Actually, (Int)Flag uses the next power of two (not already seen) -- so it isn't as easy as "last value + 1".
> 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".
Something to that effect sounds good.
> This might work for you (untested):
> def _generate_next_value_(name, start, count, previous_values):
> if not count:
> return start or 1
> last_value = previous_values[-1]
> if last_value < 1000:
> return 1001
> 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.
General code isn't going to "do what I need" 100% of the time for 100% of the people. That's why I made the
_generate_next_value_ method user over-ridable
>>> 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 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.
It's a lot more work than just making another method.
> Assuming you buy into my "necessity" claim, that is...
It this point I do not. If you can give us an example Enum and why it's necessary to be built like that I might be
swayed -- although the odds are good that the change will go into aenum instead (it's already a mess with the 2.7
More information about the Python-list