This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

Title: starts with one instead of zero
Type: enhancement Stage: resolved
Components: Versions: Python 3.11
Status: closed Resolution: not a bug
Dependencies: Superseder:
Assigned To: ethan.furman Nosy List: David Rebbe2, eric.smith, ethan.furman, veky
Priority: normal Keywords:

Created on 2021-08-24 14:19 by David Rebbe2, last changed 2022-04-11 14:59 by admin. This issue is now closed.

File name Uploaded Description Edit
enum_comparison.txt David Rebbe2, 2021-08-24 17:55
Messages (19)
msg400210 - (view) Author: David Rebbe (David Rebbe2) Date: 2021-08-24 14:19 By default, the initial value starts at 1. Per the documentation here:

This doesn't really follow expected behavior in majority of programming languages nor python. Most will expect starting value to be zero. I personally skipped over this as I've never seen an enum start at 1 in any language before. Excuse my ignorance if this is more common place then I realize.

I propose an optional argument to the class to allow different starting values:
msg400211 - (view) Author: Vedran Čačić (veky) * Date: 2021-08-24 14:29
For IntEnum, maybe. But for Enum, the whole point of auto() is that the values don't really matter. The rationale was that with IntEnum, truth testing (which is often used in Python to realize Optional) would distinguish None, and then all true Enums would actually be true in the boolean sense.

In a way, most real examples of enums already have some "fake" value which they map to 0. In Python, you idiomatically use None for that.
msg400212 - (view) Author: David Rebbe (David Rebbe2) Date: 2021-08-24 14:53
Understandable and I do believe IntEnum should default as zero as its the default type most will choose when trying to mimic other languages.

C/C++ has the same problem where the value isn't suppose to matter but as soon as you go across the compiled / interpreted application (libraries and network communication) the value of the enum becomes important. 

I have corrected all my code to have a "fake" value of 0 instead of using as the first value, but it would be nice to not have to use that workaround. Maybe a new class CIntEnum would be acceptable?
msg400216 - (view) Author: Vedran Čačić (veky) * Date: 2021-08-24 15:39
I think you should be even more explicit. If values matter, they _should_ be seen in code. (All of them, not just the first one.) auto() just means "this value doesn't really matter". And it's not really hard to write concrete values instead of auto(), in many instances it's even easier. :-)
msg400218 - (view) Author: David Rebbe (David Rebbe2) Date: 2021-08-24 16:43
I only created this issue because its a deviation from any standard that exists AFAIK. Nothing I know of starts at 1 in programming and I more than likely won't be the last one to make this mistake. If indexing in Python started at 1 or any other accessor for that matter started with 1 I could understand it but it doesn't. Its even an ongoing online joke that "arrays start at 1". 

Most other language enum types are not explicit in value definitions because they don't matter, they now have to matter and be explicit in python due to this deviation.
msg400220 - (view) Author: Vedran Čačić (veky) * Date: 2021-08-24 17:30
Honestly, I think it's backwards. Either they _do_ matter because of some external factor (you mention network interoperability, though I'd like you to clarify... what exactly did you send over the network?), or they don't matter (if done right, you shouldn't even _notice_ that they start at 1 --- unless you test their truth value, which was the reason for the original departure from the usual practice in the first place).

