Modismo de Python para devolver el primer elemento o Ninguno

10 minutos de lectura

avatar de usuario
Roberto Rossney

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 artículo; de lo contrario, quiero volver None. Este código funciona:

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

pero me parece que debería haber un modismo simple de una línea para hacer esto. ¿Esta ahí?

  • por cierto, en Python 2.6+ podrías usar next(iter(your_list), None) en vez de first_item(your_list) asumiendo your_list no es None (get_first_list() y get_second_list() siempre debe devolver un iterable).

    – jfs

    13 de diciembre de 2011 a las 15:57

  • Creo que quieres decir next(iter(your_list))ya que si proporciona un segundo argumento a iterle está diciendo que el primer argumento es invocable.

    –Robert Rossney

    17 de diciembre de 2011 a las 20:53

  • No, None aquí está el segundo parámetro para next()no iter(). next() devuelve su segundo parámetro si se da en lugar de aumentar StopIteration si your_list esta vacio.

    – jfs

    17 de diciembre de 2011 a las 22:59


  • La solución sugerida por @JFSebastian existe con una pregunta duplicada: stackoverflow.com/a/18533669/144408

    – shahjapan

    24 de junio de 2016 a las 9:35

  • No necesitas disculparte por tu pregunta, es una pregunta que mucha gente tiene. Me gusta el código conciso. Todos lo hacemos. Python es conocido por ello. Es parte de lo que lo hace más legible y más rápido de escribir.

    – NeilG

    22 de febrero de 2021 a las 23:42

avatar de usuario
jfs

Pitón 2.6+

next(iter(your_list), None)

Si your_list puede ser None:

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

Pitón 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 alinear 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 podrías escribir:

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

Dónde:

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

  • Esa última opción es casi exactamente lo que estoy buscando: es clara, funciona y no requiere que defina una nueva función. Diría “exactamente” si el descanso no fuera necesario de alguna manera, porque el riesgo de omitirlo no es insignificante. Pero este enfoque suena a verdad.

    –Robert Rossney

    16 de diciembre de 2008 a las 0:56

  • Oh, esto no me gusta nada. Si algún elemento de la lista se evalúa como Falso, ese valor se descarta y se reemplaza. Si tienes una cadena vacía "" en la lista, que se descarta y se reemplaza por una lista vacía []. Si tiene un 0, también se reemplaza por []. Si usted tiene False allí, también reemplazado. Etc. Puede salirse con la suya en un caso específico, pero este es un mal hábito para desarrollar.

    – steveha

    22 de noviembre de 2009 a las 4:10

  • @steveha: bool(lst) nos dice si len(lst) > 0 no nos dice nada sobre qué artículos lst contiene por ejemplo, bool([False]) == True; por lo tanto la expresión [False] or [] devoluciones [False]lo mismo es para [0] or [] vuelve [0].

    – jfs

    27 de noviembre de 2009 a las 23:43

  • @RobertRossney: He agregado yield_first() para evitar break declaración.

    – jfs

    26 mayo 2012 a las 19:47

  • @ThomasAhle: ambos son ciertos. Actualicé la respuesta para proporcionar la solución para el caso que no es Ninguno.

    – jfs

    23 mayo 2018 a las 11:45

La mejor manera es esta:

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

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

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

  • Una palabra de precaución, si está escribiendo código que necesita funcionar para una lista O una matriz NumPy, entonces este patrón generará un error para la matriz numpy. Prefiere stackoverflow.com/a/365934/5167537 en su lugar: next(iter(your_list), None)

    – Benjamín Brandt

    30 de marzo a las 22:19

avatar de usuario
recursivo

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

Eso debería funcionar.

Por cierto, no usé la variable listporque eso sobrescribe el incorporado list() función.

