PEP-435 reference implementation
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~
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. -- ~Ethan~
On Tue, Apr 30, 2013 at 1:12 PM, Ethan Furman
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
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()
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. -- ~Ethan~
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? -- ~Ethan~
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.
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
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
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
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~
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~
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
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.
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~
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. -- ~Ethan~
On 02/05/13 01:09, Ethan Furman wrote:
New repo to avoid confusion:
Apparently I have to log in before I can even see the repo. Not going to happen. -- Steven
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
On 02/05/13 01:09, Ethan Furman wrote:
New repo to avoid confusion:
Apparently I have to log in before I can even see the repo.
Not going to happen.
-- Steven
_______________________________________________ Python-Dev mailing list Python-Dev@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)
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:
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~
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
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.
On Wed, May 1, 2013 at 8:44 AM, Barry Warsaw
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 _______________________________________________ Python-Dev mailing list Python-Dev@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)
Am 01.05.2013 17:35, schrieb Steven D'Aprano:
On 02/05/13 01:09, Ethan Furman wrote:
New repo to avoid confusion:
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
-----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 tseaver@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-----
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/4d2c4b94cdd35022a8a3e50554794... Georg
On Wed, May 1, 2013 at 9:18 AM, Tres Seaver
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)
On 2 May 2013 02:18, Tres Seaver
-----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
On 05/01/2013 02:36 PM, Tim Delaney wrote:
On 2 May 2013 02:18, Tres Seaver 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.
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~
On 2 May 2013 02:46, "Guido van Rossum"
On Wed, May 1, 2013 at 9:18 AM, Tres Seaver
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.
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 Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe:
http://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com
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
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
On Thu, May 2, 2013 at 11:37 AM, Steven D'Aprano
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.
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 | ncoghlan@gmail.com | Brisbane, Australia
participants (10)
-
Barry Warsaw
-
Eli Bendersky
-
Ethan Furman
-
Georg Brandl
-
Glenn Linderman
-
Guido van Rossum
-
Nick Coghlan
-
Steven D'Aprano
-
Tim Delaney
-
Tres Seaver