Finding the index of an item in a list

后端 未结 30 3322
你的背包
你的背包 2020-11-21 05:28

Given a list [\"foo\", \"bar\", \"baz\"] and an item in the list \"bar\", how do I get its index (1) in Python?

30条回答
  •  心在旅途
    2020-11-21 05:55

    Finding the index of an item given a list containing it in Python

    For a list ["foo", "bar", "baz"] and an item in the list "bar", what's the cleanest way to get its index (1) in Python?

    Well, sure, there's the index method, which returns the index of the first occurrence:

    >>> l = ["foo", "bar", "baz"]
    >>> l.index('bar')
    1
    

    There are a couple of issues with this method:

    • if the value isn't in the list, you'll get a ValueError
    • if more than one of the value is in the list, you only get the index for the first one

    No values

    If the value could be missing, you need to catch the ValueError.

    You can do so with a reusable definition like this:

    def index(a_list, value):
        try:
            return a_list.index(value)
        except ValueError:
            return None
    

    And use it like this:

    >>> print(index(l, 'quux'))
    None
    >>> print(index(l, 'bar'))
    1
    

    And the downside of this is that you will probably have a check for if the returned value is or is not None:

    result = index(a_list, value)
    if result is not None:
        do_something(result)
    

    More than one value in the list

    If you could have more occurrences, you'll not get complete information with list.index:

    >>> l.append('bar')
    >>> l
    ['foo', 'bar', 'baz', 'bar']
    >>> l.index('bar')              # nothing at index 3?
    1
    

    You might enumerate into a list comprehension the indexes:

    >>> [index for index, v in enumerate(l) if v == 'bar']
    [1, 3]
    >>> [index for index, v in enumerate(l) if v == 'boink']
    []
    

    If you have no occurrences, you can check for that with boolean check of the result, or just do nothing if you loop over the results:

    indexes = [index for index, v in enumerate(l) if v == 'boink']
    for index in indexes:
        do_something(index)
    

    Better data munging with pandas

    If you have pandas, you can easily get this information with a Series object:

    >>> import pandas as pd
    >>> series = pd.Series(l)
    >>> series
    0    foo
    1    bar
    2    baz
    3    bar
    dtype: object
    

    A comparison check will return a series of booleans:

    >>> series == 'bar'
    0    False
    1     True
    2    False
    3     True
    dtype: bool
    

    Pass that series of booleans to the series via subscript notation, and you get just the matching members:

    >>> series[series == 'bar']
    1    bar
    3    bar
    dtype: object
    

    If you want just the indexes, the index attribute returns a series of integers:

    >>> series[series == 'bar'].index
    Int64Index([1, 3], dtype='int64')
    

    And if you want them in a list or tuple, just pass them to the constructor:

    >>> list(series[series == 'bar'].index)
    [1, 3]
    

    Yes, you could use a list comprehension with enumerate too, but that's just not as elegant, in my opinion - you're doing tests for equality in Python, instead of letting builtin code written in C handle it:

    >>> [i for i, value in enumerate(l) if value == 'bar']
    [1, 3]
    

    Is this an XY problem?

    The XY problem is asking about your attempted solution rather than your actual problem.

    Why do you think you need the index given an element in a list?

    If you already know the value, why do you care where it is in a list?

    If the value isn't there, catching the ValueError is rather verbose - and I prefer to avoid that.

    I'm usually iterating over the list anyways, so I'll usually keep a pointer to any interesting information, getting the index with enumerate.

    If you're munging data, you should probably be using pandas - which has far more elegant tools than the pure Python workarounds I've shown.

    I do not recall needing list.index, myself. However, I have looked through the Python standard library, and I see some excellent uses for it.

    There are many, many uses for it in idlelib, for GUI and text parsing.

    The keyword module uses it to find comment markers in the module to automatically regenerate the list of keywords in it via metaprogramming.

    In Lib/mailbox.py it seems to be using it like an ordered mapping:

    key_list[key_list.index(old)] = new
    

    and

    del key_list[key_list.index(key)]
    

    In Lib/http/cookiejar.py, seems to be used to get the next month:

    mon = MONTHS_LOWER.index(mon.lower())+1
    

    In Lib/tarfile.py similar to distutils to get a slice up to an item:

    members = members[:members.index(tarinfo)]
    

    In Lib/pickletools.py:

    numtopop = before.index(markobject)
    

    What these usages seem to have in common is that they seem to operate on lists of constrained sizes (important because of O(n) lookup time for list.index), and they're mostly used in parsing (and UI in the case of Idle).

    While there are use-cases for it, they are fairly uncommon. If you find yourself looking for this answer, ask yourself if what you're doing is the most direct usage of the tools provided by the language for your use-case.

提交回复
热议问题