Φτιάξτε μια βασική Python Iterator

ψήφοι
442

Πώς το ένα θα δημιουργήσει μια επαναληπτική συνάρτηση (ή αντικείμενο iterator) σε πύθωνα;

Δημοσιεύθηκε 20/08/2008 στις 23:36
πηγή χρήστη
Σε άλλες γλώσσες...                            


9 απαντήσεις

ψήφοι
97

Πρώτα απ 'όλα την ενότητα itertools είναι εξαιρετικά χρήσιμη για όλα τα είδη των περιπτώσεων στις οποίες ένας iterator θα ήταν χρήσιμη, αλλά εδώ είναι όλα όσα χρειάζεστε για να δημιουργήσετε ένα iterator σε python:

απόδοση παραγωγής

Δεν είναι αυτό το δροσερό; Απόδοση μπορεί να χρησιμοποιηθεί για να αντικαταστήσει μια κανονική απόδοση σε μια λειτουργία. Επιστρέφει το αντικείμενο ακριβώς το ίδιο, αλλά αντί για την καταστροφή κράτους και την έξοδο, εξοικονομεί κατάσταση για όταν θέλετε να εκτελέσετε την επόμενη επανάληψη. Εδώ είναι ένα παράδειγμα αυτού στη δράση τράβηξε απευθείας από τη λίστα itertools λειτουργία :

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

Όπως αναφέρεται στην περιγραφή λειτουργιών (αυτό είναι το count () λειτουργία από τη μονάδα itertools ...), παράγει ένα επαναλήπτη που επιστρέφει διαδοχικών ακεραίων ξεκινώντας με Ν.

Εκφράσεις Generator είναι ένα εντελώς διαφορετικό κουτί της Πανδώρας (φοβερό σκουλήκια!). Μπορούν να χρησιμοποιηθούν στη θέση ενός Λίστα Κατανόησης για να σώσει τη μνήμη (κατάλογος Η κατανόηση δημιουργήσετε μια λίστα στη μνήμη που καταστρέφεται μετά τη χρήση, εάν δεν έχουν ανατεθεί σε μια μεταβλητή, αλλά εκφράσεις γεννήτρια μπορεί να δημιουργήσει ένα αντικείμενο Generator ... το οποίο είναι ένα φανταχτερό τρόπο λέγοντας Iterator). Εδώ είναι ένα παράδειγμα ενός ορισμού έκφρασης γεννήτρια:

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

Αυτό είναι πολύ παρόμοιο με τον ορισμό iterator μας ανωτέρω εκτός το πλήρες φάσμα είναι προκαθορισμένη ώστε να είναι μεταξύ 0 και 10.

Βρήκα μόνο xrange () (έκπληκτος δεν είχα ξαναδεί ...) και πρόσθεσε ότι με το παραπάνω παράδειγμα. xrange () είναι μια iterable έκδοση της σειράς (), η οποία έχει το πλεονέκτημα ότι δεν prebuilding τη λίστα. Θα ήταν πολύ χρήσιμο αν είχε ένα τεράστιο σώμα της στοιχεία για να επαναλάβει ξανά και είχε μόνο τόσο πολύ μνήμη για να το κάνουμε.

Απαντήθηκε 20/08/2008 στις 23:36
πηγή χρήστη

ψήφοι
517

Αντικείμενα Iterator στην Python είναι σύμφωνες με το πρωτόκολλο iterator, η οποία βασικά σημαίνει ότι παρέχουν δύο μεθόδους: __iter__() και next(). Οι __iter__επιστρέφει το αντικείμενο iterator και εμμέσως ζήτησε κατά την έναρξη των βρόχων. Η next()μέθοδος επιστρέφει την επόμενη τιμή και εμμέσως ονομάζεται σε κάθε αύξηση βρόχο. next()δημιουργεί μια εξαίρεση StopIteration όταν δεν υπάρχουν πλέον αξία να επιστρέψουν, η οποία εμμέσως συλλαμβάνεται από looping δομές για να σταματήσει την επανάληψη.

Εδώ είναι ένα απλό παράδειγμα ενός μετρητή:

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

Αυτό θα εκτυπώσει:

3
4
5
6
7
8

Αυτό είναι πιο εύκολο να γράψει χρησιμοποιώντας μια γεννήτρια, όπως καλύπτονται σε προηγούμενη απάντηση:

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

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

Το εκτυπωμένο αποτέλεσμα θα είναι το ίδιο. Κάτω από το καπό, το αντικείμενο γεννήτρια υποστηρίζει το πρωτόκολλο iterator και κάνει κάτι περίπου παρόμοιο με το Counter τάξη.

Άρθρο David Mertz, η Iterators και απλό Γεννήτριες , είναι μια πολύ καλή εισαγωγή.

Απαντήθηκε 23/08/2008 στις 15:57
πηγή χρήστη

ψήφοι
324

Υπάρχουν τέσσερις τρόποι για να οικοδομήσουμε μια επαναληπτική συνάρτηση:

  • δημιουργήσετε μια γεννήτρια (χρησιμοποιεί τη λέξη-κλειδί απόδοση )
  • χρησιμοποιούν μια έκφραση γεννήτρια ( genexp )
  • δημιουργήσετε ένα iterator (καθορίζει __iter__και__next__nextσε Python 2.x))
  • δημιουργήσετε μια λειτουργία που Python μπορεί να επαναλάβει πάνω σε δική του (της ορίζει__getitem__ )

Παραδείγματα:

# 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 iterator in uc_gen, uc_genexp, uc_iter, uc_getitem:
    for ch in iterator('abcde'):
        print ch,
    print

Που έχει ως αποτέλεσμα:

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

Σημείωση :

Οι δύο τύποι της γεννήτριας ( uc_genκαι uc_genexp) δεν μπορεί να είναι reversed()? η πεδιάδα iterator ( uc_iter) θα πρέπει το __reversed__μαγικό μέθοδο (η οποία πρέπει να επιστρέψει ένα νέο iterator που πηγαίνει προς τα πίσω)? και η GetItem iteratable ( uc_getitem) πρέπει να έχει τη __len__μαγική μέθοδο:

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

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

Για να απαντήσουμε σε δευτερεύουσα ερώτηση συνταγματάρχη πανικός σχετικά με μια άπειρη νωχελικά αξιολογηθεί iterator, εδώ είναι εκείνα τα παραδείγματα, χρησιμοποιώντας κάθε μία από τις τέσσερις παραπάνω μεθόδους:

# 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

Που έχει ως αποτέλεσμα (τουλάχιστον για την εκτέλεση δείγμα μου):

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
Απαντήθηκε 24/09/2011 στις 21:13
πηγή χρήστη

ψήφοι
81

Βλέπω κάποιοι από εσάς να κάνει return selfτο __iter__. Ήθελα απλώς να σημειωθεί ότι __iter__η ίδια μπορεί να είναι μια γεννήτρια (αφαιρώντας έτσι την ανάγκη για __next__και την αύξηση StopIterationεξαιρέσεις)

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

Φυσικά εδώ θα μπορούσε κανείς να καθώς κάνει άμεσα μια γεννήτρια, αλλά και για πιο σύνθετες τάξεις μπορεί να είναι χρήσιμο.

Απαντήθηκε 27/07/2012 στις 14:05
πηγή χρήστη

ψήφοι
3

Αυτό είναι ένα iterable λειτουργία χωρίς yield. Θα κάνει χρήση της iterλειτουργίας και κλεισίματος που κρατά το κράτος σε ένα μεταβλητό ( list) στο περικλείει δυνατότητες 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)

Για Python 3, κατάσταση κλεισίματος διατηρείται σε ένα αμετάβλητο στο εγκλεισμού πεδίο εφαρμογής και nonlocalχρησιμοποιείται σε τοπική εμβέλεια για την ενημέρωση του μεταβλητή κατάστασης.

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)  

Δοκιμή;

for i in count(1,10):
    print(i)
1
2
3
4
5
6
7
8
9
Απαντήθηκε 03/03/2016 στις 15:55
πηγή χρήστη

ψήφοι
7

Αυτή η ερώτηση είναι για iterable αντικείμενα, όχι για επαναλήπτες. Στην Python, οι αλληλουχίες είναι iterable υπερβολικά έτσι ένας τρόπος για να κάνει μια iterable τάξη είναι να καταστεί συμπεριφέρεται σαν μια αλληλουχία, δηλαδή να προσφέρει αυτό __getitem__και __len__μεθόδους. Δοκίμασα αυτό σε Python 2 και 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)
Απαντήθηκε 21/03/2016 στις 15:39
πηγή χρήστη

ψήφοι
1

Αν ψάχνετε για κάτι σύντομο και απλό, ίσως θα είναι αρκετό για εσάς:

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

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

παράδειγμα χρήσης:

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

In [4]: [i for i in a]
Out[4]: [2, 3, 4]
Απαντήθηκε 26/04/2018 στις 07:38
πηγή χρήστη

ψήφοι
0

Εμπνευσμένο από την απάντηση Matt Gregory εδώ είναι λίγο πιο περίπλοκη iterator που θα επιστρέψει, β, ..., z, αα, αβ, ..., zz, ααα, 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)
Απαντήθηκε 13/07/2018 στις 16:34
πηγή χρήστη

ψήφοι
0

Όλες οι απαντήσεις σε αυτή τη σελίδα είναι πολύ μεγάλη για ένα σύνθετο αντικείμενο. Αλλά για εκείνους που περιέχουν ενσωματωμένη τύπους iterator όπως χαρακτηριστικά, όπως str, list, setή dict, ή οποιαδήποτε εφαρμογή του collections.Iterable, μπορείτε να παραλείψετε ορισμένα πράγματα στην τάξη σας.

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)

Μπορεί να χρησιμοποιηθεί σαν:

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

# prints
# a
# b
# c
# d
# e
Απαντήθηκε 14/08/2018 στις 07:25
πηγή χρήστη

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