[Python-Dev] Enum: subclassing?

2 views
Skip to first unread message

Ethan Furman

unread,
May 1, 2013, 1:21:30 PM5/1/13
to Python Dev
We may not want to /completely/ disallow subclassing. Consider:

--> class StrEnum(str, Enum):
... '''string enums for Business Basic variable names'''
...
--> class Vendors(StrEnum):
EnumError: subclassing not allowed


My point is that IntEnum, StrEnum, ListEnum, FloatEnum are all "subclasses" of Enum. To then have a subclass of
that, such as Season(StrEnum), is subclassing a subclass.

Now, if we do want to completely disallow it, we can ditch IntEnum and force the user to always specify the mixin
type:

--> class Season(str, Enum):
.
.
.

--> class Names(str, Enum):
.
.
.

But that's not very user friendly... although it's not too bad, either.

One consequence of the way it is now (IntEnum, StrEnum, etc., are allowed) is that one can put methods and other
non-Enum item in a base class and then inherit from that for actual implemented Enum classes.

--> class StrEnum(str, Enum):
... def describe(self):
... print("Hi! I'm a %s widget!" % self.value)
...

--> class Season(StrEnum):
... spring = 'green'
... summer = 'brown'
... autumn = 'red'
... winter = 'white'
...

--> class Planet(StrEnum):
... mars = 'red'
... earth = 'blue'
...

--> Season.summer.descripbe()
Hi! I'm a brown widget!

--> Planet.earth.describe()
Hi! I'm a blue widget!

--
~Ethan~
_______________________________________________
Python-Dev mailing list
Pytho...@python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/dev-python%2Bgarchive-30976%40googlegroups.com

Guido van Rossum

unread,
May 1, 2013, 1:48:45 PM5/1/13
to Ethan Furman, Python Dev
On Wed, May 1, 2013 at 10:21 AM, Ethan Furman <et...@stoneleaf.us> wrote:
> We may not want to /completely/ disallow subclassing. Consider:
>
> --> class StrEnum(str, Enum):
> ... '''string enums for Business Basic variable names'''
> ...
> --> class Vendors(StrEnum):
> EnumError: subclassing not allowed
>
>
> My point is that IntEnum, StrEnum, ListEnum, FloatEnum are all "subclasses"
> of Enum. To then have a subclass of
> that, such as Season(StrEnum), is subclassing a subclass.

True, and Enum itself also falls in this category. Maybe there could
be a special marker that you have to set in the class body (or a
keyword arg in the class statement) to flag that a class is meant as a
"category of enums" rather than a specific enum type. Such categorical
classes should not define any instances. (And maybe "defines no
instances" is enough to flag an Enum class as subclassable.)

> Now, if we do want to completely disallow it, we can ditch IntEnum and force
> the user to always specify the mixin
> type:
>
> --> class Season(str, Enum):
> .
> .
> .
>
> --> class Names(str, Enum):
> .
> .
> .
>
> But that's not very user friendly... although it's not too bad, either.

Indeed, given that we mostly want IntEnum as a last-resort backward
compatibility thing for os and socket, it may not be so bad.

> One consequence of the way it is now (IntEnum, StrEnum, etc., are allowed)
> is that one can put methods and other non-Enum item in a base class and then
> inherit from that for actual implemented Enum classes.
>
> --> class StrEnum(str, Enum):
> ... def describe(self):
> ... print("Hi! I'm a %s widget!" % self.value)
> ...
>
> --> class Season(StrEnum):
> ... spring = 'green'
> ... summer = 'brown'
> ... autumn = 'red'
> ... winter = 'white'
> ...
>
> --> class Planet(StrEnum):
> ... mars = 'red'
> ... earth = 'blue'
> ...
>
> --> Season.summer.descripbe()
> Hi! I'm a brown widget!
>
> --> Planet.earth.describe()
> Hi! I'm a blue widget!

If the base class doesn't define any instances (and perhaps is marked
specifically for this purpose) I'm fine with that.

--
--Guido van Rossum (python.org/~guido)

Eli Bendersky

unread,
May 1, 2013, 2:04:32 PM5/1/13
to Guido van Rossum, Python Dev
Actually, in flufl.enum, IntEnum had to define a magic __value_factory__ attribute, but in the current ref435 implementation this isn't needed, so IntEnum is just:

class IntEnum(int, Enum):
    '''
    Class where every instance is a subclass of int.
    '''

