The usage and example of the Python coroutine are described in detail, and the python coroutine is described in detail.

Source: Internet
Author: User
Tags terminates

The usage and example of the Python coroutine are described in detail, and the python coroutine is described in detail.

Syntactically, coroutine is similar to generator, and is a function that defines the body containing the yield keyword. However, in the coroutine, yield usually appears on the right side of the expression (for example, datum = yield), which can produce a value or no output-if there is no expression after the yield keyword, the generator generates None.

The coroutine may receive data from the caller, but the caller provides the data to the coroutine using the. send (datum) method instead of the next (…) method (...) Function.

= The yield keyword even does not receive or transmit data. Regardless of how data flows, yield is a process control tool that enables collaborative Multitasking: The coroutine can concession the Controller to the central scheduler to activate other coroutine =.

Basic actions of coroutine generators

Here is the simplest coroutine code:

def simple_coroutine(): print('-> start') x = yield print('-> recived', x)sc = simple_coroutine()next(sc)sc.send('zhexiao')

Explanation:

1. coroutine uses generator Function Definition: The definition body contains the yield keyword.
2. yield is used in the expression. If the coroutine only needs to receive data from the customer, the output value is None. This value is implicitly specified because there is no expression on the right of the yield keyword.
3. First call next (...) Function, because the generator has not been started and is not paused in the yield statement, data cannot be sent at the beginning.
4. Call the send method, pass the value to the yield variable, recover the coroutine, and continue executing the following code until it runs to the next yield expression or ends.

= Note: The send method only works when the coroutine is in the gen_susponded State. Therefore, we use the next () method to activate the coroutine and stop it at the yield expression. Alternatively, we can use SC. send (None), with the same effect as next (SC) =.

Four statuses of coroutine:

Coroutine can be in one of the four States. The current status can be inspect. getgeneratorstate (...) The function determines. This function returns one of the following strings:
1. GEN_CREATED: Waiting for execution to begin
2. GEN_RUNNING: The interpreter is executing
3. GEN_SUSPENED: pause at yield Expression
4. GEN_CLOSED: Execution ends

= The first call to the next (SC) function is usually called the "pre-Exciting" (prime) coroutine = (that is, let the coroutine forward to the first yield expression, ready for use as an active coroutine ).

import inspectdef simple_coroutine(a): print('-> start') b = yield a print('-> recived', a, b) c = yield a + b print('-> recived', a, b, c)# run sc = simple_coroutine(5)next(sc)sc.send(6) # 5, 6sc.send(7) # 5, 6, 7

Example: Use coroutine to calculate the moving average

Def averager (): total = 0.0 count = 0 avg = None while True: num = yield avg total + = num count + = 1 avg = total/count # runag = averager () # pre-exciting coroutine print (next (ag) # Noneprint (ag. send (10) #10 print (ag. send (20) #15

Explanation:

1. After the next (ag) function is called, The coroutine executes the yield expression to generate the average variable's initial value-None.
2. At this time, the coroutine is suspended at the yield expression.
3. Use send () to activate the coroutine, assign the sent value to num, and calculate the avg value.
4. print the data returned by yield using print.

Terminate the coroutine and handle exceptions

Exceptions not processed in the coroutine will bubble up and be passed to the caller of the next function or the send method (that is, the object that triggers the coroutine ).

= One way to terminate a coroutine: Send a whistle value to exit the coroutine. Built-in constants such as None and Ellipsis are often used as the delimiter value =.

Explicitly send the exception to the coroutine

From Python 2.5, the customer code can call two methods on the generator object to explicitly send exceptions to the coroutine.

