Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding start to enumerate() #47080

Closed
scottdial mannequin opened this issue May 12, 2008 · 14 comments
Closed

Adding start to enumerate() #47080

scottdial mannequin opened this issue May 12, 2008 · 14 comments
Labels
interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement

Comments

@scottdial
Copy link
Mannequin

scottdial mannequin commented May 12, 2008

BPO 2831
Nosy @gvanrossum, @birkenfeld, @rhettinger, @ncoghlan
Files
  • enumerate.diff: patch to add start= to enumerate
  • 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:

    assignee = None
    closed_at = <Date 2008-05-13.19:05:43.986>
    created_at = <Date 2008-05-12.03:49:33.387>
    labels = ['interpreter-core', 'type-feature']
    title = 'Adding start to enumerate()'
    updated_at = <Date 2010-05-06.13:49:14.343>
    user = 'https://bugs.python.org/scottdial'

    bugs.python.org fields:

    activity = <Date 2010-05-06.13:49:14.343>
    actor = 'scott.dial'
    assignee = 'none'
    closed = True
    closed_date = <Date 2008-05-13.19:05:43.986>
    closer = 'georg.brandl'
    components = ['Interpreter Core']
    creation = <Date 2008-05-12.03:49:33.387>
    creator = 'scott.dial'
    dependencies = []
    files = ['10301']
    hgrepos = []
    issue_num = 2831
    keywords = ['patch']
    message_count = 14.0
    messages = ['66705', '66709', '66710', '66711', '66712', '66776', '66778', '66783', '66789', '66790', '66792', '105111', '105145', '105148']
    nosy_count = 6.0
    nosy_names = ['gvanrossum', 'georg.brandl', 'rhettinger', 'ncoghlan', 'scott.dial', 'gsakkis']
    pr_nums = []
    priority = 'normal'
    resolution = 'accepted'
    stage = None
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue2831'
    versions = ['Python 2.6', 'Python 3.0']

    @scottdial
    Copy link
    Mannequin Author

    scottdial mannequin commented May 12, 2008

    Georg Brandel suggested enumerate() should have the ability to start on
    an arbitrary number (instead of always starting at 0). I suggest such a
    parameter should be keyword-only. Attached is a patch to add such a
    feature along with added test cases. Documentation still needs to be
    updated, but I wasn't sure how best to handle that anyways.

    I wasn't sure how best to handle a keyword-only argument, so I'd be
    interested to know if there is a better way.

    @scottdial scottdial mannequin added interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement labels May 12, 2008
    @rhettinger
    Copy link
    Contributor

    If a start argument gets accepted, it should be positional, not a
    keyword-only argument. That is a complete waste when there is just one
    argument with a straight-forward interpretation.

    Besides, METH_O is a lot faster than the alternatives.

    @rhettinger
    Copy link
    Contributor

    Forget the part about METH_O. That was incorrect.

    Another idea to order the positional args as ([start,], iterator).
    That corresponds to with range([start,] stop) and it matches the output
    order (number, element):

        for i, element in enumerate(10, iterable):
            ^-----------------------^
                  ^-------------------------^

    @scottdial
    Copy link
    Mannequin Author

    scottdial mannequin commented May 12, 2008

    As it stands, enumerate() already takes a "sequence" keyword as an
    alternative to the first positional argument (although this seems to be
    completely undocumented). So, as you say, METH_O is a no go.

    I agree with you in that my original complaint with the positional
    argument was that enumerate(iterable, start) was "backwards." My other
    argument was that a large number of these iterator utility functions are
    foo(*iterable) and upon seeing enumerate(foo, bar), a reader might be
    inclined to assume it was equivalent to enumerate(chain(foo, bar)).

    @rhettinger
    Copy link
    Contributor

    FWIW, at one point, Guido rejected all variants of the idea. His first
    objection was that enumerate() is all about pairing values with
    sequence indices, so starting from anything other than zero is in
    conflict with the core concept. His second objection is that all
    variants can easily be misread as starting at the nth item in the
    sequence (much like islice() does now): enumerate(3, 'abcdefg') -->
    (3,'d') (4,'e') (5, 'f') (6, 'g'). The latter mis-reading becomes more
    likely for those who think of enumerate as providing indices. In fact,
    one of the suggested names for enumerate was "indices".

    @ncoghlan
    Copy link
    Contributor

    Note that this functionality is currently available as follows:

    >>> from itertools import count
    >>> list(zip(count(3), 'abcdefg')
    [(3, 'a'), (4, 'b'), (5, 'c'), (6, 'd'), (7, 'e'), (8, 'f'), (9, 'g')]

    The enumerate(itr) builtin is just a convenience to avoid a module
    import for the most basic zip(count(), itr) version.

    The proposed patch would enable the example above to be written more
    verbosely as:

    >> list(enumerate('abcdefg', start=3))

    Or, with the positional argument approach as:

    >> list(enumerate(3, 'abcdefg'))

    So, more verbose than the existing approach, and ambiguous to boot - as
    Raymond noted, with the first it really isn't clear whether the first
    value returned would be (3, 'd') or (3, 'a'), and with the second form
    it isn't clear whether we're skipping the first three items, or
    returning only those items.

    Let's keep the builtins simple, and let itertools handle the variants -
    that's why the module exists.

    @ncoghlan
    Copy link
    Contributor

    Mentioning the zip(count(start), itr) version in the enumerate() docs
    may be a good idea though.

    (And of course, in 2.x, it should be izip() rather than zip() to
    preserve the memory efficiency of enumerate())

    @gvanrossum
    Copy link
    Member

    Thanks. I think this part is the main reason I see a start argument to
    enumerate as potentially problematic:

    """all variants can easily be misread as starting at the nth item in the
    sequence (much like islice() does now): enumerate(3, 'abcdefg') -->
    (3,'d') (4,'e') (5, 'f') (6, 'g')."""

    So the ambiguity is that enumerate(it, start=N) could be taken as
    skipping the first N items of it rather than adding N to the index it
    returns. (And it is my own argument!) I'd like to withdraw this
    argument. There are two separate use cases for using enumerate(): one is
    to iterate over a sequence and to have a handy index by which to update
    the value in the sequence. Another is for 1-based counting, usually when
    printing 1-based ordinals (such as line numbers in files, dates in a
    month or months in a year, etc.). N-based counting is less common but
    still conceivable. However I see no use for skipping items from the
    start, and if that use case ever came up, passing a slice to enumerate()
    would be the appropriate thing to do. In fact, if you passed in a slice,
    you might also want to pass a corresponding start value so the indices
    produced match those of the original sequence.

    So, I am still in favor of adding a new argument to enumerate().

    I'm neutral on the need for a keyword (don't think it would hurt, not
    sure how much it matters). I'm strongly against making it an optional
    *leading* argument like Raymond proposed; that's a style I just don't
    want to promote, range() and the curses module notwithstanding.

    Is the need to use zip(count(3), seq) for the offset index case really
    such
    a burden given the associated benefits in keeping the builtin function
    really simple and easy to understand?

    Yes, zip(count(3), seq) is too complex for this simple use case. I've
    always solved this so far with this less-than-elegant but certainly
    simpler idiom (except for users stuck in the tradition of for-loops in
    certain older languages :-):

    for i, line in enumerat(lines):
      i += 1
      print "%4d. %s" % (i, line)

    and variants thereof.

    @birkenfeld
    Copy link
    Member

    Okay. I'm against making the argument keyword-only -- IMO keyword-only
    arguments really should only be used in cases where their existence has
    some advantage, like for max().

    @gvanrossum
    Copy link
    Member

    Sure, fine.

    @birkenfeld
    Copy link
    Member

    Okay, committed a matching patch in r63208. Thank you all!

    @gsakkis
    Copy link
    Mannequin

    gsakkis mannequin commented May 5, 2010

    Just discovered this by chance; I would probably have noticed it earlier if the docstring had been updated. Let me know if it needs a new documentation bug ticket and I'll create one.

    Pretty handy feature by the way, thanks for adding it!

    @ncoghlan
    Copy link
    Contributor

    ncoghlan commented May 6, 2010

    Created bpo-8635 for the incomplete docstring

    @scottdial
    Copy link
    Mannequin Author

    scottdial mannequin commented May 6, 2010

    Created bpo-8636 for the broken test cases.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants