Build a Basic Python Iterator

前端 未结 10 1070
南旧
南旧 2020-11-21 12:22

How would one create an iterative function (or iterator object) in python?

相关标签:
10条回答
  • 2020-11-21 12:50

    Include the following code in your class code.

     def __iter__(self):
            for x in self.iterable:
                yield x
    

    Make sure that you replace self.iterablewith the iterable which you iterate through.

    Here's an example code

    class someClass:
        def __init__(self,list):
            self.list = list
        def __iter__(self):
            for x in self.list:
                yield x
    
    
    var = someClass([1,2,3,4,5])
    for num in var: 
        print(num) 
    

    Output

    1
    2
    3
    4
    5
    

    Note: Since strings are also iterable, they can also be used as an argument for the class

    foo = someClass("Python")
    for x in foo:
        print(x)
    

    Output

    P
    y
    t
    h
    o
    n
    
    0 讨论(0)
  • 2020-11-21 12:52

    There are four ways to build an iterative function:

    • create a generator (uses the yield keyword)
    • use a generator expression (genexp)
    • create an iterator (defines __iter__ and __next__ (or next in Python 2.x))
    • create a class that Python can iterate over on its own (defines __getitem__)

    Examples:

    # generator
    def uc_gen(text):
        for char in text.upper():
            yield char
    
    # generator expression
    def uc_genexp(text):
        return (char for char in text.upper())
    
    # iterator protocol
    class uc_iter():
        def __init__(self, text):
            self.text = text.upper()
            self.index = 0
        def __iter__(self):
            return self
        def __next__(self):
            try:
                result = self.text[self.index]
            except IndexError:
                raise StopIteration
            self.index += 1
            return result
    
    # getitem method
    class uc_getitem():
        def __init__(self, text):
            self.text = text.upper()
        def __getitem__(self, index):
            return self.text[index]
    

    To see all four methods in action:

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

    Which results in:

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

    Note:

    The two generator types (uc_gen and uc_genexp) cannot be reversed(); the plain iterator (uc_iter) would need the __reversed__ magic method (which, according to the docs, must return a new iterator, but returning self works (at least in CPython)); and the getitem iteratable (uc_getitem) must have the __len__ magic method:

        # for uc_iter we add __reversed__ and update __next__
        def __reversed__(self):
            self.index = -1
            return self
        def __next__(self):
            try:
                result = self.text[self.index]
            except IndexError:
                raise StopIteration
            self.index += -1 if self.index < 0 else +1
            return result
    
        # for uc_getitem
        def __len__(self)
            return len(self.text)
    

    To answer Colonel Panic's secondary question about an infinite lazily evaluated iterator, here are those examples, using each of the four methods above:

    # 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
    

    Which results in (at least for my sample 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
    

    How to choose which one to use? This is mostly a matter of taste. The two methods I see most often are generators and the iterator protocol, as well as a hybrid (__iter__ returning a generator).

    Generator expressions are useful for replacing list comprehensions (they are lazy and so can save on resources).

    If one needs compatibility with earlier Python 2.x versions use __getitem__.

    0 讨论(0)
  • 2020-11-21 12:57

    This is an iterable function without yield. It make use of the iter function and a closure which keeps it's state in a mutable (list) in the enclosing scope 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, closure state is kept in an immutable in the enclosing scope and nonlocal is used in local scope to update the state variable.

    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
    
    0 讨论(0)
  • 2020-11-21 12:57

    If you looking for something short and simple, maybe it will be enough for you:

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

    example of usage:

    In [3]: a = A([2,3,4])
    
    In [4]: [i for i in a]
    Out[4]: [2, 3, 4]
    
    0 讨论(0)
提交回复
热议问题