Por que não há primeiro (iterable) built-in função em Python?
Pergunta
Eu estou querendo saber se há uma razão que não há first(iterable)
no Python funções internas, algo semelhante ao any(iterable)
e all(iterable)
(pode ser escondido em um algum lugar módulo stdlib, mas eu não vê-lo em itertools
) . first
iria realizar uma avaliação gerador de curto-circuito de modo a que as operações desnecessárias (e um número potencialmente infinito de) pode ser evitada; i.
def identity(item):
return item
def first(iterable, predicate=identity):
for item in iterable:
if predicate(item):
return item
raise ValueError('No satisfactory value found')
Desta forma, você pode expressar coisas como:
denominators = (2, 3, 4, 5)
lcd = first(i for i in itertools.count(1)
if all(i % denominators == 0 for denominator in denominators))
É claro que você não pode fazer list(generator)[0]
nesse caso, já que o gerador não termina.
Ou se você tem um monte de expressões regulares para o jogo contra (útil quando todos eles têm a mesma interface groupdict
):
match = first(regex.match(big_text) for regex in regexes)
Você salvar um monte de processamento desnecessário, evitando list(generator)[0]
e curto-circuito em um resultado positivo.
Solução
Se você tem um iterador, você pode simplesmente chamar seu método next
. Algo como:
In [3]: (5*x for x in xrange(2,4)).next()
Out[3]: 10
Outras dicas
Há um PyPI pacote chamado “primeiro” que faz isso:
>>> from first import first
>>> first([0, None, False, [], (), 42])
42
Veja como você usaria para retornar o primeiro número ímpar, por exemplo:
>> first([2, 14, 7, 41, 53], key=lambda x: x % 2 == 1)
7
Se você quiser apenas para retornar o primeiro elemento do iterador independentemente de se é verdade ou não, fazer isso:
>>> first([0, None, False, [], (), 42], key=lambda x: True)
0
É um pacote muito pequeno: só contém esta função, não tem dependências, e funciona em Python 2 e 3. É um único arquivo, para que você não tem sequer para instalá-lo para usá-lo.
Na verdade, aqui é quase todo o código fonte (a partir da versão 2.0.1, por Hynek Schlawack, lançado sob a licença MIT):
def first(iterable, default=None, key=None):
if key is None:
for el in iterable:
if el:
return el
else:
for el in iterable:
if key(el):
return el
return default
Eu pedi um semelhante pergunta recentemente (ele ficou marcado como uma duplicata desta questão por agora). Minha preocupação também foi que eu gostava de usar built-ins única para resolver o problema de encontrar o primeiro verdadeiro valor de um gerador. Minha própria solução, em seguida, foi o seguinte:
x = next((v for v in (f(x) for x in a) if v), False)
Para o exemplo de encontrar o jogo da primeira regexp esta ficaria assim (não é o primeiro padrão correspondente!):
patterns = [ r'\d+', r'\s+', r'\w+', r'.*' ]
text = 'abc'
firstMatch = next(
(match for match in
(re.match(pattern, text) for pattern in patterns)
if match),
False)
Ele não avalia o predicado duas vezes (como você teria que fazer se apenas o padrão foi devolvido) e não usar hacks como os moradores locais em compreensões.
Mas tem dois geradores aninhados onde a lógica ditava para usar apenas um. Portanto, a melhor solução seria bom.
Há uma "fatia" iterator em itertools. Ele emula as operações fatia que estamos familiarizados com em python. O que você está procurando algo semelhante a isto:
myList = [0,1,2,3,4,5]
firstValue = myList[:1]
O equivalente usando itertools para iterators:
from itertools import islice
def MyGenFunc():
for i in range(5):
yield i
mygen = MyGenFunc()
firstValue = islice(mygen, 0, 1)
print firstValue
Há alguma ambiguidade na sua pergunta. Sua definição de início eo exemplo regex implica que há um teste booleano. Mas o exemplo denominadores tem explicitamente uma cláusula if; por isso é apenas uma coincidência que cada inteiro passa a ser verdade.
Parece que a combinação de lado e itertools.ifilter lhe dará o que você quer.
match = next(itertools.ifilter(None, (regex.match(big_text) for regex in regexes)))
Haskell faz uso do que você acabou de descrever, como o take
função (ou como o take 1
função parcial, tecnicamente). Python livro de receitas tem gerador de invólucros escritas que executam a mesma funcionalidade como take
, takeWhile
e drop
em Haskell.
Mas por que motivo isso não é um built-in, seu palpite é tão bom quanto o meu.