Editar: tenía una versión un poco más simple, pero incorrecta aquí antes.

  • Esto es inteligente y funciona, pero creo que “return foo[0] if foo else None”, como lo sugiere efotinis, es un poco más fácil de seguir para el mantenimiento.

    – Jay

    12 de diciembre de 2008 a las 20:21

  • La pregunta pedía una solución de una línea, así que la descarté.

    – recursivo

    12 de diciembre de 2008 a las 20:25

  • Ninguna de esas expresiones funciona si get_list() devuelve Ninguno; obtienes “TypeError: el objeto ‘NoneType’ no se puede suscribir”. o “Error de tipo: tipos de operandos no admitidos para +: ‘NoneType’ y ‘list'”.

    –Robert Rossney

    13 de diciembre de 2008 a las 19:46

  • En la pregunta, dice que los métodos devuelven listas, de las cuales None no es una. Entonces, dados los requisitos, sigo creyendo que ambos funcionan.

    – recursivo

    13 de diciembre de 2008 a las 20:30

  • Debería funcionar ahora si get_list() devuelve Ninguno, ya que ya no se suscribe ni se agrega.

    – Roberto

    1 de febrero de 2017 a las 23:51


La forma más idiomática de Python es usar next() en un iterador ya que la lista es iterable. tal como lo puso @JFSebastian en el comentario del 13 de diciembre de 2011.

next(iter(the_list), None) Esto devuelve Ninguno si the_list esta vacio. ver siguiente () Python 2.6+

o si lo sabes seguro the_list no está vacío:

iter(the_list).next() ver iterador.next() Python 2.2+

Si se encuentra tratando de extraer lo primero (o Ninguno) de una lista de comprensión, puede cambiar a un generador para hacerlo como:

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

Pro: funciona si blah no es indexable Con: es una sintaxis desconocida. Sin embargo, es útil mientras piratea y filtra cosas en ipython.

La solución del OP está casi lista, solo hay algunas cosas para hacerla más Pythonic.

Por un lado, no hay necesidad de obtener la longitud de la lista. Las listas vacías en Python se evalúan como Falso en una verificación if. Simplemente di

if list:

Además, es una muy mala idea asignar a variables que se superponen con palabras reservadas. “lista” es una palabra reservada en Python.

Así que cambiemos eso a

some_list = get_list()
if some_list:

Un punto realmente importante que muchas soluciones aquí pasan por alto es que todas las funciones/métodos de Python devuelven Ninguno por defecto. Pruebe lo siguiente a continuación.

def does_nothing():
    pass

foo = does_nothing()
print foo

A menos que necesite devolver Ninguno para finalizar una función antes de tiempo, no es necesario devolver Ninguno explícitamente. De manera bastante sucinta, simplemente devuelva la primera entrada, en caso de que exista.

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

Y finalmente, tal vez esto estaba implícito, pero solo para ser explícito (porque explícito es mejor que implícito), no debería hacer que su función obtenga la lista de otra función; solo pásalo como un parámetro. Entonces, 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 dije, el OP estaba casi listo, y solo unos pocos toques le dan el sabor de Python que estás buscando.

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

El enfoque más Pythonic es lo que demostró la respuesta más votada, y fue lo primero que me vino a la mente cuando leí la pregunta. He aquí 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

Nuevo en Python 3.8, Expresiones de asignación

Las expresiones de asignación utilizan el operador de asignación en el lugar (informalmente llamado operador morsa), :=nuevo en Python 3.8, nos permite hacer la verificación y la asignación en el lugar, lo que permite una sola línea:

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

Como usuario de Python desde hace mucho tiempo, parece que estamos tratando de hacer demasiado en una sola línea; creo que sería mejor estilo hacer lo que supuestamente tiene el mismo rendimiento:

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

En apoyo de esta formulación está Tim Peter’s ensayo en el PEP proponiendo este cambio al lenguaje. No abordó la primera formulación, pero en base a las otras formulaciones que le gustaron, no creo que le importe.

Otras formas demostradas para hacer esto aquí, con explicaciones

for

Cuando comencé a pensar en formas inteligentes de hacer esto, esto es 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 de inmediato lo que está sucediendo. Pero si estamos escribiendo 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]

rodaja or [None] y seleccione índice cero

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

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

El segmento no es necesario y crea una lista adicional de un elemento en la memoria. Lo siguiente debería ser más eficaz. Para explicar, or devuelve el segundo elemento si el primero es False en un contexto booleano, por lo que 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 está agotado, devuelve el valor predeterminado que le damos, None.

expresión ternaria redundante (a if b else c) y dando vueltas hacia atrás

Se propuso lo siguiente, pero sería preferible lo inverso, ya que la lógica suele entenderse mejor en positivo que en negativo. Ya que 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]

El mejor inverso:

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 discutió la solución Pythonic recomendada:

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

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad