Pergunta

Como é que iria criar um processo iterativo de função (ou iterator objeto) em python?

Foi útil?

Solução

O iterador de objetos em python conformidade com o iterador protocol), que basicamente significa que eles fornecem dois métodos: __iter__() e next().O __iter__ retorna o iterador objeto e é chamado implicitamente no início de loops.O next() método retorna o próximo valor e é chamado implicitamente em cada ciclo de incremento. next() levanta um StopIteration exceção quando não há mais valor para voltar, o que é implicitamente capturado por construções de loop para parar a iteração.

Aqui está um exemplo simples de um contador:

class Counter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def next(self): # Python 3: def __next__(self)
        if self.current > self.high:
            raise StopIteration
        else:
            self.current += 1
            return self.current - 1


for c in Counter(3, 8):
    print c

Isto irá imprimir:

3
4
5
6
7
8

Isto é mais fácil de escrever, usando um gerador, conforme abordado em uma resposta anterior:

def counter(low, high):
    current = low
    while current <= high:
        yield current
        current += 1

for c in counter(3, 8):
    print c

A saída impressa vai ser o mesmo.Sob o capô, o objeto de gerador suporta o iterador de protocolo e faz algo mais ou menos semelhante para a classe Contador.

David Mertz do artigo, Os iteradores e Simples Geradores de, é uma boa introdução.

Outras dicas

Existem quatro formas de se construir um processo iterativo de função:

Exemplos:

# generator
def uc_gen(text):
    for char in text:
        yield char.upper()

# generator expression
def uc_genexp(text):
    return (char.upper() for char in text)

# iterator protocol
class uc_iter():
    def __init__(self, text):
        self.text = text
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):
        try:
            result = self.text[self.index].upper()
        except IndexError:
            raise StopIteration
        self.index += 1
        return result

# getitem method
class uc_getitem():
    def __init__(self, text):
        self.text = text
    def __getitem__(self, index):
        result = self.text[index].upper()
        return result

Para ver todos os quatro métodos em ação:

for iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
    for ch in iterator('abcde'):
        print ch,
    print

Que resulta em:

A B C D E
A B C D E
A B C D E
A B C D E

Nota:

Os dois tipos de geradores (uc_gen e uc_genexp) não pode ser reversed();o simples iterador (uc_iter) seria necessário que a __reversed__ método mágico (que deve retornar um novo iterador que vai para trás);e o getitem iteratable (uc_getitem) deve ter o __len__ método mágico:

    # for uc_iter
    def __reversed__(self):
        return reversed(self.text)

    # for uc_getitem
    def __len__(self)
        return len(self.text)

A resposta do Coronel de Pânico secundário pergunta sobre um infinito preguiçosamente avaliado iterador, aqui estão os exemplos, usando cada um dos quatro métodos acima:

# generator
def even_gen():
    result = 0
    while True:
        yield result
        result += 2


# generator expression
def even_genexp():
    return (num for num in even_gen())  # or even_iter or even_getitem
                                        # not much value under these circumstances

# iterator protocol
class even_iter():
    def __init__(self):
        self.value = 0
    def __iter__(self):
        return self
    def __next__(self):
        next_value = self.value
        self.value += 2
        return next_value

# getitem method
class even_getitem():
    def __getitem__(self, index):
        return index * 2

import random
for iterator in even_gen, even_genexp, even_iter, even_getitem:
    limit = random.randint(15, 30)
    count = 0
    for even in iterator():
        print even,
        count += 1
        if count >= limit:
            break
    print

O que resulta em (pelo menos para o meu exemplo de execução):

0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38 40 42 44 46 48 50 52 54
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32 34 36 38
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30
0 2 4 6 8 10 12 14 16 18 20 22 24 26 28 30 32

O primeiro de todos os módulo itertools é incrivelmente útil para todos os tipos de casos em que um iterador que poderia ser útil, mas aqui está tudo que você precisa para criar um iterador em python:

rendimento

Não é legal?Rendimento pode ser usado para substituir um normal retorno em uma função.Ele retorna o objeto da mesma, mas em vez de destruir o estado e de sair, ele guarda do estado, para quando você deseja executar a próxima iteração.Aqui está um exemplo de que em ação obtida diretamente a partir da itertools lista de funções:

def count(n=0):
    while True:
        yield n
        n += 1

Como dito na descrição de funcionalidades (é o count() a função do módulo itertools...) , produz um iterador que retorna inteiros consecutivos, começando com n.

Gerador de expressões são uma outra lata de vermes (impressionante vermes!).Eles podem ser usados no lugar de um Lista De Compreensão para economizar memória (lista de compreensões criar uma lista na memória que é destruído após o uso, se não for atribuído a uma variável, mas o gerador de expressões pode criar um Objeto de Gerador de...que é uma maneira elegante de dizer o Iterator).Aqui está um exemplo de um gerador de definição de expressão:

gen = (n for n in xrange(0,11))

Este é muito semelhante ao nosso iterador definição acima, exceto a linha completa é predeterminado a ser entre 0 e 10.

Eu só encontrei xrange() (surpreso que eu não tinha visto isso antes...) e adicionado para o exemplo acima. xrange() é uma versão do iterable faixa (a) o que tem a vantagem de não pré-criando a lista.Seria muito útil se você tivesse um gigante corpus de dados para iterar sobre e só tinha tanta memória para fazê-lo em.

Eu vejo que alguns de vocês fazendo return self no __iter__.Eu só queria observar que __iter__ em si pode ser um gerador (eliminando assim a necessidade de __next__ e levantando StopIteration excepções)

class range:
  def __init__(self,a,b):
    self.a = a
    self.b = b
  def __iter__(self):
    i = self.a
    while i < self.b:
      yield i
      i+=1

É claro que aqui pode-se também fazer diretamente um gerador, mas para classes mais complexa que possa ser útil.

Esta pergunta é sobre iterable objetos, e não sobre os iteradores.Em Python, as seqüências são iterable também, então uma maneira de fazer uma iterable classe faze-lo se comportar como uma seqüência, isto é,dê a ele __getitem__ e __len__ métodos.Eu testei isso em Python 2 e 3.

class CustomRange:

    def __init__(self, low, high):
        self.low = low
        self.high = high

    def __getitem__(self, item):
        if item >= len(self):
            raise IndexError("CustomRange index out of range")
        return self.low + item

    def __len__(self):
        return self.high - self.low


cr = CustomRange(0, 10)
for i in cr:
    print(i)

Este é um iterable função, sem yield.Fazer uso do iter função e um fecho que mantém o estado mutável (list) no âmbito envolvente para python 2.

def count(low, high):
    counter = [0]
    def tmp():
        val = low + counter[0]
        if val < high:
            counter[0] += 1
            return val
        return None
    return iter(tmp, None)

Para Python 3, fechamento de estado é mantido em uma imutável no âmbito envolvente e nonlocal é utilizado em âmbito local para atualizar a variável de estado.

def count(low, high):
    counter = 0
    def tmp():
        nonlocal counter
        val = low + counter
        if val < high:
            counter += 1
            return val
        return None
    return iter(tmp, None)  

O teste;

for i in count(1,10):
    print(i)
1
2
3
4
5
6
7
8
9

Todas as respostas nesta página são realmente grandes para um objeto complexo.Mas para aqueles que contenham builtin iterable tipos de atributos, como str, list, set ou dict, ou qualquer implementação de collections.Iterable, você pode omitir certas coisas em sua classe.

class Test(object):
    def __init__(self, string):
        self.string = string

    def __iter__(self):
        # since your string is already iterable
        return (ch for ch in string)

Ele pode ser usado como:

for x in Test("abcde"):
    print(x)

# prints
# a
# b
# c
# d
# e

Se você procura algo simples e curto, talvez ele vai ser o suficiente para você:

class A(object):
    def __init__(self, l):
        self.data = l

    def __iter__(self):
        return iter(self.data)

exemplo de uso:

In [3]: a = A([2,3,4])

In [4]: [i for i in a]
Out[4]: [2, 3, 4]

Inspirado por Matt Gregory resposta aqui é um pouco mais complicado do iterador que retornará a,b,...,z,aa,ab,...,zz,aaa,aab,...,zzy,zzz

    class AlphaCounter:
    def __init__(self, low, high):
        self.current = low
        self.high = high

    def __iter__(self):
        return self

    def __next__(self): # Python 3: def __next__(self)
        alpha = ' abcdefghijklmnopqrstuvwxyz'
        n_current = sum([(alpha.find(self.current[x])* 26**(len(self.current)-x-1)) for x in range(len(self.current))])
        n_high = sum([(alpha.find(self.high[x])* 26**(len(self.high)-x-1)) for x in range(len(self.high))])
        if n_current > n_high:
            raise StopIteration
        else:
            increment = True
            ret = ''
            for x in self.current[::-1]:
                if 'z' == x:
                    if increment:
                        ret += 'a'
                    else:
                        ret += 'z'
                else:
                    if increment:
                        ret += alpha[alpha.find(x)+1]
                        increment = False
                    else:
                        ret += x
            if increment:
                ret += 'a'
            tmp = self.current
            self.current = ret[::-1]
            return tmp

for c in AlphaCounter('a', 'zzz'):
    print(c)
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top