Python programming skills - containers, iterators, generators

container

Containers can be intuitively imagined as units with multiple elements together, and the difference between different containers lies in the implementation method of internal data structure. Lists (list: [0, 1, 2]), tuples (tuple: (0, 1, 2)), dictionaries (dict: {0:0, 1:1, 2:2}), and collections (set: set([0, 1, 2]) are containers.

Iterator iterator

Each container is iterative. The iterator provides a next method. After calling this method, you can either get the next object of the container or get a StopIteration error. There is no need to specify the index of an element like a list, because containers such as dictionaries and collections do not have an index.
The ITER (variable) function can return an iterator, and then traverse through the next function. If the variable is not iterative, it returns TypeErrer

Generator generator

In the iterator, if you want to enumerate the elements, all its elements need to be generated in advance and saved in memory. If the amount of data is too large, it will occupy more memory and even cause errors.
The generator generates the next variable only when it calls the next() function. Generators don't take up as much memory as iterators, and they are called only when they are used. Moreover, the generator does not need to run a generation operation during initialization, so the time consumption will be shorter than that of the iterator.
The generator is written in Python in parentheses, list = (i for i in range(100000000)), that is, a generator is initialized.
Example 1:

def generator(k):
    i = 1
    while True:
        yield i ** k
        i += 1

gen_1 = generator(1)
gen_3 = generator(3)
print(gen_1)
print(gen_3)

def get_sum(n):
    sum_1, sum_3 = 0, 0
    for i in range(n):
        next_1 = next(gen_1)
        next_3 = next(gen_3)
        print('next_1 = {}, next_3 = {}'.format(next_1, next_3))
        sum_1 += next_1
        sum_3 += next_3
    print(sum_1 * sum_1, sum_3)

get_sum(8)
########## output ##########
<generator object generator at 0x000001E70651C4F8>
<generator object generator at 0x000001E70651C390>
next_1 = 1, next_3 = 1
next_1 = 2, next_3 = 8
next_1 = 3, next_3 = 27
next_1 = 4, next_3 = 64
next_1 = 5, next_3 = 125
next_1 = 6, next_3 = 216
next_1 = 7, next_3 = 343
next_1 = 8, next_3 = 512
1296 1296

The above code is to verify an identity, (1 + 2 + 3 +... + n)^2 = 1^3 + 2^3 + 3^3 +... + n^3.
generator() is a function that returns a generator. Yield is the key. It can be understood that when the function runs to this line, the program will pause here, and then jump out to the next() function. i ** k becomes the return value of the next() function. Every time the next(gen) function is called, the suspended program is revived and continues to execute from yield; At the same time, note that the local variable i is not cleared, but will continue to accumulate. We can see next_1 changes from 1 to 8, next_3 changes from 1 to 512.
An iterator is a finite set, and a generator can be an infinite set. I just call next(). The generator will automatically generate new elements according to the operation, and then return them to you. It's very convenient.
Example 2:
Given two sequences, determine whether the first is a subsequence of the second.

def is_subsequence(a, b):
    b = iter(b)
    return all(i in b for i in a)

print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))
print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))

########## output ##########
True
False

Equivalent to:

def is_subsequence(a, b):
    b = iter(b)
    print(b)

    gen = (i for i in a)
    print(gen)

    for i in gen:
        print(i)

    gen = ((i in b) for i in a)
    print(gen)

    for i in gen:
        print(i)
	# The for i in gen of this code has consumed the iterator of b, and b is already an empty iterator. Therefore, if you execute all(((i in b) for i in a)), it will be False. Normally, it should be True.
    return all(((i in b) for i in a))

print(is_subsequence([1, 3, 5], [1, 2, 3, 4, 5]))
print(is_subsequence([1, 4, 3], [1, 2, 3, 4, 5]))

########## output ##########

<list_iterator object at 0x000001E7063D0E80>
<generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C570>
1
3
5
<generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C5E8>
True
True
True
False
<list_iterator object at 0x000001E7063D0D30>
<generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C5E8>
1
4
3
<generator object is_subsequence.<locals>.<genexpr> at 0x000001E70651C570>
True
True
False
False

i in b in (i in b) for i in a of the above code can be understood as follows:

while True:
    val = next(b) #That's why b needs to be an iterator
    if val == i:
        yield True

Keywords: Python Programming

Added by tinkertron on Fri, 14 Jan 2022 03:09:17 +0200