Pergunta

Eu tenho certeza que há uma maneira mais simples de fazer isso que simplesmente não está ocorrendo para mim.

Eu estou chamando um monte de métodos que retornam uma lista. A lista pode estar vazio. Se a lista não é vazio, eu quero voltar o primeiro item; Caso contrário, eu quero voltar Nenhum. Esse código funciona:

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

Parece-me que deve haver uma simples expressão idiomática-line um para fazer isso, mas para a vida de mim eu não posso pensar nisso. Existe?

Editar:

A razão que eu estou procurando uma expressão de uma linha aqui não é que eu gosto de código extremamente concisa, mas porque eu estou tendo que escrever um monte de 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

O que eu gostaria de estar fazendo certamente pode ser feito com uma função (e provavelmente 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

Eu postei a pergunta porque estou frequentemente surpreendido por aquilo que expressões simples em Python pode fazer, e eu pensei que escrever uma função era uma coisa boba que fazer se havia uma expressão simples poderia fazer o truque. Mas vendo estas respostas, parece que uma função é a solução simples.

Foi útil?

Solução

Python 2.6 +

next(iter(your_list), None)

Se your_list pode 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

Exemplo:

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

Outra opção é para inline a função acima:

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 break evitar que você poderia escrever:

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

Onde:

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

Outras dicas

A melhor maneira é esta:

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

Você também pode fazê-lo em uma linha, mas é muito mais difícil para o programador para ler:

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

Isso deve funcionar.

BTW eu não usar o list variável, porque isso substitui a função interna list().

Edit: eu tinha um pouco mais simples, mas a versão errada aqui anteriormente

.

A forma idiomática mais python é usar a próxima () em um iterador uma vez lista é iterable . assim como o que @ J.F.Sebastian colocar no comentário em 13 de dezembro de 2011.

next(iter(the_list), None) Este retornos Nenhum se the_list está vazio. consulte next () Python 2.6+

ou se você sabe com certeza the_list não está vazio:

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

A solução do OP é quase lá, há apenas algumas coisas para torná-lo mais Pythonic.

Por um lado, não há necessidade de obter o comprimento da lista. listas vazias em Python avaliar como False em um caso de verificação. Simplesmente dizer

if list:

Além disso, é uma idéia muito ruim para atribuir a variáveis ??que se sobrepõem com palavras reservadas. "Lista" é uma palavra reservada no Python.

Então, vamos mudar isso para

some_list = get_list()
if some_list:

Um ponto muito importante que um monte de soluções aqui miss é que todas as funções do Python / métodos retornam Nenhum por padrão . Tente o seguinte abaixo.

def does_nothing():
    pass

foo = does_nothing()
print foo

A menos que você precisa de retorno Nenhum de encerrar uma função cedo, é desnecessário para retornar explicitamente Nenhum. De forma muito sucinta, basta retornar a primeira entrada, caso exista.

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

E, finalmente, talvez isso estava implícito, mas apenas para ser explícita (porque é explícito melhor do que implícita), você não deve ter sua função de obter a lista de outra função; apenas passá-lo como um parâmetro. Assim, o resultado final seria

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

my_list = get_list()
first_item = get_first_item(my_list)

Como eu disse, o OP estava quase lá, e apenas alguns toques dar-lhe o sabor Python que você está procurando.

Se você está tentando arrancar a primeira coisa (ou nenhum) a partir de uma compreensão da lista você pode alternar para um gerador de fazê-lo como:

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

Pro: funciona se blah não é indexável Con: é sintaxe desconhecida. É útil ao cortar ao redor e filtrar o material em ipython embora.

for item in get_list():
    return item

Falando francamente, eu não acho que há uma expressão melhor: o seu é clara e concisa - não há necessidade para qualquer coisa "melhor". Talvez, mas isso é realmente uma questão de gosto, você pode mudar if len(list) > 0: com if list: -. Uma lista vazia será sempre avaliada como false

Em uma nota relacionada, Python é não Perl (sem trocadilhos!), Você não tem que obter o código mais legal possível.
Na verdade, o pior código que eu já vi em Python, também foi muito legal :-) e completamente insustentável.

A propósito, a maioria da solução que tenho visto aqui não levam em consideração quando a lista [0] avalia a falsa (por exemplo cadeia vazia, ou zero) - neste caso, todos eles retornam Nenhum e não o elemento correta .

Python idioma para retornar primeiro item ou Nenhum?

A abordagem mais Pythonic é o que a resposta mais upvoted demonstrado, e foi a primeira coisa a vir à minha mente quando li a questão. Veja como usá-lo, em primeiro lugar, se a lista possivelmente vazia é passado para uma função:

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

E se a lista é retornado de uma função get_list:

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

Outras formas demonstrado para fazer isso aqui, com explicações

for

Quando comecei a tentar pensar em maneiras inteligentes de fazer isso, esta é a segunda coisa que pensei:

for item in get_list():
    return item

Isso pressupõe as extremidades de função aqui, retornando implicitamente None se get_list retorna uma lista vazia. O código abaixo explícita é exatamente equivalente:

for item in get_list():
    return item
return None

if some_list

O seguinte também foi proposto (I corrigido o nome da variável incorreta) que também usa o None implícita. Este seria preferível ao anterior, já que usa a verificação lógica em vez de uma iteração que não pode acontecer. Este deve ser mais fácil de entender imediatamente o que está acontecendo. Mas se estamos escrevendo para facilitar a leitura e manutenção, devemos acrescentar também o return None explícita no final:

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

or [None] fatia e selecionar zeroth índice

Esta também é a resposta mais up-votado:

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

A fatia é desnecessária e cria uma lista de um artigo extra na memória. O seguinte deve ser maior performance. Para explicar, or retorna o segundo elemento se o primeiro é False num contexto booleano, por isso, se get_list retorna uma lista vazia, a expressão contido nos parênteses vai devolver uma lista com 'Nenhum', que irá então ser acedida pelo índice 0 :

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

O próximo usa o fato de que e retorna o segundo item se o primeiro é True em um contexto booleano, e uma vez que ele faz referência my_list duas vezes, não é melhor (não e tecnicamente um one-liner) é do que a expressão ternária:

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

next

Em seguida, temos o seguinte uso inteligente do builtin next e iter

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

Para explicar, iter retorna um iterador com um método .next. (.__next__ em Python 3.), então as chamadas embutido next esse método .next, e se o iterador está esgotado, retorna o padrão que nós damos, None.

redundante ternário expressão (a if b else c) e circulando de volta

A abaixo foi proposta, mas o inverso seria preferível, como lógica é geralmente melhor compreendida na positiva em vez de negativa. Desde get_list é chamado duas vezes, a menos que o resultado é memoized de alguma forma, isso iria executar mal:

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

A melhor inversa:

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

Mesmo melhor, use uma variável local para que get_list é chamado apenas uma vez, e você tem a solução Pythonic recomendado discutida pela primeira vez:

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

Em relação idiomas, há um itertools receita chamada nth.

De itertools receitas:

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

Se você quiser one-liners, considerar a instalação de uma biblioteca que implementa esta receita para você, por exemplo, more_itertools :

import more_itertools as mit

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

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

Outra ferramenta está disponível que retorna apenas o primeiro item, chamado more_itertools.first .

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

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

Estas itertools escala genericamente para qualquer iterable, não só para as listas.

Por curiosidade, eu corri horários em duas das soluções. A solução que utiliza uma instrução de retorno para terminar prematuramente um loop é um pouco mais caro na minha máquina com Python 2.5.1, Eu suspeito que isso tem a ver com a criação do 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

Estes são os tempos que eu tenho:

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: eu retrabalho seu fluxo geral do programa em algo parecido com isto:

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 repetição sempre que possível)

Como sobre isto:

(my_list and my_list[0]) or None

Nota:. Isso deve funcionar bem para listas de objetos, mas pode retornar resposta incorreta em caso de número ou lista de strings per os comentários abaixo

Você pode usar Extract Method . Em outras palavras extrair esse código em um método que você iria em seguida, chamar.

Eu não iria tentar comprimi-lo muito mais, os forros um parecer mais difícil de ler do que a versão detalhada. E se você usar Extrair Método, é um um forro;)

Usando o e-ou truque:

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

E o que dizer: next(iter(get_list()), None)? Pode não ser o mais rápido aqui, mas é padrão (a partir de Python 2.6) e sucinto.

Provavelmente não é a solução mais rápida, mas ninguém mencionou essa opção:

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

Se get_list() pode retornar None você pode usar:

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

Vantagens:

linha -um

-você get_list() apenas chamada uma vez

Fácil de entender

Meu caso de uso foi apenas para definir o valor de uma variável local.

Pessoalmente, achei o try e except limpador de estilo para ler

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

que cortar uma lista.

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

       print(mylist[0])

   else:

       print(None)

Várias pessoas sugeriram fazer algo como isto:

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

que funciona em muitos casos, mas ele só vai funcionar se a lista [0] não é igual a 0, False, ou uma string vazia. Se a lista [0] é 0, False, ou uma cadeia vazia, o método retornará incorretamente Nenhum.

Eu criei este bug no meu próprio um código muitas vezes!

não é o python idiomática equivalente a operadores de estilo C ternários

cond and true_expr or false_expr

ie.

list = get_list()
return list and list[0] or None
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top