todos programación para lista libro lenguaje gran español ejemplos comandos python idioms python-2.4

programación - python 3 pdf



Idioma de Python para devolver el primer elemento o Ninguno (23)

Idioma 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. 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 es devuelta desde una función get_list :

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

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

for

Cuando comencé a pensar en formas inteligentes de hacer esto, esta es la segunda cosa en la 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 utiliza 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 la capacidad de mantenimiento, también debemos agregar el return None explícito return None al final:

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

rebanada or [None] y seleccione índice zeroth

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

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

La división no es necesaria y crea una lista de un elemento adicional en la memoria. Lo siguiente debería ser más performante. Para explicar, or devuelve el segundo elemento si el primero es False en un contexto booleano, por lo tanto, si get_list devuelve una lista vacía, la expresión contenida entre paréntesis devolverá una lista con ''Ninguno'', a la que el índice 0 accederá. :

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 mi lista_y dos veces, no es mejor que la expresión ternaria (y técnicamente no es una frase de una sola línea):

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

next

Luego tenemos el siguiente uso inteligente de la versión incorporada next e iter

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

Para explicar, iter devuelve un iterador con un método .next . ( .__next__ en Python 3.) Luego, la next llamada incorporada a ese método .next , y si el iterador está agotado, devuelve el valor predeterminado que damos, None .

Expresión ternaria redundante ( a if b else c ) y vuelta atrás

Se propuso lo siguiente, pero lo inverso sería preferible, ya que la lógica generalmente se entiende mejor en lo positivo que en lo negativo. Dado que se llama dos veces a get_list , a menos que el resultado se haya memorizado 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 solo se llame a get_list una vez, y primero se ha analizado la solución Pythonic recomendada:

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

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 simple lenguaje de una línea para hacer esto, pero por mi vida no puedo pensar en ello. ¿Esta ahí?

Editar:

La razón por la que busco una expresión de una línea aquí no es porque me guste el código increíblemente conciso, sino porque tengo que escribir muchos códigos 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 sin duda 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 a menudo me sorprende lo que pueden hacer las expresiones simples en Python, y pensé que escribir una función era una tontería si hubiera una expresión simple que pudiera hacer el truco. Pero viendo estas respuestas, parece que una función es la solución simple.


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


¿Y qué hay 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 sucinto.


Hablando francamente, no creo que haya un mejor idioma: es claro y conciso, no hay necesidad de nada "mejor". Tal vez, pero esto es realmente una cuestión de gustos, puede cambiar if len(list) > 0: con if list: - una lista vacía siempre se evaluará como Falso.

En una nota relacionada, Python no es Perl (¡no pretendía hacer un juego de palabras!), No tiene que obtener el código más genial posible.
En realidad, el peor código que he visto en Python, también fue muy bueno :-) y completamente inalcanzable.

Por cierto, la mayoría de las soluciones que he visto aquí no se tienen en cuenta cuando la lista [0] se evalúa como Falso (por ejemplo, una cadena vacía o cero); en este caso, todas devuelven Ninguno y no el elemento correcto.


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

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

o si está seguro de que the_list no está vacío:

iter(the_list).next() vea iter(the_list).next() () Python 2.2+


La mejor manera es esta:

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

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

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


La solución de OP está casi ahí, hay solo 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 comprobación if. Simplemente decir

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 vamos a cambiar eso a

some_list = get_list() if some_list:

Un punto realmente importante que muchas soluciones fallan aquí es que todas las funciones / métodos de Python devuelven Ninguna por defecto . Intente 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, no es necesario devolver explícitamente Ninguno. Bastante sucintamente, solo devuelva la primera entrada, si existiera.

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; simplemente pasarlo 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 allí, y solo unos pocos toques le dan el sabor Python que estás buscando.


Mi caso de uso era solo para establecer el valor de una variable local.

Personalmente encontré el intento y excepto el estilo más limpio para leer.

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

que cortar una lista.

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


Por curiosidad, corrí tiempos en dos de las soluciones. La solución que utiliza una declaración de retorno para finalizar prematuramente un bucle for es un poco más costosa en mi máquina con Python 2.5.1, sospecho que esto tiene que ver con la configuración de 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 horarios 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.


Por el gusto de hacerlo, aquí hay otra posibilidad.

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

Beneficio: este método maneja el caso donde get_list es None, sin usar try / except o la asignación. Que yo sepa, ninguna de las implementaciones anteriores puede manejar esta posibilidad

Downfalls: get_list () se llama dos veces, bastante innecesariamente, especialmente si la lista es larga y / o se crea cuando se llama a la función.

La verdad es que, en mi opinión, es más "Pythonic" proporcionar código que sea legible que hacer una sola frase solo porque puedes :) Tengo que admitir que soy culpable muchas veces de compactar innecesariamente el código Python solo porque Estoy tan impresionado de lo pequeño que puedo hacer que se vea una función compleja :)

Edición: Como el usuario "hasen j" comentó a continuación, la expresión condicional anterior es nueva en Python 2.5, como se describe aquí: https://docs.python.org/whatsnew/2.5.html#pep-308 . Gracias, hasen!


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

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

Si get_list() puede devolver None , puedes usar:

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

Ventajas:

-una línea

get_list() acaba de llamar get_list() una vez

-fácil de entender


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 devolver una respuesta incorrecta en caso de número o lista de cadenas según los comentarios a continuación.


Respecto a las expresiones idiomáticas, hay una receta de itertools llamada nth .

De las recetas de itertools:

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

Si desea more_itertools una sola línea, considere instalar una biblioteca que implemente esta receta para usted, por ejemplo, more_itertools :

import more_itertools as mit mit.nth([3, 2, 1], 0) # 3 mit.nth([], 0) # default is `None` # None

Hay otra herramienta disponible que solo devuelve el primer elemento, llamado more_itertools.first .

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

Estos itertools escalan genéricamente para cualquier iterable, no solo para listas.


Si te encuentras tratando de arrancar lo primero (o ninguno) de una lista de comprensión, puedes cambiar a un generador para hacerlo como:

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

Pro: funciona si bla no es indexable Con: es una sintaxis desconocida. Aunque es útil para piratear y filtrar cosas en ipython.


Usando el y / o truco:

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


Usted podría utilizar el método de extracción . En otras palabras, extraiga ese código en un método que luego llamaría.

No trataría de comprimirlo mucho más, los liners parecen más difíciles de leer que la versión detallada. Y si utiliza el método de extracción, es un trazador de líneas;)


Varias personas han sugerido hacer algo como esto:

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

Eso funciona en muchos casos, pero solo funcionará si la lista [0] no es igual a 0, Falso o una cadena vacía. Si la lista [0] es 0, Falso, o una cadena vacía, el método devolverá incorrectamente Ninguno.

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


no es el pitón idiomático equivalente a los operadores ternarios estilo C

cond and true_expr or false_expr

es decir.

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


def head(iterable): try: return iter(iterable).next() except StopIteration: return None print head(xrange(42, 1000) # 42 print head([]) # None

Por cierto: me gustaría volver a trabajar su flujo de programa general 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 repeticiones siempre que sea posible)


for item in get_list(): return item


if mylist != []: print(mylist[0]) else: print(None)


try: return a[0] except IndexError: return None


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

Eso debería funcionar.

Por cierto, no usé la list variables, porque eso sobrescribe la función de list() integrada list() .

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