Pregunta

Estoy seguro de que hay una forma más sencilla de hacer esto que simplemente no se me ocurre.

Estoy llamando a un montón de métodos que devuelven una lista.La lista puede estar vacía.Si la lista no está vacía, quiero devolver el primer elemento;de lo contrario, quiero devolver Ninguno.Este código funciona:

my_list = get_list()
if len(my_list) > 0: return my_list[0]
return None

Me parece que debería haber un modismo simple de una línea para hacer esto, pero por mi vida no puedo pensar en eso.¿Está ahí?

Editar:

La razón por la que estoy buscando una expresión de una línea aquí no es porque me guste el código increíblemente conciso, sino porque tengo que escribir mucho código como este:

x = get_first_list()
if x:
    # do something with x[0]
    # inevitably forget the [0] part, and have a bug to fix
y = get_second_list()
if y:
    # do something with y[0]
    # inevitably forget the [0] part AGAIN, and have another bug to fix

Lo que me gustaría hacer ciertamente se puede lograr con una función (y probablemente lo será):

def first_item(list_or_none):
    if list_or_none: return list_or_none[0]

x = first_item(get_first_list())
if x:
    # do something with x
y = first_item(get_second_list())
if y:
    # do something with y

Publiqué la pregunta porque con frecuencia me sorprende lo que pueden hacer las expresiones simples en Python, y pensé que escribir una función era una tontería si había una expresión simple que pudiera funcionar.Pero al ver estas respuestas, parece una función. es la solución sencilla.

¿Fue útil?

Solución

Python 2.6 +

next(iter(your_list), None)

Si your_list puede ser 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

Ejemplo:

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

Otra opción es inline la función anterior:

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

Para evitar break se podría escribir:

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

Donde:

def yield_first(iterable):
    for item in iterable or []:
        yield item
        return

Otros consejos

La mejor forma de hacerlo es la siguiente:

a = get_list()
return a[0] if a else None

También puede hacerlo en una sola línea, pero es mucho más difícil para el programador para leer:

return (get_list()[:1] or [None])[0]
(get_list() or [None])[0]

Eso debería funcionar.

Por cierto yo no usar la variable list, ya que sobrescribe la función incorporada list().

Editar:. Tenía una versión ligeramente más simple, pero mal aquí anteriormente

La forma más pitón idiomática es utilizar la siguiente () en un iterador ya está lista iterables . al igual que lo @ J.F.Sebastian poner en el comentario el 13 dic, 2011.

next(iter(the_list), None) Esto devuelve Ninguno si the_list está vacía. ver next () Python 2.6+

o si conoce a ciencia cierta iter(the_list).next() no está vacío:

<=> ver iterator.next () Python 2.2 +

La solución de la OP es casi allí, hay sólo unas pocas cosas para que sea más Pythonic.

Por un lado, no hay necesidad para obtener la longitud de la lista. listas vacías en Python evalúan como False si en un cheque. Simplemente decir

if list:

Además, es una idea muy mala para asignar a las variables que se superponen con las palabras reservadas. "Lista" es una palabra reservada en Python.

Así que vamos a cambiar a

some_list = get_list()
if some_list:

Un punto muy importante que una gran cantidad de soluciones que aquí se pierda es que todas las funciones de Python / métodos devuelven Ninguno de forma predeterminada . Pruebe lo siguiente a continuación.

def does_nothing():
    pass

foo = does_nothing()
print foo

A menos que necesite volver Ninguno de interrumpir una función temprana, es innecesario volver explícitamente Ninguno. Muy sucintamente, vuelve la primera entrada, en caso de existir.

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

Y, por último, tal vez esto está implícito, pero sólo para ser explícita (porque explícita es mejor que implícita), que no debería tener su función de obtener la lista de otra función; simplemente pasarlo como parámetro. Por lo tanto, el resultado final sería

def get_first_item(some_list): 
    if some_list:
        return list[0]

my_list = get_list()
first_item = get_first_item(my_list)

Como ya he dicho, el PO estaba casi allí, y sólo unos toques que dan el sabor de Python que esté buscando.

Si usted se encuentra tratando de arrancar lo primero (o ninguno) a partir de una lista por comprensión puede cambiar a un generador que hacerlo así:

next((x for x in blah if cond), None)

Pro: funciona si no es bla Con indexable: es la sintaxis desconocida. Es útil, mientras que la piratería y todo lo demás filtrado en ipython sin embargo.

for item in get_list():
    return item

Francamente, no creo que hay una mejor expresión idiomática: su clara y concisa es - no hay necesidad de nada "mejor". Tal vez, pero esto es realmente una cuestión de gusto, puede cambiar if len(list) > 0: con if list: -. Una lista vacía siempre evaluará en False

En una nota relacionada, Python es no Perl (sin juego de palabras!), Que no tiene que obtener el código más fresco posible.
En realidad, la peor que he visto código en Python, también era muy fresco :-) y completamente imposible de mantener.

Por cierto, la mayor parte de la solución que he visto aquí no tienen en cuenta a la hora de lista [0] se evalúa como falsa (por ejemplo, cadena vacía, o cero) - en este caso, todos vuelven Ninguno y no el elemento correcto .

¿Idioma de Python para devolver el primer elemento o ninguno?

El enfoque más pitónico es lo que demostró la respuesta más votada, y fue lo primero que me vino a la mente cuando leí la pregunta.Aquí se explica cómo usarlo, primero si la lista posiblemente vacía se pasa a una función:

def get_first(l): 
    return l[0] if l else None

Y si la lista se devuelve de un get_list función:

l = get_list()
return l[0] if l else None

Otras formas demostradas de hacer esto aquí, con explicaciones.

for

Cuando comencé a pensar en formas inteligentes de hacer esto, esto fue lo segundo que pensé:

