[Python-Dev] PEP-435 reference implementation

5 views
Skip to first unread message

Ethan Furman

unread,
Apr 30, 2013, 4:12:05 PM4/30/13
to Python Dev
Greetings,

Eli asked me to put the reference implementation here for review.

It is available at https://bitbucket.org/stoneleaf/aenum in ref435.py and test_ref435.py

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

Eli Bendersky

unread,
Apr 30, 2013, 5:07:34 PM4/30/13
to Python Dev



On Tue, Apr 30, 2013 at 1:12 PM, Ethan Furman <et...@stoneleaf.us> wrote:
Greetings,

Eli asked me to put the reference implementation here for review.

It is available at https://bitbucket.org/stoneleaf/aenum in ref435.py and test_ref435.py


Thanks, Ethan.

All - note that strictly speaking this implements PEP 435 with the modifications that were decided in recent discussions and pronouncement by Guido. If you're not up to date with the discussion, look for Ethan's summary and Guido's pronouncement in the archives. I'll work on updating PEP 435 to reflect these decisions by the end of this week.

Eli

Ethan Furman

unread,
Apr 30, 2013, 4:54:44 PM4/30/13
to Python Dev
On 04/30/2013 01:12 PM, Ethan Furman wrote:
>
> It is available at https://bitbucket.org/stoneleaf/aenum in ref435.py and test_ref435.py

Oh, as written in requires 3.3+. If you want to play around with it and are stuck on an earlier version, remove the
`from None` on line 68.

Glenn Linderman

unread,
Apr 30, 2013, 6:24:14 PM4/30/13
to Pytho...@python.org
On 4/30/2013 1:12 PM, Ethan Furman wrote:
Greetings,

Eli asked me to put the reference implementation here for review.

It is available at https://bitbucket.org/stoneleaf/aenum in ref435.py and test_ref435.py

Thanks for the code reference.

Tests ran fine here on Python 3.3

If I alter test_ref435.py at the end, as follows, I get an error: nothing matches 'BDFL'
Can someone explain why?


if __name__ == '__main__':
    class AnotherName( Name ):
        'just uses prior names'
    print(AnotherName['BDFL'])
   
    unittest.main()

Ethan Furman

unread,
Apr 30, 2013, 6:34:36 PM4/30/13
to pytho...@python.org
Because Guido said no subclassing.

At this point, if you try to subclass all your getting is the same type. So AnotherName is a string Enumeration.

Ethan Furman

unread,
Apr 30, 2013, 7:49:52 PM4/30/13
to pytho...@python.org
On 04/30/2013 03:34 PM, Ethan Furman wrote:
> On 04/30/2013 03:24 PM, Glenn Linderman wrote:
>> On 4/30/2013 1:12 PM, Ethan Furman wrote:
>>> Greetings,
>>>
>>> Eli asked me to put the reference implementation here for review.
>>>
>>> It is available at https://bitbucket.org/stoneleaf/aenum in ref435.py and test_ref435.py
>>
>> Thanks for the code reference.
>>
>> Tests ran fine here on Python 3.3
>>
>> If I alter test_ref435.py at the end, as follows, I get an error: nothing matches 'BDFL'
>> Can someone explain why?
>>
>>
>> if __name__ == '__main__':
>> class AnotherName( Name ):
>> 'just uses prior names'
>> print(AnotherName['BDFL'])
>
> Because Guido said no subclassing.
>
> At this point, if you try to subclass all your getting is the same type. So AnotherName is a string Enumeration.

It wouldn't be hard to check for instances of the Enum in question, and if there are some to raise an error instead.
That way:

--> class StrEnum(str, Enum):
... 'str-based enumerations'

--> class Names(StrEnum): # this works, as StrEnum has no instances
... BDFL = 'GvR'

--> class MoreNames(Names): # this fails, as Names has instances

Thoughts?

Glenn Linderman

