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.

Foi útil?

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.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top