You mention indexing... I _hope_ you're not using IntEnums for indexing tuples (or lists). :-o In such a scenario, you're really much better off cutting the middleman and using objects with attributes directly (an interesting insight: due to the way most objects in Python are implemented, you _still_ have the middlemen integers in the process -- only they are called "hash values" and they _truly_ don't matter:).
msg400221 - (view) Author: David Rebbe (David Rebbe2) Date: 2021-08-24 17:55
Welcome to enums, they don't matter until they do. I'm personally not a fan of enums for APIs but they exist all the time.

Indexing was an example case that nothing starts at 1.

See the attached file to demonstrate differences.
msg400222 - (view) Author: Vedran Čačić (veky) * Date: 2021-08-24 18:11
_But why should it matter that starting value is the same_ unless you actually use IntEnums for indexing?

About your code: what do you _actually_ mean by "equivalent"? I hope you don't think that the memory representation is the same. You keep mentioning APIs and networks, but never concretely... do you intend to say that you somehow serialize those values, send them over the wire and then deserialize them into another language?
msg400224 - (view) Author: David Rebbe (David Rebbe2) Date: 2021-08-24 18:25
Definition of equivalent
1: equal in force, amount, or value

Are you referring to memory space as what is actually stored in RAM? If so, that seems to be outside the scope here. I don't think anyone expected an interpreted language to have the same memory space as a compiled binary from C for example.

Concretely anything to do with Serialization, API (ctypes), network communication (non-serialization), etc will fail.

Regardless of the reasons above, 1 is never the intended default value of anything default initialized in any language I've ever come across.
msg400228 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-08-24 19:09
Other than it’s not what you expect, do you have a concrete example of what problem this is causing you?

We’re unlikely to change anything without knowing what problem is being solved.
msg400229 - (view) Author: David Rebbe (David Rebbe2) Date: 2021-08-24 19:18
Seems like there is a misunderstanding here as to why this is an issue.  I can write an example up that would expand on the file I attached, but I feel like anyone that has experience in the above situations would identify this as an issue.

Can I ask why Python strayed from something that is considered a standard in almost all other languages?
msg400232 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2021-08-24 19:47
I think everyone understands, we just feel that it doesn't matter.

You can see if PEP 435 answers your question.

Changing to an enhancement request for 3.11, for the proposal to add a optional argument to auto().
msg400236 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2021-08-24 20:43
Firstly (or Zeroithly ;) my apologies for not noticing this earlier.

In the functional API section:
The reason for defaulting to 1 as the starting number and not 0 is that 0 is False in a boolean sense, but enum members all evaluate to True.


To the best of my knowledge, Python Enums are different from other languages' enums in several respects; apparently this is one of them.  Having the default first value be 1 has been there since the beginning, and isn't going to change now.

Rather than allowing numbers to be assigned using the function that says the assigned number doesn't matter, I would rather add `start=x` in the header, the way I have it in `aenum`:

    from aenum import Enum, auto

    class ZeroEnum(Enum, start=0):
        nothing = auto()
        something = auto()

    # [<ZeroEnum.nothing: 0>, <ZeroEnum.something: 1>]

    class ZeroBaseEnum(Enum, start=0):

    class ZeroEnum2(ZeroBaseEnum):
        nothing = auto()
        something = auto()

    # [<ZeroEnum2.nothing: 0>, <ZeroEnum2.something: 1>]

Whether the `0` goes in the header, or in auto (which it isn't), people will still need to read the docs to find out about it (unless somebody asks an SO question, of course).
msg400237 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2021-08-24 20:45
My apologies for my apologies -- for some reason I thought this was first posted in August of 2020.
msg400242 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2021-08-24 21:32
David, what is the actual use-case that tripped you up?  There are a few ways to create Enums from other systems (json files, cpp files, etc.).
msg400274 - (view) Author: David Rebbe (David Rebbe2) Date: 2021-08-25 16:41
Thank you for referencing the PEP, I just managed to read through it and I still don't have a very good understanding why it needs to default to 1. PEP 435 states:

"The reason for defaulting to 1 as the starting number and not 0 is that 0 is False in a boolean sense, but enum members all evaluate to True."

I can't find anything in the PEP that states why enum members need to evaluate to True. What am I missing here?

I was specifically tripped up with the generator I wrote that takes a C header and converts it to python ctype/enum objects. I have fixed the issue so its not a show stopper by any means its just not expected behavior for anyone with programming experience with enums outside Python. 

Here is the project in question where I discovered this:

enum in question:
which is generated from this:

Maybe it would be better to have an Enum type in the ctypes module instead to match all other language enums?
msg400276 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2021-08-25 17:40
Putting an enum in the ctypes module is a decent idea.
msg400278 - (view) Author: Vedran Čačić (veky) * Date: 2021-08-25 18:02
And CEnum is probably the best name for it. "Int" part is pretty much implied in "C" part.
msg400290 - (view) Author: Ethan Furman (ethan.furman) * (Python committer) Date: 2021-08-25 19:25
David, I added a PR for the Enum handling/creation in the ics library.  Syncing values with external libraries is definitely *not* what `auto` is intended for.

As for why enum members evaluate as True:  most objects in Python evaluate to True, unless they have a reason not to (such as an empty container or the number 0).  One of the main ideas behind the creation of Enum is that the exact values didn't usually matter,  but when they did they would be specified.  `auto()` was added later to make the "doesn't usually matter" case easier.


Vedran, or `cEnum`?  ;-)

One concern is should cEnum be Python's `int`, or a c_type int?  That should be in a new issue, though.

Okay, it's Issue45004.
Date User Action Args
2022-04-11 14:59:49adminsetgithub: 89156
2021-08-27 01:47:03eric.smithsetstatus: open -> closed
2021-08-25 19:25:09ethan.furmansetmessages: + msg400290
stage: resolved
2021-08-25 18:02:05vekysetmessages: + msg400278
2021-08-25 17:40:07ethan.furmansetmessages: + msg400276
2021-08-25 16:41:20David Rebbe2setmessages: + msg400274
2021-08-24 21:32:14ethan.furmansetmessages: + msg400242
2021-08-24 20:45:30ethan.furmansetmessages: + msg400237
2021-08-24 20:43:44ethan.furmansetassignee: ethan.furman
resolution: not a bug
messages: + msg400236
2021-08-24 19:47:12eric.smithsetversions: - Python 3.6, Python 3.7, Python 3.8, Python 3.9, Python 3.10
nosy: + ethan.furman

messages: + msg400232

type: enhancement
2021-08-24 19:18:27David Rebbe2setmessages: + msg400229
2021-08-24 19:09:12eric.smithsetnosy: + eric.smith
messages: + msg400228
2021-08-24 18:25:45David Rebbe2setmessages: + msg400224
2021-08-24 18:11:55vekysetmessages: + msg400222
2021-08-24 17:55:54David Rebbe2setfiles: + enum_comparison.txt

messages: + msg400221
2021-08-24 17:30:02vekysetmessages: + msg400220
2021-08-24 16:43:54David Rebbe2setmessages: + msg400218
2021-08-24 15:39:53vekysetmessages: + msg400216
2021-08-24 14:53:16David Rebbe2setmessages: + msg400212
2021-08-24 14:29:49vekysetnosy: + veky
messages: + msg400211
2021-08-24 14:19:43David Rebbe2create