简体   繁体   中英

Return generator VS iterate with while until None

From two options below what is more appealing(readable code, more pythonic, efficiency , etc..) for running through iterable and if I want add more logic in the future(for example add 1 to every returned value)?

One of options returns generator, the other returns next item and None when done all iterations.

Is there a preferable method? If yes, why? Is there limitations some method has that another don't.

class Option1():
    def __init__(self):
        self.current = 0

    def get_next_batch(self, my_list):
        if self.current == len(my_list):
            self.current = 0
            return None
        self.current += 1
        return [my_list[self.current-1]]


class Option2():
        def __init__(self):
            self.current = 0

        def get_next_batch(self, my_list):
            while self.current < len(my_list):
                yield [my_list[self.current]]
                self.current += 1
            self.current = 0
            raise StopIteration()

Usage:

o1 = Option1()
o2 = Option2()
arr = [1,2,3,4,5]

a = o1.get_next_batch(arr)

while a is not None:
     print a
     a = o1.get_next_batch(arr)

for item in o2.get_next_batch(arr):
    print item

Output in both cases:

[1]
[2]
[3]
[4]
[5]

You almost certainly want to go with the second. Less lines, less opportunity for things to go wrong.

However, seeing as you're not using current outside the iterator I would just optimise the whole thing down to:

def get_next_batch(my_list):
    for item in my_list:
        yield [item]

arr = [1,2,3,4,5]
for item in get_next_batch(arr):
    print item

Side points. Always make your classes inherit from object in python 2.7, and don't raise StopIteration to halt a generator in python -- it's deprecated behaviour that can lead to bugs. Use return instead. eg.

def get_next_batch(my_list):
    for item in my_list:
        if item > 3:
            return
        yield [item]

batched = list(get_next_batch([1,2,3,4,5]))
expected = [[1], [2], [3]]
print batched == expected

You can tidy up Option1 to make it easy to use in for loops. To do this you can make it with the iterator protocol. That is an __iter__ method that returns self and a next method to get the next item. eg.

class UsingNext(object):

    def __init__(self, mylist):
        self.current = 0
        self.mylist = mylist

    def __iter__(self):
        return self

    def next(self): # __next__ in python 3
        if self.current >= len(self.mylist):
            raise StopIteration
        if self.current == 2:
            self.current += 1
            return "special"
        item = self.mylist[self.current]
        self.current += 1
        return [item]

class UsingYield(object):

    def __init__(self, mylist):
        self.mylist = mylist

    def __iter__(self):
        for current, item in enumerate(self.mylist):
            if current == 2:
                yield "special"
                continue
            yield [item]

arr = range(1, 6)
# both print
# [1]
# [2]
# special
# [4]
# [5]
for item in UsingNext(arr):
    print item
for item in UsingYield(arr):
    print item

In my mind the generator version is cleaner, and easier to understand.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM