# Feature or enhancement `len(Enum)` has no useful meaning and should therefore raise the appropriate error # Pitch I cannot figure out any scenario, in which len(Enum) should not raise. Enum is a type and as any other types, such as `len(list)` or `len(dict)`, and should not give any meaningful result on len. Thoughts?
An Enum is functionally a container with a limited set of constants. Why should it not be legitimate to ask for the number of constants in it? Op 1/04/2023 om 16:45 schreef Richard Hajek:
# Feature or enhancement
`len(Enum)` has no useful meaning and should therefore raise the appropriate error
# Pitch
I cannot figure out any scenario, in which len(Enum) should not raise. Enum is a type and as any other types, such as `len(list)` or `len(dict)`, and should not give any meaningful result on len.
Thoughts? _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/TQB2HR... Code of Conduct: http://python.org/psf/codeofconduct/
On 2/04/23 6:36 pm, Benedict Verhegghe wrote:
An Enum is functionally a container with a limited set of constants. Why should it not be legitimate to ask for the number of constants in it?
But Enum itself isn't an enum, it's a constructor for enums. I think len() just happens to work on it as a side effect of the way enums are implemented. -- Greg
On Mon, 3 Apr 2023 at 08:28, Greg Ewing <gcewing@snap.net.nz> wrote:
On 2/04/23 6:36 pm, Benedict Verhegghe wrote:
An Enum is functionally a container with a limited set of constants. Why should it not be legitimate to ask for the number of constants in it?
But Enum itself isn't an enum, it's a constructor for enums. I think len() just happens to work on it as a side effect of the way enums are implemented.
Enum itself is iterable, so the question is: how important is it to block this? Is it actually a problem that the Enum type acts like it has no elements? ChrisA
I believe that is the core question. In my use case it caused a bug because I accidentally typed "Enum" instead of "MyEnum" and the "len" worked, which is something I did not expect. However I do not know how prevalent this mistake is.
Enum is not a type, like the OP suggested: from enum import Enum class Color(Enum): WHITE = 1 type(Color) <class 'enum.EnumMeta'> type(Enum) <class 'enum.EnumMeta'> type(enum.EnumMeta) <class 'type'> Enum.__len__ <bound method EnumMeta.__len__ of <enum 'Enum'>> So to me it looks like Enum is just a generic, empty enum. The __len__ method is implemented in EnumMeta. Op 3/04/2023 om 00:18 schreef Greg Ewing:
On 2/04/23 6:36 pm, Benedict Verhegghe wrote:
An Enum is functionally a container with a limited set of constants. Why should it not be legitimate to ask for the number of constants in it?
But Enum itself isn't an enum, it's a constructor for enums. I think len() just happens to work on it as a side effect of the way enums are implemented.
On Mon, 3 Apr 2023 at 15:54, Benedict Verhegghe <bverheg@gmail.com> wrote:
Enum is not a type, like the OP suggested:
Well, it is:
isinstance(Enum, type) True
but it has a metaclass, meaning that the type of Enum is a subclass of type.
type(Enum) <class 'enum.EnumType'> type(Enum).__bases__ (<class 'type'>,)
I mean, we wouldn't be subclassing it if it weren't a type. The __len__ method is implemented on the metaclass to allow all Enum subclasses to have lengths (ditto __iter__ to make them iterable), and as a consequence of that, Enum itself has all the attributes that it wants to give to its subclasses. ChrisA
Well, it's not an object of type 'type' like the list or dict mentioned by the OP, for which len() give a TypeError: object of type 'type' has no len() Any derived class of Enum will also return True for isinstance(..., type). Should the derived classes then neither have a meaningful result for len()? Enum and derived classes hold a container with a fixed set of values. It makes perfectly sense to ask for the number of possible values, even when there are none. Op 3/04/2023 om 08:53 schreef Chris Angelico:
On Mon, 3 Apr 2023 at 15:54, Benedict Verhegghe <bverheg@gmail.com> wrote:
Enum is not a type, like the OP suggested:
Well, it is:
isinstance(Enum, type) True
but it has a metaclass, meaning that the type of Enum is a subclass of type.
type(Enum) <class 'enum.EnumType'> type(Enum).__bases__ (<class 'type'>,)
I mean, we wouldn't be subclassing it if it weren't a type.
The __len__ method is implemented on the metaclass to allow all Enum subclasses to have lengths (ditto __iter__ to make them iterable), and as a consequence of that, Enum itself has all the attributes that it wants to give to its subclasses.
ChrisA _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/V2HVHK... Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, 3 Apr 2023 at 17:57, Benedict Verhegghe <bverheg@gmail.com> wrote:
Well, it's not an object of type 'type' like the list or dict mentioned by the OP, for which len() give a TypeError: object of type 'type' has no len()
I'm not sure what you mean here. Enum is of type EnumMeta, which is a subclass of type. That means that, according to the normal rules of type hierarchy, Enum is indeed a type. Types add functionality that the parent type (with some Exceptions), so it should be no surprise that EnumMeta can add a __len__ method that type itself didn't have.
Any derived class of Enum will also return True for isinstance(..., type).
Yes, that is correct. Any subclass of Enum will also be a type, and will have a length.
class TestEnum(Enum): ... SPAM = 1 ... HAM = 2 ... isinstance(TestEnum, type) True len(TestEnum) 2
Should the derived classes then neither have a meaningful result for len()? Enum and derived classes hold a container with a fixed set of values. It makes perfectly sense to ask for the number of possible values, even when there are none.
Yeah, and I'm of the opinion that it's not a problem for Enum to have zero possible values, but to have the concept of values. Although it also wouldn't be a problem if it didn't. ChrisA
Yes, that is correct. Any subclass of Enum will also be a type, and will have a length.
That is the correct and expected behavior, as described on https://docs.python.org/3/library/enum.html
Yeah, and I'm of the opinion that it's not a problem for Enum to have zero possible values, but to have the concept of values.
I encountered that Enum having a len hid a mistake on my part. If len(Enum) raised, my mistake would be immediately apparent, however at the end of the day, my mistake was easily found. I am using this issue to mostly try how to contribute to Python and I do not have strong opinion on this issue either way. IMHO len(Enum) should raise, but idk if it is worth potential downstream breakages.
Hi Richard, On Tue, 4 Apr 2023 at 12:49, Richard Hajek <richard.m.hajek@gmail.com> wrote:
I encountered that Enum having a len hid a mistake on my part. If len(Enum) raised, my mistake would be immediately apparent, however at the end of the day, my mistake was easily found.
Can any Python linting tools (such as pylint) detect a potential problem with the code? Thanks, James
Hi Here's a similar example $ python3
Python 3.10.6 (main, Nov 14 2022, 16:10:14) [GCC 11.3.0] on linux Type "help", "copyright", "credits" or "license" for more information.
from collections import Counter cnt = Counter # Oops! cnt.update('abcde') cnt <class 'collections.Counter'>
This is what happens without the typo.
cnt = Counter()
cnt.update('abcde') cnt Counter({'a': 1, 'b': 1, 'c': 1, 'd': 1, 'e': 1})
Here's a link to the source for Counter.update: https://github.com/python/cpython/blob/3.11/Lib/collections/__init__.py#L658... -- Jonathan
mypy does not detect this as a problem because EnumMeta has a `.__len__` method https://github.com/python/typeshed/blob/60939b00afede13feeec3cee6f6dfe6eb2df... what would the type hints look like if len(Enum) failed but class Foo(Enum): pass len(Foo) succeeds?
On 4/1/23 07:45, Richard Hajek wrote:
# Feature or enhancement
`len(Enum)` has no useful meaning and should therefore raise the appropriate error
# Pitch
I cannot figure out any scenario, in which len(Enum) should not raise. Enum is a type and as any other types, such as `len(list)` or `len(dict)`, and should not give any meaningful result on len.
Enums are not like any other type in a multitude of ways: `__len__`, `__contains__`, `__getitem__`, etc. Special-casing only `Enum`, `IntEnum`, `StrEnum`, and every other base enum (i.e. no members), would be like special-casing empty lists, empty dicts, empty tuples, etc. -- ~Ethan~
In addition to this, there's nothing wrong with defining additional behavior on class objects. They're just objects. The pattern of class objects acting as containers of registered instances is used in many places, and it makes perfect sense here IMO. On Tue, Apr 4, 2023, at 2:54 PM, Ethan Furman wrote:
Enums are not like any other type in a multitude of ways: `__len__`, `__contains__`, `__getitem__`, etc. Special-casing only `Enum`, `IntEnum`, `StrEnum`, and every other base enum (i.e. no members), would be like special-casing empty lists, empty dicts, empty tuples, etc.
-- ~Ethan~
participants (9)
-
Benedict Verhegghe
-
Chris Angelico
-
Ethan Furman
-
Greg Ewing
-
James Addison
-
Jonathan Fine
-
outthere@me.gregwerbin.com
-
Richard Hajek
-
Thomas Grainger