http://bugs.python.org/issue19011 So, as anyone who has worked with Enum is probably aware by now, Enum's are a strange duck -- you might even call them platypuses. For example, Enum members are instances of the class, but they are defined inside the class structure, and new ones cannot be created afterwards; Enum classes also support iteration and containment checks. What I'm looking for feedback on is the question is #19011: should an Enum member be considered a class level attribute? On the one hand, they are defined inside the class, and they are accessed via dot notation (EnumClass.member). On the other hand, inside the class they look like 3 and 5 and 'up' and 'east', and they don't live in the class dictionary. Also, if we change Enum so that members do act more like class attributes, then things like Color.red.blue.green.blue will result in Color.blue, and that seems stranger to me than having class instances be available on the class without be full-fledged class-attributes. Thoughts? Opinions? Pearls of wisdom? -- ~Ethan~
On Sun, Sep 22, 2013 at 4:52 PM, Ethan Furman
http://bugs.python.org/**issue19011 http://bugs.python.org/issue19011
So, as anyone who has worked with Enum is probably aware by now, Enum's are a strange duck -- you might even call them platypuses.
For example, Enum members are instances of the class, but they are defined inside the class structure, and new ones cannot be created afterwards; Enum classes also support iteration and containment checks.
What I'm looking for feedback on is the question is #19011: should an Enum member be considered a class level attribute?
On the one hand, they are defined inside the class, and they are accessed via dot notation (EnumClass.member).
On the other hand, inside the class they look like 3 and 5 and 'up' and 'east', and they don't live in the class dictionary.
Also, if we change Enum so that members do act more like class attributes, then things like Color.red.blue.green.blue will result in Color.blue, and that seems stranger to me than having class instances be available on the class without be full-fledged class-attributes.
Thoughts? Opinions? Pearls of wisdom?
I wouldn't lose much sleep over this. Classes can override __getattribute__ so that instance variables appear to exist even though they are not in the instance's __dict__. It's the same for metaclasses. As for attributes appearing different inside the class than when accessed as an attribute, that's not unusual -- descriptors can legitimately do that. It was more common in Python 2, where this applied to all unbound methods, but even in Python 3 it applies to static and class methods. I would draw the line at being able to access members as attributes of other members. Making Color.red.blue be a spelling for Color.blue feels like an abomination. If you can make dir(Color) return the strings 'red', 'blue' etc. in addition to other class attributes without making Color.red.blue work, go for it. If you can't, that's fine too. Users should (obviously) steer clear from relying on either behavior. -- --Guido van Rossum (python.org/~guido)
: On Sun, Sep 22, 2013 at 04:52:36PM -0700, Ethan Furman wrote:
So, as anyone who has worked with Enum is probably aware by now, Enum's are a strange duck -- you might even call them platypuses.
Yes :-)
What I'm looking for feedback on is the question is #19011: should an Enum member be considered a class level attribute?
I may be misunderstanding the use case given in the issue, but it seems to me that having to use Color.red.__class__.blue (what is being complained about in the issue), while not exactly pretty, makes a lot more semantic sense than Color.red.blue ... which is just bizarre. Enum members aren't class attributes, even if the way they're defined makes them look as though they are. Allowing this is just asking for more confusion on the part of anyone using them IMHO. -[]z. -- Zero Piraeus: omnia omnibus http://etiol.net/pubkey.asc
On Sunday, September 22, 2013, Zero Piraeus wrote:
I may be misunderstanding the use case given in the issue, but it seems to me that having to use
Color.red.__class__.blue
(what is being complained about in the issue), while not exactly pretty, makes a lot more semantic sense than
Color.red.blue
... which is just bizarre.
Right.
Enum members aren't class attributes, even if the way they're defined makes them look as though they are. Allowing this is just asking for more confusion on the part of anyone using them IMHO.
Depends on how you define "class sttribute". --Guido -- --Guido van Rossum (on iPad)
On Sun, Sep 22, 2013 at 10:41 PM, Zero Piraeus
I may be misunderstanding the use case given in the issue,
To clarify the use case, since it is my bug, this is so that the enum values are always available for comparison. The exact use case is in Django templates where a value comes from the database. If you want to compare you either have to use __class__ which I would say is a code smell, or you have to provide the Enum class. The code we are using for this is open source http://github.com/tindie/django-tidyenum. An example of how this will be used in practice is: {% if object.state == object.state.completed %} some html {% endif %}
but it seems to me that having to use
Color.red.__class__.blue
(what is being complained about in the issue), while not exactly pretty, makes a lot more semantic sense than
Color.red.blue
... which is just bizarre.
Any more bizarre than any other class that has properties of it's own type? a = 0 a.real.numerator.real.numerator It is only weird because we are not used to seeing classes where the properties are instances of the class. I am not a core developer, but I have been programming in Python for more than 10 years, and I (and the other similarly experienced developers I work with) found that not having access to the class level properties was weird (yes I am aware that they are not actually class level properties but everywhere else Enum works hard to make it look like they are. See for instance the __dir__ method: http://hg.python.org/cpython/file/ed011b0d7daf/Lib/enum.py#l242). -Chris -- Christopher Lambacher chris@kateandchris.net
On Mon, Sep 23, 2013 at 6:45 AM, Chris Lambacher
On Sun, Sep 22, 2013 at 10:41 PM, Zero Piraeus
wrote: I may be misunderstanding the use case given in the issue,
To clarify the use case, since it is my bug, this is so that the enum values are always available for comparison. The exact use case is in Django templates where a value comes from the database. If you want to compare you either have to use __class__ which I would say is a code smell, or you have to provide the Enum class. The code we are using for this is open source http://github.com/tindie/django-tidyenum. An example of how this will be used in practice is:
{% if object.state == object.state.completed %} some html {% endif %}
Now I see your use case. But I disagree that the best solution is to allow accessing the enum values as attributes on object.state -- I would recommend using __class__ to make it clear to the reader what's going on, or to add the right Enum subclass to your template parameters. Tbe expression you show in your example here will just baffle most readers who haven't thought deeply about the issue. (How would you compare a value that is a timedelta with a known timedelta? You'd have to import the datetime module or use __class__, right?)
but it seems
to me that having to use
Color.red.__class__.blue
(what is being complained about in the issue), while not exactly pretty, makes a lot more semantic sense than
Color.red.blue
... which is just bizarre.
Any more bizarre than any other class that has properties of it's own type?
Yes, because you rarely if ever see them accessed that way.
a = 0 a.real.numerator.real.numerator
But that's different! Each of the attributes here (.real, .numerator) is defined as an instance attribute.
It is only weird because we are not used to seeing classes where the properties are instances of the class. I am not a core developer, but I have been programming in Python for more than 10 years, and I (and the other similarly experienced developers I work with) found that not having access to the class level properties was weird (yes I am aware that they are not actually class level properties but everywhere else Enum works hard to make it look like they are. See for instance the __dir__ method: http://hg.python.org/cpython/file/ed011b0d7daf/Lib/enum.py#l242).
Sorry to burst your bubble, but there is no rule that because you can access something on the class you should be able to access it on the instance. Try asking an instance for its class's __mro__ or __bases__. -- --Guido van Rossum (python.org/~guido)
On Mon, Sep 23, 2013 at 07:53:00AM -0700, Guido van Rossum wrote:
there is no rule that because you can access something on the class you should be able to access it on the instance. Try asking an instance for its class's __mro__ or __bases__.
It might not be a rule, but it's certainly the norm. I reckon that class attributes that aren't accessible from the instance are significantly more surprising than Color.red.blue. I know I'm in a minority here, but Color.red.blue seems obvious and straightforward to me. The fact that it doesn't work surprises me. Given that instance = Color.red assert isinstance(instance, Color) # well of course it is assert hasattr(Color, "blue") I would expect instance.blue to work, and I'm completely at a loss as to how Enum has managed to prevent it. -- Steven
On Mon, Sep 23, 2013 at 8:16 AM, Steven D'Aprano
On Mon, Sep 23, 2013 at 07:53:00AM -0700, Guido van Rossum wrote:
there is no rule that because you can access something on the class you should be able to access it on the instance. Try asking an instance for its class's __mro__ or __bases__.
It might not be a rule, but it's certainly the norm. I reckon that class attributes that aren't accessible from the instance are significantly more surprising than Color.red.blue.
Whether this feels right (to me) depends on whether the class attribute was *meant* to be used as an instance attribute as well, or not. For methods, that's an obvious yes. For class attributes used as defaults for instance variables, yes again. For class attributes that are meant to be per-class state rather than per-instance state, I would use the class name or __class__ to make it clear to the (human) reader that we're using a class variable. This also avoids accidentally overriding it with an instance variable (although that's not an issue for Enum).
I know I'm in a minority here, but Color.red.blue seems obvious and straightforward to me. The fact that it doesn't work surprises me. Given that
instance = Color.red assert isinstance(instance, Color) # well of course it is assert hasattr(Color, "blue")
I would expect instance.blue to work, and I'm completely at a loss as to how Enum has managed to prevent it.
Through the magic of metaclasses. :-) The only attributes that an enum instance should have are things like .name and .value, and the methods implementing various standard protocols like __repr__ and __eq__. The whole point of Enum is that it involves a fair bit of magic to offer a robust way to define Enums that "feel right" as Enums. You shouldn't think of it as a slightly odd class. You should think of it as a different data type altogether that happens to be implemented using metaclass tecnology. But many things you "know" about classes don't apply to Enums; this is just one of many. -- --Guido van Rossum (python.org/~guido)
On 09/23/2013 08:16 AM, Steven D'Aprano wrote:
I would expect instance.blue to work, and I'm completely at a loss as to how Enum has managed to prevent it.
[A peek behind the curtains...] Currently, Enum members do not live in the class dict. So when, for example, blue is searched for in red, it is not found in the instance dict, and it is not found in the class dict. As you know, __getattr__ will then be invoked -- but the Enum class does not have its own __getattr__, nor its own __getattribute__, and so we get an AttributeError. Well, you may ask, if blue does not live in the class dict, how does Color.blue work? I'm glad you asked. ;) Color is of type EnumMeta, and EnumMeta /does/ have __getattr__, so a failed /class/ lookup will invoke the metaclass __getattr__, which will search in the right place, find, and return, Color.blue. -- ~Ethan~
Steven D'Aprano wrote:
It might not be a rule, but it's certainly the norm. I reckon that class attributes that aren't accessible from the instance are significantly more surprising than Color.red.blue.
There are really two different kinds of things that we refer to as "class attributes". One is things that really are attributes of the class itself, and the other is things that are meant to serve as default or shared instance attributes. The confusion comes in because we use the same terminology for both, and because Python doesn't provide any straightforward way of creating user-defined class-only attributes, so shared attributes tend to get abused for that purpose. Then when someone comes along and creates a true class-only attribute, people get all surprised and complain about it. They shouldn't, IMO. -- Greg
On 24 Sep 2013 08:09, "Greg Ewing"
Steven D'Aprano wrote:
It might not be a rule, but it's certainly the norm. I reckon that class
attributes that aren't accessible from the instance are significantly more surprising than Color.red.blue.
There are really two different kinds of things that we refer to as "class attributes". One is things that really are attributes of the class itself, and the other is things that are meant to serve as default or shared instance attributes.
The confusion comes in because we use the same terminology for both, and because Python doesn't provide any straightforward way of creating user-defined class-only attributes,
Using @property in a metaclass definition isn't *that* complicated :) (says the guy who helps maintain the type system)
so shared attributes tend to get abused for that purpose. Then when someone comes along and creates a true class-only attribute, people get all surprised and complain about it.
One of the interesting aspects of adding Enum has been the subtle descriptor handling bugs it has uncovered in the inspect module :)
They shouldn't, IMO.
There's a helper for Enum's descriptors that will probably be exposed through the types module in the next 3.4 alpha (tentative name is types.DynamicClassAttribute). It's the inverse, though - it throws AttributeError when looked up on the *class* in order to trigger __getattr__ on the metaclass. Cheers, Nick.
-- Greg
_______________________________________________ Python-Dev mailing list Python-Dev@python.org https://mail.python.org/mailman/listinfo/python-dev Unsubscribe:
https://mail.python.org/mailman/options/python-dev/ncoghlan%40gmail.com
On Mon, Sep 23, 2013 at 09:45:46AM -0400, Chris Lambacher wrote:
[...] The exact use case is in Django templates where a value comes from the database. If you want to compare you either have to use __class__ which I would say is a code smell, or you have to provide the Enum class.
I'm having a hard time seeing why the latter is problematic, I must admit (certainly no more so than the "Alice in Wonderland" side effects previously described).
[...] An example of how this will be used in practice is:
{% if object.state == object.state.completed %} some html {% endif %}
The names used slightly obscure the weirdness of it, but what you're really saying there is: if my_state == my_state.another_state ... which feels more like a code smell to me than {% if object.state == State.completed %} some html {% endif %} That's quite intelligible, and doesn't require anyone to know that an Enum member's siblings can, in your proposal, be accessed directly via dot notation (an unintuitive state of affairs, to me at least). -[]z. -- Zero Piraeus: omnia omnibus http://etiol.net/pubkey.asc
On Mon, Sep 23, 2013 at 8:17 AM, Zero Piraeus
On Mon, Sep 23, 2013 at 09:45:46AM -0400, Chris Lambacher wrote:
[...] The exact use case is in Django templates where a value comes from the database. If you want to compare you either have to use __class__ which I would say is a code smell, or you have to provide the Enum class.
I'm having a hard time seeing why the latter is problematic, I must admit (certainly no more so than the "Alice in Wonderland" side effects previously described).
[...] An example of how this will be used in practice is:
{% if object.state == object.state.completed %} some html {% endif %}
The names used slightly obscure the weirdness of it, but what you're really saying there is:
if my_state == my_state.another_state
... which feels more like a code smell to me than
{% if object.state == State.completed %} some html {% endif %}
That's quite intelligible, and doesn't require anyone to know that an Enum member's siblings can, in your proposal, be accessed directly via dot notation (an unintuitive state of affairs, to me at least).
Right. The OP is just concerned that (because these are Django templates) he will have to pass in the 'State' class as a separate template parameter for this to work. But to me that's a problem with Django, and not something for which the Enum type should bend over backwards to cover up for. Given that it's a Djano weakness, IMO the __class__ solution is reasonable enough, although in theory it would allow having object.state be something of the wrong class that happens to have a 'completed' attribute -- that would be a bug of a different color, though. :-) -- --Guido van Rossum (python.org/~guido)
Guido van Rossum writes:
On Mon, Sep 23, 2013 at 8:17 AM, Zero Piraeus
wrote: On Mon, Sep 23, 2013 at 09:45:46AM -0400, Chris Lambacher wrote:
[...] An example of how this will be used in practice is:> {% if object.state == object.state.completed %} some html {% endif %}
[Zero Piraeus suggests instead:]
{% if object.state == State.completed %} some html {% endif %}
That's quite intelligible, and doesn't require anyone to know that an Enum member's siblings can, in your proposal, be accessed directly via dot notation (an unintuitive state of affairs, to me at least).
Right. The OP is just concerned that (because these are Django templates) he will have to pass in the 'State' class as a separate template parameter for this to work.
Given your earlier description of what makes sense for class attributes, an alternative solution might be to put State-valued class attributes (constants) on DjangoObject (the object's class), like DjangoObject.completed_state = State.completed, and so on. Then you write "{% if object.state == object.completed_state %}". IIUC, you wouldn't have a problem with that? It still doesn't feel quite right, but given the limitations of a template language, it might grow on me. Another alternative would be to have attributes like 'completed' be *boolean* properties computed from a State-valued attribute, and write just "{% if object.completed %}". This actually feels good to me given it's a templating language. But I don't know if either of those is reasonable in the context.
I am quickly losing interest in this -- I was only jumping in because I
feared there was support for making Color.red.blue "work". I don't think I
hve to worry about that any more -- what's best for Django templates is up
to the template author.
On Mon, Sep 23, 2013 at 10:46 AM, Stephen J. Turnbull
Guido van Rossum writes:
On Mon, Sep 23, 2013 at 8:17 AM, Zero Piraeus
wrote: On Mon, Sep 23, 2013 at 09:45:46AM -0400, Chris Lambacher wrote:
[...] An example of how this will be used in practice is:> {% if object.state == object.state.completed %} some html {% endif %}
[Zero Piraeus suggests instead:]
{% if object.state == State.completed %} some html {% endif %}
That's quite intelligible, and doesn't require anyone to know that an Enum member's siblings can, in your proposal, be accessed directly via dot notation (an unintuitive state of affairs, to me at least).
Right. The OP is just concerned that (because these are Django templates) he will have to pass in the 'State' class as a separate template parameter for this to work.
Given your earlier description of what makes sense for class attributes, an alternative solution might be to put State-valued class attributes (constants) on DjangoObject (the object's class), like DjangoObject.completed_state = State.completed, and so on. Then you write "{% if object.state == object.completed_state %}".
IIUC, you wouldn't have a problem with that? It still doesn't feel quite right, but given the limitations of a template language, it might grow on me.
Another alternative would be to have attributes like 'completed' be *boolean* properties computed from a State-valued attribute, and write just "{% if object.completed %}". This actually feels good to me given it's a templating language.
But I don't know if either of those is reasonable in the context.
-- --Guido van Rossum (python.org/~guido)
participants (8)
-
Chris Lambacher
-
Ethan Furman
-
Greg Ewing
-
Guido van Rossum
-
Nick Coghlan
-
Stephen J. Turnbull
-
Steven D'Aprano
-
Zero Piraeus