Generator. throw (exc_type [, exc_value [, traceback])

This causes the generator to throw the specified exception at the paused yield expression. If the generator processes the thrown exception, the code will execute to the next yield expression, and the output value will be the return value obtained by calling the generator. throw method. If the generator does not handle the thrown exception, the exception will bubble up and be passed to the context of the caller.

Generator. close ()

This causes the generator to throw a GeneratorExit exception at the paused yield expression. If the generator does not handle this exception or throws a StopIteration exception (usually from running to the end), the caller will not report an error. If a GeneratorExit exception is received, the generator cannot generate a value. Otherwise, the interpreter will throw a RuntimeError exception. Other exceptions thrown by the generator will bubble up and be passed to the caller.

Exception Handling example:

class DemoException(Exception): """ custom exception """def handle_exception(): print('-> start') while True:  try:   x = yield  except DemoException:   print('-> run demo exception')  else:   print('-> recived x:', x) raise RuntimeError('this line should never run')he = handle_exception()next(he)he.send(10) # recived x: 10he.send(20) # recived x: 20he.throw(DemoException) # run demo exceptionhe.send(40) # recived x: 40he.close()

If an exception cannot be handled, the coroutine terminates:

he.throw(Exception) # run demo exception

Yield from gets the return value of coroutine

In order to get the return value, the coroutine must be terminated normally. Then, the generator object throws a StopIteration exception. The value Attribute of the exception object stores the returned value.

= The yield from structure automatically captures the StopIteration exception internally =. For the yield from structure, the interpreter not only captures the StopIteration exception, but also changes the value of the value attribute to the value of the yield from expression.

Yield from basic usage

= When yield from subgen () is used in the generator gen, subgen will gain control and pass the output value to the gen caller, that is, the caller can directly control the subgen. At the same time, gen will block and wait for subgen to terminate =.

The following two functions serve the same purpose, but use yield from to be more concise:

def gen(): for c in 'AB':  yield cprint(list(gen()))def gen_new(): yield from 'AB'print(list(gen_new()))

= The first thing yield from x expressions do to x objects is to call iter (x) to obtain the iterator from it. Therefore, x can be any iteratable object, this is only the basic usage of yield from =.

Yield from advanced usage

= Yield from the main function is to open a two-way channel, connect the caller of the outermost layer with the child generator of the innermost layer, so that the two can directly send and generate values, you can also directly import exceptions without adding a large number of sample Codes = to the intermediate coroutine.

Yield from special terms

Delegate generator: A generator function that contains the yield from expression.
Sub-generator: The generator obtained from yield from.

Illustration

Explanation:

1. When the delegate generator is suspended in the yield from expression, the caller can directly send the data to The subgenerator.
2. The sub-generator then sends the output value to the caller.
3. After the sub-generator returns, the interpreter throws a StopIteration exception and attaches the returned value to the exception object. In this case, the delegate generator recovers.

Advanced example

From collections import namedtupleResClass = namedtuple ('res', 'count average') # sub-generator def averager (): total = 0.0 count = 0 average = None while True: term = yield if term is None: break total + = term count + = 1 average = total/count return ResClass (count, average) # Delegate generator def grouper (storages, key ): while True: # Get the value returned by averager () storages [key] = yield from averager () # client code def client (): process_data = {'Boys _ 2': [39.0, 40.8, 43.2, 40.8, 43.1, 38.6, 41.4, 40.6, 36.3], 'Boys _ 1': [1.38, 1.5, 1.32, 1.25, 1.37, 1.48, 1.25, 1.49, 1.46]} storages = {} for k, v in process_data.items (): # obtain coroutine = grouper (storages, k) # pre-exciting coroutine next (coroutine) # send data to coroutine in v: coroutine. send (dt) # terminate coroutine. send (None) print (storages) # runclient ()

Explanation:

1. Each iteration of the outer for loop creates a grouper instance and assigns a value to the coroutine variable. grouper is a delegate generator.

2. Call next (coroutine) to pre-delegate the generator grouper. At this time, the while True loop is entered. After the sub-generator averager is called, it is paused in the yield from expression.
3. The inner for loop calls coroutine. send (value) and directly transmits the value to the sub-generator averager. Meanwhile, the current grouper instance (coroutine) is suspended at the yield from expression.
4. After the inner loop ends, the grouper instance is still suspended in the yield from expression. Therefore, the statement assigned to the results [key] value in the grouper function definition has not been executed.
5. coroutine. send (None) terminates the averager sub-generator. The sub-generator throws a StopIteration exception and includes the returned data in the value of the exception object, yield from can directly capture the StopItration exception and assign the value of the exception object to results [key]

Significance of yield from

  • The values produced by the sub-generator are directly transmitted to the caller (client code) of the delegate generator ).
  • The values sent to the delegate generator using the send () method are directly transmitted to The subgenerator. If the sent value is None, the next () method of the Subgenerator is called. If the sent value is not None, The subgenerator's send () method is called. If the called method throws a StopIteration exception, the delegate generator resumes running. Any other exceptions will bubble up and be passed to the delegate generator.
  • When the generator exits, the return expr expression in the generator (or sub-generator) triggers the StopIteration (expr) exception and throws.
  • The value of the yield from expression is the first parameter that is passed to the StopIteration exception when The subgenerator ends.
  • The exception of passing in the delegate generator is passed to the throw () method of The subgenerator except GeneratorExit. If a StopIteration exception is thrown when the throw () method is called, delegate the generator to resume running. Exceptions other than StopIteration will bubble up and be passed to the delegate generator.
  • If the GeneratorExit exception is passed into the delegate generator, or the close () method is called on the delegate generator, the close () method is called on The subgenerator, if any. If you call the close () method to throw an exception, the exception will bubble up and be passed to the delegate generator. Otherwise, the delegate generator throws a GeneratorExit exception.