for item in get_list():
    return item

Esto supone que la función termina aquí, devolviendo implícitamente None si get_list devuelve una lista vacía.El siguiente código explícito es exactamente equivalente:

for item in get_list():
    return item
return None

if some_list

También se propuso lo siguiente (corregí el nombre de variable incorrecto) que también usa el implícito None.Esto sería preferible a lo anterior, ya que utiliza la verificación lógica en lugar de una iteración que puede no ocurrir.Esto debería ser más fácil de entender inmediatamente lo que está sucediendo.Pero si escribimos para facilitar la lectura y el mantenimiento, también deberíamos agregar el explícito return None al final:

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

rebanada or [None] y seleccione el índice cero

Esta también está en la respuesta más votada:

return (get_list()[:1] or [None])[0]

El segmento es innecesario y crea una lista adicional de un elemento en la memoria.Lo siguiente debería tener más rendimiento.Para explicar, or devuelve el segundo elemento si el primero es False en un contexto booleano, entonces si get_list devuelve una lista vacía, la expresión contenida entre paréntesis devolverá una lista con 'Ninguno', a la que luego accederá el 0 índice:

return (get_list() or [None])[0]

El siguiente usa el hecho de que y devuelve el segundo elemento si el primero es True en un contexto booleano, y dado que hace referencia a my_list dos veces, no es mejor que la expresión ternaria (y técnicamente no es una sola línea):

my_list = get_list() 
return (my_list and my_list[0]) or None

next

Entonces tenemos el siguiente uso inteligente del incorporado next y iter

return next(iter(get_list()), None)

Para explicar, iter devuelve un iterador con un .next método.(.__next__ en Python 3.) Entonces el incorporado next llama eso .next método, y si el iterador se agota, devuelve el valor predeterminado que damos, None.

expresión ternaria redundante (a if b else c) y dando vueltas de regreso

Se propuso lo siguiente, pero sería preferible lo contrario, ya que la lógica suele entenderse mejor en lo positivo que en lo negativo.Desde get_list se llama dos veces, a menos que el resultado se memorice de alguna manera, esto funcionaría mal:

return None if not get_list() else get_list()[0]

La mejor inversa:

return get_list()[0] if get_list() else None

Aún mejor, use una variable local para que get_list solo se llama una vez y primero se analiza la solución Pythonic recomendada:

l = get_list()
return l[0] if l else None

En cuanto a los idiomas, hay un itertools receta llama nth.

De itertools recetas:

def nth(iterable, n, default=None):
    "Returns the nth item or a default value"
    return next(islice(iterable, n, None), default)

Si desea una sola línea, considere la instalación de una biblioteca que implementa esta receta para que, por ejemplo, more_itertools :

import more_itertools as mit

mit.nth([3, 2, 1], 0)
# 3

mit.nth([], 0)                                             # default is `None`
# None

Otra herramienta que está disponible sólo devuelve el primer elemento, llamado more_itertools.first .

mit.first([3, 2, 1])
# 3

mit.first([], default=None)
# None

Estos itertools escala genéricamente para cualquier iterable, no sólo para las listas.

Por curiosidad, me encontré con los tiempos en dos de las soluciones. La solución que utiliza una instrucción de retorno a terminar prematuramente un bucle for es un poco más costoso en mi máquina con Python 2.5.1, Sospecho que esto tiene que ver con la creación de la 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

Estos son los tiempos que tengo:

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.
try:
    return a[0]
except IndexError:
    return None
def head(iterable):
    try:
        return iter(iterable).next()
    except StopIteration:
        return None

print head(xrange(42, 1000)  # 42
print head([])               # None

BTW: Me Rehago su flujo general del programa en algo como esto:

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))

(evitando la repetición siempre que sea posible)

¿Qué tal esto:

(my_list and my_list[0]) or None

Nota: Esto debería funcionar bien para las listas de objetos pero podría volver respuesta incorrecta en el caso de número o lista de cadenas por los comentarios a continuación

.

Se puede usar Extraer método . En otras palabras extraer ese código en un método que desea llamar entonces.

No intentaría comprimirlo mucho más, los trazadores de líneas uno parecen difíciles de leer que la versión prolija. Y si utiliza Extraer método, que es un chiste;)

Usando el truco and-or:

a = get_list()
return a and a[0] or None

Y qué decir de: next(iter(get_list()), None)? Puede que no sea el más rápido aquí, pero es estándar (a partir de Python 2.6) y sucinta.

Probablemente no es la solución más rápida, pero nadie mencionó esta opción:

dict(enumerate(get_list())).get(0)

Si get_list() puede volver None puede usar:

dict(enumerate(get_list() or [])).get(0)

Ventajas:

línea -ona

-usted a llamarlo <=> vez

Fácil de entender

Mi caso de uso sólo para establecer el valor de una variable local.

En lo personal he encontrado el try y except más limpio estilo para leer

items = [10, 20]
try: first_item = items[0]
except IndexError: first_item = None
print first_item

de rebanar una lista.

items = [10, 20]
first_item = (items[:1] or [None, ])[0]
print first_item
if mylist != []:

       print(mylist[0])

   else:

       print(None)

Varias personas han sugerido hacer algo como esto:

list = get_list()
return list and list[0] or None

Esto funciona en muchos casos, pero sólo funcionará si la lista [0] no es igual a 0, o una cadena vacía Falso. Si la lista [0] es 0, falso, o una cadena vacía, el método incorrectamente volver Ninguno.

  
    

He creado este error en mi propio código demasiadas veces!

  

no es el pitón idiomática equivalente a de estilo C operadores ternarios

cond and true_expr or false_expr

ie.

list = get_list()
return list and list[0] or None
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top