python / cpython

The Python programming language
https://www.python.org
Other
63.16k stars 30.24k forks source link

Add AutoNumberedEnum to stdlib #71175

Closed 2df14aea-354c-46a6-8f5c-342c9f465901 closed 8 years ago

2df14aea-354c-46a6-8f5c-342c9f465901 commented 8 years ago
BPO 26988
Nosy @warsaw, @rhettinger, @vstinner, @ethanfurman, @vedgar, @Vgr255, @johnthagen, @kennethreitz
Files
  • issue26988.stoneleaf.01.patch
  • issue26988.stoneleaf.02.patch
  • issue26988.stoneleaf.03.patch
  • issue26988.stoneleaf.05.patch
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields: ```python assignee = 'https://github.com/ethanfurman' closed_at = created_at = labels = ['type-feature', 'library'] title = 'Add AutoNumberedEnum to stdlib' updated_at = user = 'https://github.com/johnthagen' ``` bugs.python.org fields: ```python activity = actor = 'ethan.furman' assignee = 'ethan.furman' closed = True closed_date = closer = 'ethan.furman' components = ['Library (Lib)'] creation = creator = 'John Hagen' dependencies = [] files = ['43694', '43743', '43972', '43986'] hgrepos = [] issue_num = 26988 keywords = ['patch'] message_count = 62.0 messages = ['265214', '265215', '265216', '265218', '265237', '269476', '269521', '270039', '270048', '270049', '270051', '270054', '270056', '270071', '270088', '270107', '270117', '270137', '270142', '270148', '270151', '270199', '270201', '270212', '270219', '270238', '270523', '270524', '271811', '271855', '271868', '272071', '272874', '272876', '272885', '272901', '272932', '272941', '272951', '272952', '272971', '272982', '273006', '273039', '273040', '273064', '273065', '273076', '273084', '273092', '273103', '273105', '273109', '273118', '273125', '273126', '273127', '273131', '273134', '273145', '273154', '273188'] nosy_count = 11.0 nosy_names = ['barry', 'rhettinger', 'vstinner', 'eli.bendersky', 'ethan.furman', 'python-dev', 'veky', 'abarry', 'John Hagen', 'David Hagen', 'kennethreitz'] pr_nums = [] priority = 'normal' resolution = 'rejected' stage = 'resolved' status = 'closed' superseder = None type = 'enhancement' url = 'https://bugs.python.org/issue26988' versions = ['Python 3.6'] ```

    fe5a23f9-4d47-49f8-9fb5-d6fbad5d9e38 commented 8 years ago

    I must say I never understood how exactly is assigning a constant better in this regard. To me, after

        a = SOMETHING
        b = SOMETHING

    a and b should be the same, right? _Especially_ when SOMETHING is None or (), but also when it's some bare identifier like AUTO_ENUM.

    Mental models are already broken. We are already "lying" according to semantics of type metaclass. There is no reason then to have to sprinkle some magic dust over our code, just so it looks like an assignment is the most important thing here, when it really isn't.

    Either we should embrace Python's power in full and communicate to users that there's something weird going on, or we shouldn't have AutoEnum. To me, green = None is in no way better, in fact it seems more dishonest, than simply green. Again, if we communicate (e.g. via style='declarative') that the semantics is not ordinary. (Alternatively, we might just write green = uuid4() and be done with it.:)

    kennethreitz commented 8 years ago

    Explicit is better than implicit :)

    fe5a23f9-4d47-49f8-9fb5-d6fbad5d9e38 commented 8 years ago

    Absolutely. How the hell is green = None explicit?!

    On the other hand, `class Color(Enum, style='declarative'):` is explicit. People must learn something. Why then don't they learn the right thing instead of "hey, assigning None inside enums magically does the right thing" - except when it doesn't.

    Just a hint of a nightmare scenario: you write a method decorator, but you forget to return a value from it (happened to me more times than I care to admit). Ooops, your method is now next member of your enum. Good luck debugging that. :-O

    In fact, what _is_ explicit, is this:

        class Color(metaclass=Enum):
            green = object()
            yellow = object()

    I could live with it. :-)

    vstinner commented 8 years ago

    2016-08-19 14:19 GMT+02:00 Vedran Čačić \report@bugs.python.org\:

    In fact, what _is_ explicit, is this:

    class Color(metaclass=Enum):
        green = object()
        yellow = object()

    I could live with it. :-)

    For me, it's legit to use a singleton in an enum. I mean that I expect that green != yellow. Some remark for an empty tuple.

    So I woud really prefer a contant from enum like AUTO_ENUM.

    fe5a23f9-4d47-49f8-9fb5-d6fbad5d9e38 commented 8 years ago

    For me, it's legit to use a singleton in an enum.

    But you cannot explain _why_, right? Like Raymond says, it's not working like anything else in Python works.

    To me, this looks almost the same as the gripes of those lazy people who want "myfile.close" expression to close myfile, or worse, "quit" to exit the Python interpreter. An irrational phobia of parentheses. Why not call your object, if you expect some code to be executed? [At least a dotted name like myfile.close _could_ be made to work using descriptors, but still I think everyone agrees it's not a good idea.]

    To me, we either want to stay with default type metaclass (style='imperative':), and write something like

        class Color(Enum):
            green = member()
            yellow = member()

    or we acknowledge that style='declarative' has its place under the sun (not only for Enums, of course... database models, namedtuples, even ABCs could profit from it), and embrace its full power, realizing it's not Python we usually see, but it's still Python.

    Magic is not something to be afraid of, if you understand it. We are talking Py3.6 here... aren't formatted strings a totally insane magic? Yet they ended up in the language, because they are immensely better than the alternatives. Here the gain is much smaller, but the threshold is much lower too: we don't need new syntax at all.

    ethanfurman commented 8 years ago

    Vedran, you have some very interesting points. However, if you really want to champion this you'll need to take it to Python Ideas.

    warsaw commented 8 years ago

    Hey, I just realized that you can get pretty darn close with just a little bit of extra typing, and existing stdlib:

        from enum import Enum
        from itertools import count
    
        auto = count()
    
        class Color(Enum):
            red = next(auto)
            green = next(auto)
            blue = next(auto)

    Look ma, no magic!

    ethanfurman commented 8 years ago

    No magic, but a whole heap of extra boiler-plate. :(

    kennethreitz commented 8 years ago

    There's a difference between boiler-plate and "code".

    On Aug 19, 2016, at 10:53 AM, Ethan Furman \report@bugs.python.org\ wrote:

    Ethan Furman added the comment:

    No magic, but a whole heap of extra boiler-plate. :(

    ----------


    Python tracker \report@bugs.python.org\ \http://bugs.python.org/issue26988\


    3fa19196-1e8a-4473-b524-6181d40d40a9 commented 8 years ago

    One solution similar to one proposed by Vedran works with the current Enum:

        class Color(Enum):
            red = object()
            green = object()
            blue= object()

    I tested this in PyCharm and it is perfectly happy with the autocomplete and everything. The main disadvantage is the boilerplate, of course. And perhaps "object()" does not show the clearest intent, but it depends on your perspective. The repr also looks kind of funny:

        >>>  repr(Color.red)
        <Color.red: <object object at 0x7fb2f353a0d0>>

    One possibility would be to add an auto() function to enum as a wrapper around object(), providing a more explicit name and a cleaner repr:

        from enum import Enum, auto
    
        class Color(Enum):
            red = auto()
            blue = auto()
            green = auto()
    
        repr(Color.red)
        <Color.red>
        # auto() means it has no (meaningful) value, so show nothing
    fe5a23f9-4d47-49f8-9fb5-d6fbad5d9e38 commented 8 years ago

    # Just wait until I put the keys to the time machine in their usual place... :-)

    Ok, while we're talking about whether declarative style is a good idea, Python has already got the initial seed of that style. With Guido's blessing! PEP-526 enables us to mix declarative and imperative style in "ordinary" code, so we can write

        @Enum
        class Color:
            green: member
            yellow: member

    without any additional syntax. I think it satisfies everyone: there are no parentheses, and there are no assignments. [And there is no misleading analogy with existing syntax, because this is a new syntax.:] There are just declarations, and the decorator instantiates them.

    Decorator is needed because formally we need to exclude the type checking semantics, and the only official way currently is through a decorator. But in fact we can use the forward references to _actually_ annotate the members with their real type:

        class Color(Enum):
            green: 'Color'
            yellow: 'Color'

    And once the forward references get a nicer syntax, and the unpacking issues are solved, we'll be able to write

        class Color(Enum):
            green, yellow: Color

    And I think finally everyone will be happy. :-)

    1762cc99-3127-4a62-9baf-30c3d0f51ef7 commented 8 years ago

    New changeset 2e243f78720e by Ethan Furman in branch 'default': bpo-26988: remove AutoEnum https://hg.python.org/cpython/rev/2e243f78720e