unread,
Apr 30, 2013, 10:39:57 PM4/30/13
to pytho...@python.org
On 4/30/2013 4:49 PM, Ethan Furman wrote:
On 04/30/2013 03:34 PM, Ethan Furman wrote:
On 04/30/2013 03:24 PM, Glenn Linderman wrote:
On 4/30/2013 1:12 PM, Ethan Furman wrote:
Greetings,

Eli asked me to put the reference implementation here for review.

It is available at https://bitbucket.org/stoneleaf/aenum in ref435.py and test_ref435.py

Thanks for the code reference.

Tests ran fine here on Python 3.3

If I alter test_ref435.py at the end, as follows, I get an error: nothing matches 'BDFL'
Can someone explain why?


if __name__ == '__main__':
     class AnotherName( Name ):
         'just uses prior names'
     print(AnotherName['BDFL'])

Because Guido said no subclassing.

Indeed, I heard him.  But what I heard was that subclasses shouldn't be allowed to define new enumeration values, and that was the point of all his justification and the justifications in the Stack Overflow discussion he linked to. I don't want to disagree, or argue that point, there are reasons for it, although some have raised counter-arguments to it.  This is not intended to be a counter-argument to the point that there should be no new enumeration values created in subclasses.



At this point, if you try to subclass all your getting is the same type.  So AnotherName is a string Enumeration.

So if I get the same type, it'd be kind of nice if it worked the same too... even if the instances are of type Name, it seems that one should be able to look them up, the same as one can look them up using Name.


It wouldn't be hard to check for instances of the Enum in question, and if there are some to raise an error instead. That way:

--> class StrEnum(str, Enum):
...     'str-based enumerations'

--> class Names(StrEnum):  # this works, as StrEnum has no instances
...      BDFL = 'GvR'

--> class MoreNames(Names): # this fails, as Names has instances

Thoughts?

So to me, it would seem quite reasonable to allow only one class in the hierarchy to define instances.  If no instances have been defined before, then defining an enumeration instance should occur for each attribute for which it is appropriate.  But if a superclass has defined enumeration instances, then things defined in subclasses should not be taken as enumeration instances... and the choice should be between an error, and simply allowing it to be defined as a normal class attribute of the subclass.

Ethan Furman

unread,
May 1, 2013, 12:19:49 AM5/1/13
to pytho...@python.org
Latest code available at https://bitbucket.org/stoneleaf/aenum.

--> class Color(Enum):
... red = 1
... green = 2
... blue = 3

Enum items are virtual attributes looked by EnumType's __getattr__. The win here is that

--> Color.red.green.blue

no longer works. ;)

Subclassing an implemented Enum class now raises an error (is there a better word than 'implemented'?)

--> class MoreColor(Color):
... cyan = 4
... magenta = 5
... yellow = 6

Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "./ref435.py", line 83, in __new__
raise EnumError("cannot subclass an implemented Enum class")
ref435.EnumError: cannot subclass an implemented Enum class

Barry Warsaw

unread,
May 1, 2013, 12:47:51 AM5/1/13
to pytho...@python.org
On Apr 30, 2013, at 07:39 PM, Glenn Linderman wrote:

>>> Because Guido said no subclassing.
>
>Indeed, I heard him. But what I heard was that subclasses shouldn't be
>allowed to define new enumeration values, and that was the point of all his
>justification and the justifications in the Stack Overflow discussion he
>linked to. I don't want to disagree, or argue that point, there are reasons
>for it, although some have raised counter-arguments to it. This is not
>intended to be a counter-argument to the point that there should be no new
>enumeration values created in subclasses.

