Python idiom to return first item or None

后端 未结 24 1716
清酒与你
清酒与你 2020-12-07 07:46

I\'m sure there\'s a simpler way of doing this that\'s just not occurring to me.

I\'m calling a bunch of methods that return a list. The list may be empty. If the

相关标签:
24条回答
  • 2020-12-07 08:04

    The OP's solution is nearly there, there are just a few things to make it more Pythonic.

    For one, there's no need to get the length of the list. Empty lists in Python evaluate to False in an if check. Just simply say

    if list:
    

    Additionally, it's a very Bad Idea to assign to variables that overlap with reserved words. "list" is a reserved word in Python.

    So let's change that to

    some_list = get_list()
    if some_list:
    

    A really important point that a lot of solutions here miss is that all Python functions/methods return None by default. Try the following below.

    def does_nothing():
        pass
    
    foo = does_nothing()
    print foo
    

    Unless you need to return None to terminate a function early, it's unnecessary to explicitly return None. Quite succinctly, just return the first entry, should it exist.

    some_list = get_list()
    if some_list:
        return list[0]
    

    And finally, perhaps this was implied, but just to be explicit (because explicit is better than implicit), you should not have your function get the list from another function; just pass it in as a parameter. So, the final result would be

    def get_first_item(some_list): 
        if some_list:
            return list[0]
    
    my_list = get_list()
    first_item = get_first_item(my_list)
    

    As I said, the OP was nearly there, and just a few touches give it the Python flavor you're looking for.

    0 讨论(0)
  • 2020-12-07 08:04
    def head(iterable):
        try:
            return iter(iterable).next()
        except StopIteration:
            return None
    
    print head(xrange(42, 1000)  # 42
    print head([])               # None
    

    BTW: I'd rework your general program flow into something like this:

    lists = [
        ["first", "list"],
        ["second", "list"],
        ["third", "list"]
    ]
    
    def do_something(element):
        if not element:
            return
        else:
            # do something
            pass
    
    for li in lists:
        do_something(head(li))
    

    (Avoiding repetition whenever possible)

    0 讨论(0)
  • 2020-12-07 08:04

    Borrowing more_itertools.first_true code yields something decently readable:

    def first_true(iterable, default=None, pred=None):
        return next(filter(pred, iterable), default)
    
    def get_first_non_default(items_list, default=None):
        return first_true(items_list, default, pred=lambda x: x!=default)
    
    0 讨论(0)
  • 2020-12-07 08:06
    for item in get_list():
        return item
    
    0 讨论(0)
  • 2020-12-07 08:06

    Out of curiosity, I ran timings on two of the solutions. The solution which uses a return statement to prematurely end a for loop is slightly more costly on my machine with Python 2.5.1, I suspect this has to do with setting up the iterable.

    import random
    import timeit
    
    def index_first_item(some_list):
        if some_list:
            return some_list[0]
    
    
    def return_first_item(some_list):
        for item in some_list:
            return item
    
    
    empty_lists = []
    for i in range(10000):
        empty_lists.append([])
    
    assert empty_lists[0] is not empty_lists[1]
    
    full_lists = []
    for i in range(10000):
        full_lists.append(list([random.random() for i in range(10)]))
    
    mixed_lists = empty_lists[:50000] + full_lists[:50000]
    random.shuffle(mixed_lists)
    
    if __name__ == '__main__':
        ENV = 'import firstitem'
        test_data = ('empty_lists', 'full_lists', 'mixed_lists')
        funcs = ('index_first_item', 'return_first_item')
        for data in test_data:
            print "%s:" % data
            for func in funcs:
                t = timeit.Timer('firstitem.%s(firstitem.%s)' % (
                    func, data), ENV)
                times = t.repeat()
                avg_time = sum(times) / len(times)
                print "  %s:" % func
                for time in times:
                    print "    %f seconds" % time
                print "    %f seconds avg." % avg_time
    

    These are the timings I got:

    empty_lists:
      index_first_item:
        0.748353 seconds
        0.741086 seconds
        0.741191 seconds
        0.743543 seconds avg.
      return_first_item:
        0.785511 seconds
        0.822178 seconds
        0.782846 seconds
        0.796845 seconds avg.
    full_lists:
      index_first_item:
        0.762618 seconds
        0.788040 seconds
        0.786849 seconds
        0.779169 seconds avg.
      return_first_item:
        0.802735 seconds
        0.878706 seconds
        0.808781 seconds
        0.830074 seconds avg.
    mixed_lists:
      index_first_item:
        0.791129 seconds
        0.743526 seconds
        0.744441 seconds
        0.759699 seconds avg.
      return_first_item:
        0.784801 seconds
        0.785146 seconds
        0.840193 seconds
        0.803380 seconds avg.
    
    0 讨论(0)
  • 2020-12-07 08:07

    Python 2.6+

    next(iter(your_list), None)
    

    If your_list can be None:

    next(iter(your_list or []), None)
    

    Python 2.4

    def get_first(iterable, default=None):
        if iterable:
            for item in iterable:
                return item
        return default
    

    Example:

    x = get_first(get_first_list())
    if x:
        ...
    y = get_first(get_second_list())
    if y:
        ...
    

    Another option is to inline the above function:

    for x in get_first_list() or []:
        # process x
        break # process at most one item
    for y in get_second_list() or []:
        # process y
        break
    

    To avoid break you could write:

    for x in yield_first(get_first_list()):
        x # process x
    for y in yield_first(get_second_list()):
        y # process y
    

    Where:

    def yield_first(iterable):
        for item in iterable or []:
            yield item
            return
    
    0 讨论(0)
提交回复
热议问题