Builder

   Builder

       Through list comprehension, I can directly create a list, but due to memory constraints, the capacity of the list is definitely limited, and creating a list containing 1 million elements not only takes up a lot of storage space, if we only need to access the front A few elements, then the space occupied by most of the elements behind is wasted.

So, if the elements of the list can be calculated according to a certain algorithm, can we continue to calculate the subsequent elements in the process of looping? This saves a lot of space by not having to create a complete list. In Python, this mechanism of computing while looping is called a generator: generator.

To create a generator, there are many ways. The first method is very simple, as long as a list comprehension is []changed ()to create a generator:

L = [X * X for x in range(10)]

print(l)         

[0149162536496481]

The generator is created.

g = (x * x for x in range(10))

print(g)

<generator object <genexpr> at 0x1022ef630>

 

The difference between creating a Lsum gis that the outermost []sum is a list (), butL a generator.g

We can print out each element of the list directly, but how do we print out each element of the generator?

If you want to print them out one by one, you can next()get the next return value of the generator through the function:

next(g)

0

next(g)

1

next(g)

4

next(g)

8

next(g)

9

next(g)

10

>>> next(g)

Traceback (most recent call last):
   File  "<stdin>" , line  1 in  <module>
StopIteration
 
        The generator saves the algorithm. Each time it is called next(g) , it calculates the value of g the next element until the last element is calculated, and when there are no more elements, StopIteration an error is thrown.
       Of course, the constant call next(g) above is too perverted. The correct way is to use a for loop, because the generator is also an iterable object:
      g  = (x * x for x in range(10))
           for n in g: 
                 print(n)
 0 1 2 3 4 5  6  7  8 9 
 
 
 
 

So, after we create a generator, it is basically never called next(), but foriterates through a loop and doesn't need to care about StopIterationerrors.

generators are very powerful. If the calculation algorithm is complex and forcannot be implemented by a loop similar to the list generation type, it can also be implemented by a function.

For example, in the famous Fibonacci sequence, any number except the first and second numbers can be obtained by adding the first two numbers:

1, 1, 2, 3, 5, 8, 13, 21, 34, ...

The Fibonacci sequence cannot be written with list comprehension, but it is easy to print it with a function:

   def fib(mix):

        n,x,y, = 0,0,1

        while n < mix:
              print(b)

              a,b = b, a+b

              n = n+1

        return 'ss'

 

Note that the assignment statement

a,b = b, a+b

is equivalent to:

t = (b,a+b) t is a tuple

a = t[0]

b = t[1]

But you can assign a value without explicitly writing out the temporary variable t.

    fibThe function actually defines the calculation rules of the Fibonacci sequence. It can start from the first element and calculate any subsequent elements. This logic is actually very similar to the generator.

 

def fib(max):
    n,a,b = 0,0,1

    while n < max:
        #print(b)
        yield  b
        a,b = b,a+b

        n += 1

    return 'done'
This is another way to define a generator. If a function definition contains the yield keyword , then the function is no longer a normal function, but a generator:
 
       Here, the most difficult thing to understand is that the execution flow of generator and function is different. Functions are executed sequentially, returning when the return statement or the last line of the function statement is encountered. The function that becomes the generator is executed every time it is called next() , and it yield returns when it encounters a statement. When it is executed again, it continues to execute from the last returned yield statement .
     

  在上面fibexample, we keep calling during the loop yield, it will keep breaking. Of course, a condition must be set for the loop to exit the loop, otherwise an infinite number list will be generated.

     Similarly, after changing the function to a generator, we basically never use it next()to get the next return value, but directly use the forloop to iterate:

     But forwhen the generator is called with a loop, it is found that the return value of the generator's returnstatement cannot be obtained. If you want to get the return value, you must catch the StopIterationerror, and the return value is contained StopIterationin value:

=  fib(6)

  while  True :
      try :
         x  =  next (g)
          print ( 'g:' , x)
      except  StopIteration as e:
          print ( 'Generator return value:' , e.value)
          break
 
It is also possible to achieve the effect of concurrent operations in the case of a single thread through yield
 
import time
def consumer(name):
    print("%s is ready to eat buns!" %name)
    while True:
       baozi = yield

       print("Baozi [%s] came and was eaten by [%s]!" %(baozi,name))


def producer(name):
    c = consumer('A')
    c2 = consumer('B')
    c.__next__()
    c2.__next__()
    print("I'm ready to make buns!")
    for i in range(10):
        time.sleep(1)
        print("Make 2 buns!")
        c.send(i)
        c2.send(i)

producer("alex")

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324781707&siteId=291194637