Use Cases

A process can naturally express many algorithms, such as simulation, games, asynchronous I/O, and other event-driven programming forms or collaborative multitasking. Coroutine is the basic construction of the asyncio package. The simulation system can explain how to use coroutine instead of thread to implement concurrent activities.

In the field of simulation, the term process refers to the activity of an entity in the model and has nothing to do with the process in the operating system. A process in the simulation system can be implemented by a process in the operating system, but usually implemented by a thread or coroutine.

Taxi examples

Import collections # The time field is the simulation time when an event occurs. # The proc field is the ID of the taxi process instance. # The action field is the string that describes the activity. Event = collections. namedtuple ('event', 'Time proc Action') def taxi_process (proc_num, trips_num, start_time = 0): "creates an Event every time the status changes, giving control to the simulator: param proc_num: param trips_num: param start_time: return: "" time = yield Event (start_time, proc_num, 'Leave garage') for I in range (trips_num ): time = yield Event (time, proc_num, 'Pick up people ') time = yield Event (time, proc_num, 'drop off people') yield Event (time, proc_num, 'Go home') # runt1 = taxi_process (1, 1) a = next (t1) print (a) # Event (time = 0, proc = 1, action = 'ave garage') B = t1.send (. time + 6) print (B) # Event (time = 6, proc = 1, action = 'pick up people ') c = t1.send (B. time + 12) print (c) # Event (time = 18, proc = 1, action = 'drop off others') d = t1.send (c. time + 1) print (d) # Event (time = 19, proc = 1, action = 'go home ')

Simulate the console to control the Asynchronization of Three taxis

Import collectionsimport queueimport random # The time field is the simulation time when an event occurs, # The proc field is the ID of the taxi process instance, and # The action field is the string that describes the activity. Event = collections. namedtuple ('event', 'Time proc Action') def taxi_process (proc_num, trips_num, start_time = 0): "creates an Event every time the status changes, giving control to the simulator: param proc_num: param trips_num: param start_time: return: "" time = yield Event (start_time, proc_num, 'Leave garage') for I in range (trips_num ): time = yield Event (time, proc_num, 'Pick up people ') time = yield Event (time, proc_num, 'drop off people') yield Event (time, proc_num, 'Go home') class SimulateTaxi (object): "simulated taxi console" def _ init _ (self, proc_map): # Save the PriorityQueue object of the scheduled event, # If the tuple type is used, tuple [0] is used by default for sorting self. events = queue. priorityQueue () # The procs_map parameter is a dictionary that uses dict to build a local copy of self. procs = dict (proc_map) def run (self, end_time): "" scheduled and show events until the end of time: param end_time: return: "_, taxi_gen in self. procs. items (): leave_evt = next (taxi_gen) self. events. put (leave_evt) # main loop of the simulation system simulate_time = 0 while simulate_time <end_time: if self. events. empty (): print ('*** end of events ***') break # occurrence of the first event current_evt = self. events. get () simulate_time, proc_num, action = current_evt print ('Taxi: ', proc_num,', at time: ', simulate_time,', ', action) # Prepare the occurrence of the next event proc_gen = self. procs [proc_num] next_simulate_time = simulate_time + self. compute_duration () try: next_evt = proc_gen.send (next_simulate_time) failed t StopIteration: del self. procs [proc_num] else: self. events. put (next_evt) else: msg = '*** end of simulation time: {} events pending ***' print (msg. format (self. events. qsize () @ staticmethod def compute_duration (): "time when the next event is generated randomly: return:" duration_time = random. randint (1, 20) return duration_time # generate 3 taxis, and now all of them have not left garagetaxis = {I: taxi_process (I, (I + 1) * 2, I * 5) for I in range (3)} # simulate running st = SimulateTaxi (taxis) st. run (1, 100)

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.