That's a shame, because disallowing subclassing to extend an enum will break
my existing use cases. Maybe I won't be able to adopt stdlib.enums after
all. :(

-Barry

Barry Warsaw

unread,
May 1, 2013, 1:41:02 AM5/1/13
to pytho...@python.org
On Apr 30, 2013, at 09:19 PM, Ethan Furman wrote:

>Subclassing an implemented Enum class now raises an error (is there a better
>word than 'implemented'?)
>
>--> class MoreColor(Color):
>... cyan = 4
>... magenta = 5
>... yellow = 6
>
>Traceback (most recent call last):
> File "<stdin>", line 1, in <module>
> File "./ref435.py", line 83, in __new__
> raise EnumError("cannot subclass an implemented Enum class")
>ref435.EnumError: cannot subclass an implemented Enum class

What does it break if you remove the `if base._enum` check? I mean, can we be
consenting adults here or not?

-Barry

Ethan Furman

unread,
May 1, 2013, 1:50:31 AM5/1/13
to pytho...@python.org
On 04/30/2013 09:47 PM, Barry Warsaw wrote:
> On Apr 30, 2013, at 07:39 PM, Glenn Linderman wrote:
>
>>>> Because Guido said no subclassing.
>>
>> Indeed, I heard him. But what I heard was that subclasses shouldn't be
>> allowed to define new enumeration values, and that was the point of all his
>> justification and the justifications in the Stack Overflow discussion he
>> linked to. I don't want to disagree, or argue that point, there are reasons
>> for it, although some have raised counter-arguments to it. This is not
>> intended to be a counter-argument to the point that there should be no new
>> enumeration values created in subclasses.
>
> That's a shame, because disallowing subclassing to extend an enum will break
> my existing use cases. Maybe I won't be able to adopt stdlib.enums after
> all. :(

What is your existing use case?

The way I had subclassing working originally was for the subclass to create it's own versions of the superclass' enum
items -- they weren't the same object, but they were equal:

--> class Color(Enum):
... red = 1
... green = 2
... blue = 3

--> class MoreColor(Color):
... cyan = 4
... magenta = 5
... yellow = 6

--> Color.red is MoreColor.red
False

--> Color.red == MoreColor.red
True

If you switched from `is` to `==` would this work for you?

--
~Ethan~

Ethan Furman

unread,
May 1, 2013, 2:40:22 AM5/1/13
to pytho...@python.org
On 04/30/2013 10:41 PM, Barry Warsaw wrote:
>
> What does it break if you remove the `if base._enum` check? I mean, can we be
> consenting adults here or not?

I removed the error and added a couple lines to EnumType.__getattr_, and now subclassing works as I think you are used
to it working.

Very unsure on this change being permanent (I have no objections to it).

--
~Ethan~

Steven D'Aprano

unread,
May 1, 2013, 3:05:36 AM5/1/13
to pytho...@python.org
On Tue, Apr 30, 2013 at 09:19:49PM -0700, Ethan Furman wrote:
> Latest code available at https://bitbucket.org/stoneleaf/aenum.
>
> --> class Color(Enum):
> ... red = 1
> ... green = 2
> ... blue = 3

Ethan, you seem to be writing a completely new PEP in opposition to
Barry's PEP 435. But your implementation doesn't seem to match what your
proto-PEP claims.

Your proto-PEP (file enum.txt) says:

``Enum` - a valueless, unordered type. It's related integer value is merely to
allow for database storage and selection from the enumerated class. An
``Enum`` will not compare equal with its integer value, but can compare equal
to other enums of which it is a subclass.


but:


py> import aenum
py> class Color(aenum.Enum):
... red = 1
...
py> Color.red == 1
True
py> type(Color.red) is int
True


So the implemented behaviour is completely different from the documented
behaviour. What gives?

Given the vast number of words written about enum values being instances
of the enum class, I'm surprised that your proto-PEP doesn't seem to
mention one word about that. All it says is that enum values are
singletons. (Unless I've missed something.)



--
Steven

Glenn Linderman

unread,
May 1, 2013, 3:09:49 AM5/1/13
to pytho...@python.org
On 4/30/2013 9:19 PM, Ethan Furman wrote:
Latest code available at https://bitbucket.org/stoneleaf/aenum.

--> class Color(Enum):
...     red = 1
...     green = 2
...     blue = 3

Enum items are virtual attributes looked by EnumType's __getattr__.  The win here is that

--> Color.red.green.blue

no longer works.  ;)

Color.red.green.blue not working seems like a win.

Still seems like it should be possible to look them up from a subclass, though.

--> class FancyColor( Color ):
...        'strikes my fancy'

--> FancyColor['red']
Color.red



Subclassing an implemented Enum class now raises an error (is there a better word than 'implemented'?)

--> class MoreColor(Color):
...     cyan = 4
...     magenta = 5
...     yellow = 6

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "./ref435.py", line 83, in __new__
    raise EnumError("cannot subclass an implemented Enum class")
ref435.EnumError: cannot subclass an implemented Enum class

Yes, I think raising an error is appropriate, if implementing Guido's "no subclass" comments, rather than treating what otherwise would look like enumeration settings as subclass attributes.

My suggested error wording would be "Cannot define additional enumeration items in a subclass".

That allows for "original enumeration items" to be defined in a subclass, of course.  And it isn't the subclassing that is disallowed, but the definition of more enumeration items that is disallowed.  At least, I hope that is the case.

Then, should consenting adults lift the restriction, there wouldn't be surprises in other code.

Ethan Furman

unread,
May 1, 2013, 9:16:47 AM5/1/13
to pytho...@python.org
On 05/01/2013 12:05 AM, Steven D'Aprano wrote:
> On Tue, Apr 30, 2013 at 09:19:49PM -0700, Ethan Furman wrote:
>> Latest code available at https://bitbucket.org/stoneleaf/aenum.
>
> Ethan, you seem to be writing a completely new PEP in opposition to
> Barry's PEP 435. But your implementation doesn't seem to match what your
> proto-PEP claims.

aenum.py was my original competitor for enums; the files you should be paying attention to are the *ref435* files, as
stated in the original post. (The stars are for pattern matching, not bolding.)

Apologies for any confusion.

--
~Ethan~

Ethan Furman

unread,
May 1, 2013, 11:09:20 AM5/1/13
to pytho...@python.org
New repo to avoid confusion:

https://bitbucket.org/stoneleaf/ref435

which has the latest updates from the feedback.

Subclassing is again disabled. Let's get the rest of it done, then we can come back to that issue if necessary.

Steven D'Aprano

unread,
May 1, 2013, 11:35:32 AM5/1/13
to pytho...@python.org
On 02/05/13 01:09, Ethan Furman wrote:
> New repo to avoid confusion:
>
> https://bitbucket.org/stoneleaf/ref435

Apparently I have to log in before I can even see the repo.

Not going to happen.



--
Steven

Guido van Rossum

unread,
May 1, 2013, 11:39:42 AM5/1/13
to Steven D'Aprano, Python-Dev
I can see it just fine without logging in, even in an Incognito Chrome window.

On Wed, May 1, 2013 at 8:35 AM, Steven D'Aprano <st...@pearwood.info> wrote:
> On 02/05/13 01:09, Ethan Furman wrote:
>>
>> New repo to avoid confusion:
>>
>> https://bitbucket.org/stoneleaf/ref435
>
>
> Apparently I have to log in before I can even see the repo.
>
> Not going to happen.
>
>
>
> --
> Steven
>
> _______________________________________________
> Python-Dev mailing list
> Pytho...@python.org
> http://mail.python.org/mailman/listinfo/python-dev
> Unsubscribe:
> http://mail.python.org/mailman/options/python-dev/guido%40python.org



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

Ethan Furman

unread,
May 1, 2013, 11:40:46 AM5/1/13
to pytho...@python.org
On 05/01/2013 08:35 AM, Steven D'Aprano wrote:
> On 02/05/13 01:09, Ethan Furman wrote:
>> New repo to avoid confusion:
>>
>> https://bitbucket.org/stoneleaf/ref435
>
> Apparently I have to log in before I can even see the repo.
>
> Not going to happen.

Sorry, just made it public. Try again?

--
~Ethan~

Barry Warsaw

unread,
May 1, 2013, 11:44:32 AM5/1/13
to pytho...@python.org
On Apr 30, 2013, at 10:50 PM, Ethan Furman wrote:

>The way I had subclassing working originally was for the subclass to create
>it's own versions of the superclass' enum items -- they weren't the same
>object, but they were equal:
>
>--> class Color(Enum):
>... red = 1
>... green = 2
>... blue = 3
>
>--> class MoreColor(Color):
>... cyan = 4
>... magenta = 5
>... yellow = 6
>
>--> Color.red is MoreColor.red
>False
>
>--> Color.red == MoreColor.red
>True
>
>If you switched from `is` to `==` would this work for you?

Not really, because in practice you don't compare one enum against another
explicitly. You have a value in a variable and you're comparing against a
literal enum. So `is` is still the more natural spelling.

My point is, if you want enums to behave more class-like because you're using
the class syntax, then you shouldn't explicitly break this one class-like
behavior just to protect some users from themselves. There doesn't even seem
to be an easy way to override the default behavior if you really wanted to do
it.

-Barry

Guido van Rossum

unread,
May 1, 2013, 12:14:12 PM5/1/13
to Barry Warsaw, Python-Dev
Personally I would probably compare enums using ==, but I agree that
'is' should also work -- since the instances are predefined there's no
reason to ever have multiple equivalent instances, so we might as well
guarantee it.

I'm sorry that my requirements for the relationship between the enum
class and its values ends up forcing the decision not to allow
subclasses (and I really mean *no* subclasses, not just no subclasses
that add new values), but after thinking it all over I still think
this is the right way forward. Something has got to give, and I think
that disallowing subclasses is better than having the isinstance
relationships be inverted or having to test for enum-ness using
something other than isinstance.

I guess the only way to change my mind at this point would be to come
up with overwhelming evidence that subclassing enums is a very useful
feature without which enums are pretty much useless. But we'd probably
have to give up something else, e.g. adding methods to enums, or any
hope that the instance/class/subclass relationships make any sense.

Contravariance sucks.

Georg Brandl

unread,
May 1, 2013, 12:16:29 PM5/1/13
to pytho...@python.org
Am 01.05.2013 17:35, schrieb Steven D'Aprano:
> On 02/05/13 01:09, Ethan Furman wrote:
>> New repo to avoid confusion:
>>
>> https://bitbucket.org/stoneleaf/ref435
>
> Apparently I have to log in before I can even see the repo.
>
> Not going to happen.

I'm sure he made the repo private by accident just to keep you out.

Georg

Tres Seaver

unread,
May 1, 2013, 12:18:11 PM5/1/13
to pytho...@python.org
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 05/01/2013 12:14 PM, Guido van Rossum wrote:
> But we'd probably have to give up something else, e.g. adding methods
> to enums, or any hope that the instance/class/subclass relationships
> make any sense.

I'd be glad to drop both of those in favor of subclassing: I think the
emphasis on "class-ness" makes no sense, given the driving usecases for
adopting enums into the stdlib in the first place. IOW, I would vote
that real-world usecases trump hypothetical purity.


Tres.
- --
===================================================================
Tres Seaver +1 540-429-0999 tse...@palladion.com
Palladion Software "Excellence by Design" http://palladion.com
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.11 (GNU/Linux)
Comment: Using GnuPG with undefined - http://www.enigmail.net/

iEYEARECAAYFAlGBQEMACgkQ+gerLs4ltQ6myQCZAZqKCR/6H6I8bogHtSwhTM9I
ok8AnjBKfFyuse6caMF085wBHvlrf0uA
=nJ5C
-----END PGP SIGNATURE-----

Georg Brandl

unread,
May 1, 2013, 12:29:32 PM5/1/13
to pytho...@python.org
Am 01.05.2013 17:09, schrieb Ethan Furman:
> New repo to avoid confusion:
>
> https://bitbucket.org/stoneleaf/ref435
>
> which has the latest updates from the feedback.
>
> Subclassing is again disabled. Let's get the rest of it done, then we can
> come back to that issue if necessary.

Thanks. I'm reviewing the code and adding comments to
https://bitbucket.org/stoneleaf/ref435/commits/4d2c4b94cdd35022a8a3e50554794f4a1c956e46

Georg

Guido van Rossum

unread,
May 1, 2013, 12:43:33 PM5/1/13
to Tres Seaver, Python-Dev
On Wed, May 1, 2013 at 9:18 AM, Tres Seaver <tse...@palladion.com> wrote:
> I'd be glad to drop both of those in favor of subclassing: I think the
> emphasis on "class-ness" makes no sense, given the driving usecases for
> adopting enums into the stdlib in the first place. IOW, I would vote
> that real-world usecases trump hypothetical purity.

Yeah, this is the dilemma. But what *are* the real-world use cases?
Please provide some.

Here's how I would implement "extending" an enum if subclassing were
not allowed:

class Color(Enum):
red = 1
white = 2
blue = 3

class ExtraColor(Enum):
orange = 4
yellow = 5
green = 6

flag_colors = set(Color) | set(ExtraColor)

Now I can test "c in flag_colors" to check whether c is a flag color.
I can also loop over flag_colors. If I want the colors in definition
order I could use a list instead:

ordered_flag_colors = list(Color) + list(ExtraColor)

But this would be less or more acceptable depending on whether it is a
common or esoteric use case.

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

Tim Delaney

unread,
May 1, 2013, 5:36:08 PM5/1/13
to Python-Dev
On 2 May 2013 02:18, Tres Seaver <tse...@palladion.com> wrote:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 05/01/2013 12:14 PM, Guido van Rossum wrote:
> But we'd probably have to give up something else, e.g. adding methods
> to enums, or any hope that the instance/class/subclass relationships
> make any sense.

I'd be glad to drop both of those in favor of subclassing:  I think the
emphasis on "class-ness" makes no sense, given the driving usecases for
adopting enums into the stdlib in the first place.   IOW, I would vote
that real-world usecases trump hypothetical purity.

I have real-world use cases of enums (in java) that are essentially classes and happen to use the enum portion purely to obtain a unique name without explicitly supplying an ID.

In the particular use case I'm thinking of, the flow is basically like this:

1. An Enum where each instance describes the shape of a database query.
2. Wire protocol where the Enum instance name is passed.
3. At one end, the data for performing the DB query is populated.
4. At the other end, the data is extracted and the appropriate enum is used to perform the query.

Why use an enum? By using the name in the wire protocol I'm guaranteed a unique ID that won't change across versions (there is a requirement to only add to the enum) but does not rely on people setting it manually - the compiler will complain if there is a conflict, as opposed to setting values. And having the behaviour be part of the class simplifies things immensely.

Yes, I could do all of this without an enum (have class check that each supplied ID is unique, etc) but the code is much clearer using the enum.

I am happy to give up subclassing of enums in order to have behaviour on enum instances. I've always seen enums more as a container for their instances. I do want to be able to find out what enum class a particular enum belongs to (I've used this property in the past) and it's nice that the enum instance is an instance of the defining class (although IMO not required).

I see advantages to enums being subclassable, but also significant disadvantages. For example, given the following:

class Color(Enum):
    red = 1

class MoreColor(Color):
    blue = 2

class DifferentMoreColor(Color):
    green = 2

then the only reasonable way for it to work IMO is that MoreColor contains both (red, blue) and DifferentMoreColor contains both (red, green) and that red is not an instance of either MoreColor or DifferentMoreColor. If you allow subclassing, at some point either something is going to be intuitively backwards to some people (in the above that Color.red is not an instance of MoreColor), or is going to result in a contravariance violation.

Tim Delaney 

Ethan Furman

unread,
May 1, 2013, 6:02:00 PM5/1/13
to pytho...@python.org
On 05/01/2013 02:36 PM, Tim Delaney wrote:
Nice example, thank you.

As far as subclassing goes, you can have behavior either way. The sticky issue is are the enum items from an inherited
enumeration available in the child enumeration, or should such an inheritance raise an error, or should all subclassing
inheritance raise an error.

It sounds like we're leaning towards allowing subclassing as long as the enumeration being subclassed doesn't define any
enum items itself.

--
~Ethan~

Nick Coghlan

unread,
May 1, 2013, 6:54:10 PM5/1/13
to Guido van Rossum, Tres Seaver, pytho...@python.org

If enums had an "as_dict" method that returned an ordered dictionary, you could do:

class MoreColors(Enum):
    locals().update(Colors.as_dict())
    orange = 4
    ...

Using a similar API to PEP 422's class initialisation hook, you could even simplify that to:

class MoreColors(Enum,  namespace=Colors.as_dict()):
    orange = 4
    ...

Cheers,
Nick.

>
> --
> --Guido van Rossum (python.org/~guido)
> _______________________________________________
> Python-Dev mailing list
> Pytho...@python.org
> http://mail.python.org/mailman/listinfo/python-dev

> Unsubscribe: http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com

Steven D'Aprano

unread,
May 1, 2013, 9:37:48 PM5/1/13
to pytho...@python.org
On 02/05/13 08:54, Nick Coghlan wrote:

> If enums had an "as_dict" method that returned an ordered dictionary, you
> could do:
>
> class MoreColors(Enum):
> locals().update(Colors.as_dict())


Surely that is an implementation-specific piece of code? Writing to locals()
is not guaranteed to work, and the documentation warns against it.

http://docs.python.org/3/library/functions.html#locals


--
Steven
_______________________________________________
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

Steven D'Aprano

unread,
May 1, 2013, 10:33:06 PM5/1/13
to pytho...@python.org
On 02/05/13 02:43, Guido van Rossum wrote:

> Here's how I would implement "extending" an enum if subclassing were
> not allowed:
>
> class Color(Enum):
> red = 1
> white = 2
> blue = 3
>
> class ExtraColor(Enum):
> orange = 4
> yellow = 5
> green = 6
>
> flag_colors = set(Color) | set(ExtraColor)
>
> Now I can test "c in flag_colors" to check whether c is a flag color.

Earlier you argued that testing for enums should be done with isinstance, not "in". Or did I misunderstood? So I would have thought that isinstance(c, (Color, ExtraColor)) would be the way to check c.

I would prefer to write "c in ExtraColor", assuming c extends Color.


Lookups by value also become more complex. Instead of c = ExtraColor[value], this leads to two choices, both of which are equally ugly in my opinion:


c = [c for c in flag_colors if c.value == value][0]


try:
c = ExtraColor[value]
except: # I'm not sure what exception you get here
c = Color[value]


There is a further problem if the two enum classes have duplicate values, by accident or design. Accident being more likely, since now you have no warning when ExtraColor defines a value that duplicates something in Color. flag_colors will now contain both duplicates, since enum values from different enums never compare equal, but that's probably not what you want.



--
Steven

Nick Coghlan

unread,
May 2, 2013, 9:10:04 PM5/2/13
to Steven D'Aprano, pytho...@python.org
On Thu, May 2, 2013 at 11:37 AM, Steven D'Aprano <st...@pearwood.info> wrote:
> On 02/05/13 08:54, Nick Coghlan wrote:
>
>> If enums had an "as_dict" method that returned an ordered dictionary, you
>> could do:
>>
>> class MoreColors(Enum):
>> locals().update(Colors.as_dict())
>
>
>
> Surely that is an implementation-specific piece of code? Writing to locals()
> is not guaranteed to work, and the documentation warns against it.
>
> http://docs.python.org/3/library/functions.html#locals

I've long thought we should stop being wishy-washy about modification
of locals(), and make the current CPython behaviour part of the
language spec:

- at module scope, locals() must return the same thing as globals(),
which must be the actual module namespace
- at class scope, it must return the namespace returned by __prepare__()
- at function scope, it returns a snapshot of the current locals and
free variables, and thus does not support modifications (and may not
see subsequent changes)

I'll start a separate thread about that.

Cheers,
Nick.

--
Nick Coghlan | ncog...@gmail.com | Brisbane, Australia
Reply all
Reply to author
Forward
0 new messages