So why don't we just drop IntEnum from the API and tell users they should do the above explicitly, i.e.:

class SocketFamily(int, Enum):
  AF_UNIX = 1
  AF_INET = 2

As opposed to having an IntEnum explicitly, this just saves 2 characters (comma+space), but is more explicit (zen!) and helps us avoid the special-casing the subclass restriction implementation.

Eli

Guido van Rossum

unread,
May 1, 2013, 2:14:36 PM5/1/13
to Eli Bendersky, Python Dev
On Wed, May 1, 2013 at 11:04 AM, Eli Bendersky <eli...@gmail.com> wrote:
> Actually, in flufl.enum, IntEnum had to define a magic __value_factory__
> attribute, but in the current ref435 implementation this isn't needed, so
> IntEnum is just:
>
> class IntEnum(int, Enum):
> '''
> Class where every instance is a subclass of int.
> '''
>
> So why don't we just drop IntEnum from the API and tell users they should do
> the above explicitly, i.e.:
>
> class SocketFamily(int, Enum):
> AF_UNIX = 1
> AF_INET = 2
>
> As opposed to having an IntEnum explicitly, this just saves 2 characters
> (comma+space), but is more explicit (zen!) and helps us avoid the
> special-casing the subclass restriction implementation.

Sounds good to me.

Antoine Pitrou

unread,
May 1, 2013, 2:44:39 PM5/1/13
to pytho...@python.org
On Wed, 01 May 2013 10:21:30 -0700
Ethan Furman <et...@stoneleaf.us> wrote:
> We may not want to /completely/ disallow subclassing. Consider:
>
> --> class StrEnum(str, Enum):
> ... '''string enums for Business Basic variable names'''
> ...
> --> class Vendors(StrEnum):
> EnumError: subclassing not allowed

I don't see the point of disallowing subclassing. It sounds like
a pointless restriction.

However, perhaps the constructor should forbid the returning of a base
type, e.g.:

class Season(Enum):
spring = 1

class MySeason(Season):
"""I look nicer than Season"""

MySeason('spring')
...
ValueError: Season.spring is not a MySeason instance

(what this means is perhaps the subclassing of non-empty enum classes
should be forbidden)

Regards

Antoine.

Georg Brandl

unread,
May 1, 2013, 2:47:19 PM5/1/13
to pytho...@python.org
Am 01.05.2013 20:04, schrieb Eli Bendersky:

> Actually, in flufl.enum, IntEnum had to define a magic __value_factory__
> attribute, but in the current ref435 implementation this isn't needed, so
> IntEnum is just:
>
> class IntEnum(int, Enum):
> '''
> Class where every instance is a subclass of int.
> '''
>
> So why don't we just drop IntEnum from the API and tell users they should do the
> above explicitly, i.e.:
>
> class SocketFamily(int, Enum):
> AF_UNIX = 1
> AF_INET = 2
>
> As opposed to having an IntEnum explicitly, this just saves 2 characters
> (comma+space), but is more explicit (zen!) and helps us avoid the special-casing
> the subclass restriction implementation.

