Bygge en Basic Python Iterator

stemmer
457

Hvordan ville en skape en iterativ funksjon (eller iterator objekt) i python?

Publisert på 21/08/2008 klokken 00:36
kilden bruker
På andre språk...                            


9 svar

stemmer
537

Iteratorknapper objekter i python samsvar med iteratoren protokollen, som i utgangspunktet betyr at de gir to metoder: __iter__() og next(). De __iter__returnerer iterator objekt og implisitt kalt ved starten av sløyfer. Den next()metoden returnerer den neste verdien og er implisitt heter ved hver sløyfe inkrement. next()reiser en StopIteration unntak når det ikke er mer verdi for å gå tilbake, som er implisitt fanget av looping konstruksjoner for å stoppe itera.

Her er et enkelt eksempel på en teller:

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

Dette vil skrive ut:

3
4
5
6
7
8

Dette er lettere å skrive ved hjelp av en generator, som er omtalt i et tidligere svar:

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

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

Utskriften vil være den samme. Under panseret, støtter generatoren objektet iterator protokollen og gjør noe omtrent lik klassen Counter.

David Mertz artikkel, iteratorer og Enkle Generatorer , er en ganske god introduksjon.

Svarte 23/08/2008 kl. 16:57
kilden bruker

stemmer
335

Det er fire måter å bygge en iterativ funksjon:

eksempler:

# 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

For å se alle fire metodene i aksjon:

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

Noe som resulterer i:

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

Merk :

De to generatorer typer ( uc_genog uc_genexp) kan ikke være reversed(); sletten iterator ( uc_iter) ville trenge __reversed__magiske metoden (som må returnere en ny iterator som går bakover); og getitem iteratable ( uc_getitem) må ha __len__magiske metoden:

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

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

For å svare Colonel panikk sekundære spørsmål om en uendelig dovent evaluert iteratoren, her er de eksempler, under anvendelse av hver av de fire ovennevnte metoder:

# 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

Noe som resulterer i (i hvert fall for min prøve run):

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
Svarte 24/09/2011 kl. 22:13
kilden bruker

stemmer
98

Først av alt itertools modulen er utrolig nyttig for alle slags tilfeller der en iterator ville være nyttig, men her er alt du trenger for å lage en iterator i python:

utbytte

Er ikke det kult? Utbytte kan brukes til å erstatte en normal retur i en funksjon. Den returnerer objektet akkurat det samme, men i stedet for å ødelegge staten og spennende, det sparer staten for når du ønsker å utføre neste iterasjon. Her er et eksempel på den i aksjon trukket direkte fra itertools funksjonslisten :

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

Som det fremgår av beskrivelsen funksjoner (det er den telle () funksjon fra itertools modul ...), produserer den en iteratoren som returnerer fortløpende heltall fra og med n.

Generator uttrykk er en helt annen boks med ormer (fantastisk ormer!). De kan brukes i stedet for en liste Forståelsen for å spare minne (liste oppfattelser lage en liste i minnet som er ødelagt etter bruk dersom de ikke er tildelt en variabel, men generator uttrykk kan opprette en Generator Object ... som er en fancy måte sier Iterator). Her er et eksempel på en generator uttrykk definisjon:

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

Dette er svært lik våre iteratoren definisjonen ovenfor, bortsett fra hele spekteret er forhåndsbestemt til å være mellom 0 og 10.

Jeg har nettopp funnet xrange () (overrasket over at jeg ikke hadde sett det før ...) og lagt den til eksempelet ovenfor. xrange () er en iterable versjon av området () som har fordelen av ikke prebuilding listen. Det ville være svært nyttig hvis du hadde en gigantisk corpus av data for å iterere over og hadde bare så mye minne til å gjøre det i.

Svarte 21/08/2008 kl. 00:36
kilden bruker

stemmer
84

Jeg ser noen av det du gjør return selfi __iter__. Jeg ville bare å merke seg at __iter__i seg selv kan være en generator (og dermed fjerne behovet for __next__og heve StopIterationunntak)

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

Selvfølgelig her en kan like godt direkte lage en generator, men for mer komplekse klasser det kan være nyttig.

Svarte 27/07/2012 kl. 15:05
kilden bruker

stemmer
8

Dette spørsmålet er om iterable objekter, ikke om iteratorer. I Python, sekvenser er iterable også så en måte å gjøre en iterable klassen er å gjøre det oppføre seg som en sekvens, det vil si gi den __getitem__og __len__metoder. Jeg har testet dette på Python 2 og 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)
Svarte 21/03/2016 kl. 17:39
kilden bruker

stemmer
3

Dette er en iterable funksjon uten yield. Den gjør bruk av en iterfunksjon og en lukkeanordning som holder den tilstand i en foranderlig ( list) i den omsluttende rammen for 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)

For Python 3 er lukke tilstand holdes i en uforanderlig i den omsluttende omfang og nonlocalbrukes i lokal omfang for å oppdatere tilstandsvariabel.

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)  

Test;

for i in count(1,10):
    print(i)
1
2
3
4
5
6
7
8
9
Svarte 03/03/2016 kl. 17:55
kilden bruker

stemmer
1

Hvis du leter etter noe kort og enkel, kanskje det vil være nok for deg:

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

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

eksempel på bruk:

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

In [4]: [i for i in a]
Out[4]: [2, 3, 4]
Svarte 26/04/2018 kl. 08:38
kilden bruker

stemmer
0

Alle svar på denne siden er virkelig flott for et komplekst objekt. Men for de som inneholder builtin iteratorknapper typer som attributter, som str, list, seteller dict, eller gjennomføring av collections.Iterable, du kan utelate visse ting i klassen.

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)

Den kan brukes som:

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

# prints
# a
# b
# c
# d
# e
Svarte 14/08/2018 kl. 08:25
kilden bruker

stemmer
0

Inspirert av Matt Gregory svar her er litt mer komplisert iterator som vil returnere 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)
Svarte 13/07/2018 kl. 17:34
kilden bruker

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more