Wait a moment... it might not be immediately useful for IntEnums (however,
that's because base Enum currently defines __int__ which I find questionable),
but with current ref435 you *can* create your own enum base classes with your
own methods, and derive concrete enums from that. It also lets you have a
base class for enums and use it in isinstance().

If you forbid subclassing completely that will be impossible.

Georg

Georg Brandl

unread,
May 1, 2013, 2:59:15 PM5/1/13
to pytho...@python.org
Am 01.05.2013 20:44, schrieb Antoine Pitrou:
> On Wed, 01 May 2013 10:21:30 -0700
> Ethan Furman <et...@stoneleaf.us> wrote:
>> We may not want to /completely/ disallow subclassing. Consider:
>>
>> --> class StrEnum(str, Enum):
>> ... '''string enums for Business Basic variable names'''
>> ...
>> --> class Vendors(StrEnum):
>> EnumError: subclassing not allowed
>
> I don't see the point of disallowing subclassing. It sounds like
> a pointless restriction.
>
> However, perhaps the constructor should forbid the returning of a base
> type, e.g.:
>
> class Season(Enum):
> spring = 1
>
> class MySeason(Season):
> """I look nicer than Season"""
>
> MySeason('spring')
> ...
> ValueError: Season.spring is not a MySeason instance
>
> (what this means is perhaps the subclassing of non-empty enum classes
> should be forbidden)

That's exactly what's implemented in the ref435 code at the moment.

Georg

Eli Bendersky

unread,
May 1, 2013, 4:05:53 PM5/1/13
to Georg Brandl, Python Dev



On Wed, May 1, 2013 at 11:59 AM, Georg Brandl <g.br...@gmx.net> wrote:
Am 01.05.2013 20:44, schrieb Antoine Pitrou:
> On Wed, 01 May 2013 10:21:30 -0700
> Ethan Furman <et...@stoneleaf.us> wrote:
>> We may not want to /completely/ disallow subclassing.  Consider:
>>
>> --> class StrEnum(str, Enum):
>> ...    '''string enums for Business Basic variable names'''
>> ...
>> --> class Vendors(StrEnum):
>> EnumError: subclassing not allowed
>
> I don't see the point of disallowing subclassing. It sounds like
> a pointless restriction.
>
> However, perhaps the constructor should forbid the returning of a base
> type, e.g.:
>
> class Season(Enum):
>     spring = 1
>
> class MySeason(Season):
>     """I look nicer than Season"""
>
> MySeason('spring')
> ...
> ValueError: Season.spring is not a MySeason instance
>
> (what this means is perhaps the subclassing of non-empty enum classes
> should be forbidden)

That's exactly what's implemented in the ref435 code at the moment.


It can't be because __call__ is by-value lookup, not by-name lookup. By-name lookup is Season.spring or getattr(Season, 'spring')

Eli

Eli Bendersky

unread,
May 1, 2013, 4:09:53 PM5/1/13
to Georg Brandl, Python Dev
On Wed, May 1, 2013 at 11:47 AM, Georg Brandl <g.br...@gmx.net> wrote:
Am 01.05.2013 20:04, schrieb Eli Bendersky:

> Actually, in flufl.enum, IntEnum had to define a magic __value_factory__
> attribute, but in the current ref435 implementation this isn't needed, so
> IntEnum is just:
>
> class IntEnum(int, Enum):
>     '''
>     Class where every instance is a subclass of int.
>     '''
>
> So why don't we just drop IntEnum from the API and tell users they should do the
> above explicitly, i.e.:
>
> class SocketFamily(int, Enum):
>   AF_UNIX = 1
>   AF_INET = 2
>
> As opposed to having an IntEnum explicitly, this just saves 2 characters
> (comma+space), but is more explicit (zen!) and helps us avoid the special-casing
> the subclass restriction implementation.

Wait a moment... it might not be immediately useful for IntEnums (however,
that's because base Enum currently defines __int__ which I find questionable),
but with  current ref435 you *can* create your own enum base classes with your
own methods, and derive concrete enums from that.  It also lets you have a
base class for enums and use it in isinstance().

If you forbid subclassing completely that will be impossible.

I'm not sure what you mean, Georg, could you clarify?
This works:

>>> from ref435 import Enum
>>> class SocketFamily(int, Enum):
...   AF_UNIX = 1
...   AF_INET = 2
...
>>> SocketFamily.AF_INET
SocketFamily.AF_INET [value=2]
>>> SocketFamily.AF_INET == 2
True
>>> type(SocketFamily.AF_INET)
<Enum 'SocketFamily'>
>>> isinstance(SocketFamily.AF_INET, SocketFamily)
True

Now, with the way things are currently implemented, class IntEnum is just syntactic sugar for above. Guido decided against allowing any kind of subclassing, but as an implementation need we should keep some restricted form to implement IntEnum. But is IntEnum really needed if the above explicit multiple-inheritance of int and Enum is possible?

Eli
 


Antoine Pitrou

unread,
May 1, 2013, 4:33:44 PM5/1/13
to pytho...@python.org
Ok, I've mixed up the example. But, still, since Season(1) should
return the Season.spring singleton, I don't see any reasonable thing
for MySeason(1) to return. Hence the request to raise an exception.

Regards

Antoine.

Eli Bendersky

unread,
May 1, 2013, 4:43:22 PM5/1/13
to Antoine Pitrou, Python Dev
What do you need MySeason for, though? IIUC, you don't ask to allow adding enum values in it, so it only leaves adding extra functionality (methods)? What are the use cases?

Eli





Antoine Pitrou

unread,
May 1, 2013, 4:45:53 PM5/1/13
to pytho...@python.org
On Wed, 1 May 2013 13:43:22 -0700
I was talking in the context where subclassing is allowed. I don't
think there's a use-case for subclassing of non-empty enums. On the
other hand, empty enums should probably allow subclassing (they are
"abstract base enums", in a way).

Eli Bendersky

unread,
May 1, 2013, 4:57:11 PM5/1/13
to Antoine Pitrou, Python Dev
I still don't understand what you mean, sorry. Like, this:

class MyEmptyEnum(Enum):
  pass

Why would you want to subclass MyEmptyEnum ?

Or do you mean this:

class IntEnum(int, Enum):
  pass

Now I can have:

class SocketFamily(IntEnum):
  ??

If it's the latter, then why allow subclassing explicitly just for this reason? I think the explicit approach of:

class SocketFamily(int, Enum):

Is cleaner anyway, and absolves us of providing yet another enum class to export from the stdlib.

Eli

Antoine Pitrou

unread,
May 1, 2013, 5:00:01 PM5/1/13
to pytho...@python.org
Because I may want to share methods accross all concrete subclasses of
IntEnum (or WhateverEnum).

Eli Bendersky

unread,
May 1, 2013, 5:04:11 PM5/1/13
to Antoine Pitrou, Python Dev



On Wed, May 1, 2013 at 2:00 PM, Antoine Pitrou <soli...@pitrou.net> wrote:
On Wed, 1 May 2013 13:57:11 -0700
Eli Bendersky <eli...@gmail.com> wrote:
>
> I still don't understand what you mean, sorry. Like, this:
>
> class MyEmptyEnum(Enum):
>   pass
>
> Why would you want to subclass MyEmptyEnum ?
>
> Or do you mean this:
>
> class IntEnum(int, Enum):
>   pass
>
> Now I can have:
>
> class SocketFamily(IntEnum):
>   ??
>
> If it's the latter, then why allow subclassing explicitly just for this
> reason?

Because I may want to share methods accross all concrete subclasses of
IntEnum (or WhateverEnum).

You mean this?

class BehaviorMixin:
  # bla bla

class MyBehavingIntEnum(int, BehaviorMixin, Enum):
  foo = 1
  bar = 2

Eli


Antoine Pitrou

unread,
May 1, 2013, 5:11:16 PM5/1/13
to pytho...@python.org
On Wed, 1 May 2013 14:04:11 -0700
Eli Bendersky <eli...@gmail.com> wrote:
>
> You mean this?
>
> class BehaviorMixin:
> # bla bla
>
> class MyBehavingIntEnum(int, BehaviorMixin, Enum):
> foo = 1
> bar = 2

Yes, but without the need for multiple inheritance and separate
mixins ;-) Especially if the behaviour is enum-specific, e.g.:

class IETFStatusCode(IntEnum):

@classmethod
def from_statusline(cls, line):
return cls(int(line.split()[0]))

class HTTPStatusCode(IETFStatusCode):
NOT_FOUND = 404

class SIPStatusCode(IETFStatusCode):
RINGING = 180

Guido van Rossum

unread,
May 1, 2013, 5:07:51 PM5/1/13
to Eli Bendersky, Antoine Pitrou, Python Dev
It's a common pattern to do this with a base class rather than a
mixin, though, and I think the rule "only allow subclassing empty
enums" makes a lot of sense.


--
--Guido van Rossum (python.org/~guido)

Eli Bendersky

unread,
May 1, 2013, 5:19:00 PM5/1/13
to Guido van Rossum, Antoine Pitrou, Python Dev
I see your point (and Antoine's example in the next email is good), but my concern is that this is a TIMTOWTDI thing, since the same can be achieved with mixins. Specifically, Antoine's example
 becomes:

class IETFStatusCode:

    @classmethod
    def from_statusline(cls, line):
        return cls(int(line.split()[0]))

class HTTPStatusCode(int, IETFStatusCode, Enum):
    NOT_FOUND = 404

class SIPStatusCode(int, IETFStatusCode, Enum):
    RINGING = 180

Same thing, while keeping the stdlib API cleaner and more minimal. Cleaner because "no subclassing" is a simpler, more explicit, and easier to understand rule than "no subclassing unless base class is devoid of enumeration values". And because we can no longer say "Enum classes are final", which is a relatively familiar and understood semantic.

That said, I don't feel strongly about this so if the above does not convert you, I'm fine with allowing subclassing enum classes that don't define any enums =)

Eli

Georg Brandl

unread,
May 1, 2013, 5:35:17 PM5/1/13
to pytho...@python.org
Right, I was just referring to the parenthetical remark.

Georg Brandl

unread,
May 1, 2013, 5:38:35 PM5/1/13
to pytho...@python.org
Well, my point is that you currently don't have to inherit from int (or IntEnum)
to get an __int__ method on your Enum, which is what I find questionable. IMO
conversion to integers should only be defined for IntEnums. (But I haven't
followed all of the discussion and this may already have been decided.)

If __int__ stays where it is, a separate IntEnum is not necessary, but that
doesn't mean that enum baseclasses aren't useful for other use cases (and
they aren't hard to support, as ref435 shows.)

Georg Brandl

unread,
May 1, 2013, 5:44:03 PM5/1/13
to pytho...@python.org
Am 01.05.2013 23:19, schrieb Eli Bendersky:

> It's a common pattern to do this with a base class rather than a
> mixin, though, and I think the rule "only allow subclassing empty
> enums" makes a lot of sense.
>
>
> I see your point (and Antoine's example in the next email is good), but my
> concern is that this is a TIMTOWTDI thing, since the same can be achieved with
> mixins. Specifically, Antoine's example
> becomes:
>
> class IETFStatusCode:
> @classmethod
> def from_statusline(cls, line):
> return cls(int(line.split()[0]))
>
> class HTTPStatusCode(int, IETFStatusCode, Enum):
> NOT_FOUND = 404
>
> class SIPStatusCode(int, IETFStatusCode, Enum):
> RINGING = 180

Now try it like this:

class SIPStatusCode(IETFStatusCode, int, Enum):
RINGING = 180

and you'll get

Traceback (most recent call last):
File "/home/gbr/devel/ref435/ref435.py", line 84, in __new__
enum_item = obj_type.__new__(result, value)
TypeError: object.__new__(SIPStatusCode) is not safe, use int.__new__()

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
File "ex.py", line 11, in <module>
class SIPStatusCode(IETFStatusCode, int, Enum):
File "/home/gbr/devel/ref435/ref435.py", line 86, in __new__
raise EnumError(*exc.args) from None
TypeError: exception causes must derive from BaseException


> Same thing, while keeping the stdlib API cleaner and more minimal. Cleaner
> because "no subclassing" is a simpler, more explicit, and easier to understand
> rule than "no subclassing unless base class is devoid of enumeration values".
> And because we can no longer say "Enum classes are final", which is a relatively
> familiar and understood semantic.

I fear the "you can use mixins provided you put them in the right spot in the
base classes list" rule is not much simpler than the "no subclassing of enums
with values" rule.

Georg

Eli Bendersky

unread,
May 1, 2013, 5:48:08 PM5/1/13
to Georg Brandl, Ethan Furman, Python Dev
Good point. I think this may be just an artifact of the implementation - PEP 435 prohibits implicit conversion to integers for non-IntEnum enums. Since IntEnum came into existence, there's no real need for int-opearbility of other enums, and their values can be arbitrary anyway.

Ethan - unless I'm missing something, __int__ should probably be removed.

Eli




Georg Brandl

unread,
May 1, 2013, 5:52:33 PM5/1/13
to pytho...@python.org
Am 01.05.2013 23:48, schrieb Eli Bendersky:

> Well, my point is that you currently don't have to inherit from int (or IntEnum)
> to get an __int__ method on your Enum, which is what I find questionable. IMO
> conversion to integers should only be defined for IntEnums. (But I haven't
> followed all of the discussion and this may already have been decided.)
>
>
> Good point. I think this may be just an artifact of the implementation - PEP 435
> prohibits implicit conversion to integers for non-IntEnum enums. Since IntEnum
> came into existence, there's no real need for int-opearbility of other enums,
> and their values can be arbitrary anyway.

OK, I'm stupid -- I was thinking about moving the __int__ method to IntEnum
(that's why I brought it up in this part of the thread), but as a subclass of
int itself that obviously isn't needed :)

Georg Brandl

unread,
May 1, 2013, 5:53:30 PM5/1/13
to pytho...@python.org
Am 01.05.2013 23:44, schrieb Georg Brandl:

> Traceback (most recent call last):
> File "/home/gbr/devel/ref435/ref435.py", line 84, in __new__
> enum_item = obj_type.__new__(result, value)
> TypeError: object.__new__(SIPStatusCode) is not safe, use int.__new__()
>
> During handling of the above exception, another exception occurred:
>
> Traceback (most recent call last):
> File "ex.py", line 11, in <module>
> class SIPStatusCode(IETFStatusCode, int, Enum):
> File "/home/gbr/devel/ref435/ref435.py", line 86, in __new__
> raise EnumError(*exc.args) from None
> TypeError: exception causes must derive from BaseException

To be fair the secondary exception is an artifact of me trying the example
with Python 3.2, which doesn't have "from None".

Eli Bendersky

unread,
May 1, 2013, 5:57:33 PM5/1/13
to Georg Brandl, Python Dev
On Wed, May 1, 2013 at 2:52 PM, Georg Brandl <g.br...@gmx.net> wrote:
Am 01.05.2013 23:48, schrieb Eli Bendersky:

>     Well, my point is that you currently don't have to inherit from int (or IntEnum)
>     to get an __int__ method on your Enum, which is what I find questionable.  IMO
>     conversion to integers should only be defined for IntEnums.  (But I haven't
>     followed all of the discussion and this may already have been decided.)
>
>
> Good point. I think this may be just an artifact of the implementation - PEP 435
> prohibits implicit conversion to integers for non-IntEnum enums. Since IntEnum
> came into existence, there's no real need for int-opearbility of other enums,
> and their values can be arbitrary anyway.

OK, I'm stupid -- I was thinking about moving the __int__ method to IntEnum
(that's why I brought it up in this part of the thread), but as a subclass of
int itself that obviously isn't needed :)

You did bring up a good point, though - __int__ should not be part of vanilla Enum.

Eli

Ethan Furman

unread,
May 1, 2013, 6:11:20 PM5/1/13
to Python Dev
The reason __int__ is there is because pure Enums should be using plain ints as their value 95% or more of the time, and
being able to easily convert to a real int for either database storage, wire transmission, or C functions is a Good Thing.

IntEnum is for when the enum item *must* be a real, bonafide int in its own right, and the use case here is backwards
compatibility with APIs that are already using real ints -- and this is really the *only* time IntEnum should be used).

The downside to IntEnum is you lose all Enum type protection; so if you don't need a real int, use a fake int, er, I
mean, Enum, which can easily be int'ified on demand due to its handy dandy __int__ method.

Guido van Rossum

unread,
May 1, 2013, 6:22:55 PM5/1/13
to Ethan Furman, Python Dev
On Wed, May 1, 2013 at 3:11 PM, Ethan Furman <et...@stoneleaf.us> wrote:
> The reason __int__ is there is because pure Enums should be using plain ints
> as their value 95% or more of the time, and being able to easily convert to
> a real int for either database storage, wire transmission, or C functions is
> a Good Thing.

What would int(x) return if x is an enum whose value is not a plain int?

Why can't you use x.value for this use case?

--
--Guido van Rossum (python.org/~guido)

Eli Bendersky

unread,
May 1, 2013, 6:25:59 PM5/1/13
to Ethan Furman, Python Dev

Good point. I think this may be just an artifact of the implementation - PEP 435 prohibits implicit conversion to
integers for non-IntEnum enums. Since IntEnum came into existence, there's no real need for int-opearbility of other
enums, and their values can be arbitrary anyway.

Ethan - unless I'm missing something, __int__ should probably be removed.

The reason __int__ is there is because pure Enums should be using plain ints as their value 95% or more of the time, and being able to easily convert to a real int for either database storage, wire transmission, or C functions is a Good Thing.

Yes, but the .value attribute makes it "easy enough". If you have foo which is of type SomeEnum, all you need (if you know for sure it has int values) is to pass foo.value instead of just foo to places that do int conversion.

Relying on __init__ is *unsafe* in the general case because enums can have non-int values, or mixed values, or whatever. You only want "true int conversion" in rare cases for which IntEnum exists.

Eli

 

Ethan Furman

unread,
May 1, 2013, 6:51:44 PM5/1/13
to pytho...@python.org
On 05/01/2013 02:07 PM, Guido van Rossum wrote:
> On Wed, May 1, 2013 at 2:04 PM, Eli Bendersky <eli...@gmail.com> wrote:
>>
>> class BehaviorMixin:
>> # bla bla
>>
>> class MyBehavingIntEnum(int, BehaviorMixin, Enum):
>> foo = 1
>> bar = 2
>
> It's a common pattern to do this with a base class rather than a
> mixin, though, and I think the rule "only allow subclassing empty
> enums" makes a lot of sense.

So is this a pronouncement? I'm going to get whiplash if I change that bit of code many more times. ;)

--
~Ethan~

Guido van Rossum

unread,
May 1, 2013, 8:11:32 PM5/1/13
to Ethan Furman, Python-Dev

Steven D'Aprano

unread,
May 1, 2013, 9:47:25 PM5/1/13
to pytho...@python.org
On 02/05/13 06:45, Antoine Pitrou wrote:

> I was talking in the context where subclassing is allowed. I don't
> think there's a use-case for subclassing of non-empty enums. On the
> other hand, empty enums should probably allow subclassing (they are
> "abstract base enums", in a way).

If you google for "subclassing enums" you will find many people asking
how to subclass enums.

Apparently Apache's Java allows subclassing, if I'm reading this correctly:

http://commons.apache.org/proper/commons-lang/javadocs/api-2.6/org/apache/commons/lang/enums/Enum.html

So do Scala and Kotlin.

The most obvious use-case for subclassing enums is to extend them:

class Directions(Enum):
north = 1
east = 2
west = 3
south = 4

class Directions3D(Directions):
up = 5
down = 6


If you allow enums to have methods, then the most obvious use-case is to add or extend methods, no different to any other class.



--
Steven

Greg Ewing

unread,
May 1, 2013, 11:07:18 PM5/1/13
to pytho...@python.org
On 02/05/13 13:47, Steven D'Aprano wrote:
> The most obvious use-case for subclassing enums is to extend them:
>
> class Directions(Enum):
> north = 1
> east = 2
> west = 3
> south = 4
>
> class Directions3D(Directions):
> up = 5
> down = 6

It doesn't necessarily follow that subclassing is the right
mechanism for extending enums, though. If anything, you
really want to "superclass" them. Maybe

class Directions3D(Enum, extends = Directions):
up = 5
down = 6

Then we could have issubclass(Directions, Directions3D)
rather than the reverse.

--
Greg

Barry Warsaw

unread,
May 2, 2013, 11:20:03 AM5/2/13
to pytho...@python.org
On May 01, 2013, at 11:04 AM, Eli Bendersky wrote:

>Actually, in flufl.enum, IntEnum had to define a magic __value_factory__
>attribute, but in the current ref435 implementation this isn't needed, so
>IntEnum is just:
>
>class IntEnum(int, Enum):
> '''
> Class where every instance is a subclass of int.
> '''
>
>So why don't we just drop IntEnum from the API and tell users they should
>do the above explicitly, i.e.:

+1

-Barry

Barry Warsaw

unread,
May 2, 2013, 11:23:56 AM5/2/13
to pytho...@python.org
On May 01, 2013, at 08:47 PM, Georg Brandl wrote:

>Wait a moment... it might not be immediately useful for IntEnums (however,
>that's because base Enum currently defines __int__ which I find questionable),

And broken. And unnecessary. :)

>>> class Foo(Enum):
... a = 'a'
... b = 'b'
...
>>> int(Foo.a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __int__ returned non-int (type str)


...remove Enum.__int__()...

>>> class Bar(int, Enum):
... a = 1
... b = 2
...
>>> int(Bar.a)
1


So yes, Enum.__int__() should be removed.

Barry Warsaw

unread,
May 2, 2013, 11:54:00 AM5/2/13
to pytho...@python.org
On May 01, 2013, at 03:11 PM, Ethan Furman wrote:

>The reason __int__ is there is because pure Enums should be using plain ints
>as their value 95% or more of the time, and being able to easily convert to a
>real int for either database storage, wire transmission, or C functions is a
>Good Thing.

But then, Foo.a.value is good enough.

-Barry

Eli Bendersky

unread,
May 2, 2013, 11:58:07 AM5/2/13
to Barry Warsaw, Python Dev
On Thu, May 2, 2013 at 8:54 AM, Barry Warsaw <ba...@python.org> wrote:
On May 01, 2013, at 03:11 PM, Ethan Furman wrote:

>The reason __int__ is there is because pure Enums should be using plain ints
>as their value 95% or more of the time, and being able to easily convert to a
>real int for either database storage, wire transmission, or C functions is a
>Good Thing.

But then, Foo.a.value is good enough.

__int__ is out of Enum, Barry. You're preaching to the choir now ;-)

Eli
 

Reply all
Reply to author
Forward
0 new messages