Python's Natural Selection

Function

Execution starts from the first line and continues until​

  • an Exception is raised
  • there is a return statement
  • return implies the function is returning control of execution to the point where the function was called
  • and then there's yield
  • yield implies the transfer of control is temporary and the function expects to regain it in the future

Iterable

Object capable of returning it's members one at a time

  • list
  • str
  • tuple
  • dict
  • file object
  • object of any class with defined __iter__() method

Can be used in for, zip, map, etc.

Iterator

An object representing a stream of data

Repeated calls to the iterator’s __next__() method (or passing it to the built-in function next()) return successive items in the stream

When no more data is available a StopIteration exception is raised instead

Iterators are required to have an __iter__() method that returns the iterator object itself

>>>examples

>simple_iterator.py

Generator

A generator generates values

Generator function

We can get the values from a generator by calling next

>simple_generator.py

  • If the body of a def contains yield, the function automatically becomes a generator function

Generator function creates generator iterator a.k.a. generator

Once a generator has been exhausted it will raise StopIteration

You can only ever consume all values from a generator once

I will call you back

.send(value) - resumes execution & passes value into the generator

Simplified - calling .send(None) is equivalent to calling .next()

>>>example

>complex_generator.py

`send` returns the next value yielded by the generator

  • Or raises StopIteration                                                                        

Execution begins at the top of the generator's function body

There is no yield expression to receive a value when the generator has just been created

Yield for me

yield from <expression>

The expression must evaluate to an iterable, from which an iterator is extracted and run until exhaustion

When the iterator is another generator, the subgenerator is allowed to execute a return statement with a value, and that value becomes the value of the yield from expression

>yield_from.py

Coroutines

Computer program components that generalize subroutines for nonpreemptive multitasking, by allowing multiple entry points for suspending and resuming execution at certain locations

Functions whose execution you can pause

They can be

  • Entered                                                                                               
  • Exited                                                                                                    
  • And resumed                                                                                       
  • At many different points                                                                    

They don't execute until you tell them to

The GIL

Global interpreter lock

It ensures that only one thread runs in the interpreter at once

You can't utilize multiple CPUs

   1) You do not talk about the GIL

   2) You do NOT talk about the GIL

Asyncio

Programming construct that waits for and
dispatches events or messages in a program

Maximize the use of single thread

You can't achieve parallelism

One event loop calling callbacks

Python3.5

In Python 3.5, the `types.coroutine` decorator was introduced

A coroutine function may be defined with the async def statement, and may contain await, async for, and async with keywords.

An object that can be used in an await expression can be a coroutine or an object with an __await__() method

It transforms a generator function into a coroutine function which returns a generator-based coroutine. The generator-based coroutine is still a generator iterator, but is also considered to be a coroutine object and is awaitable

It takes coroutines from simply being an interface to an actual type, making the distinction between any generator and a generator that is meant to be a coroutine much more stringent

I will await you if you async me

`await` expression is only valid within an `async def`

An object that defines an __await__() method which returns an iterator which is not a coroutine itself

Coroutines themselves are also considered awaitable objects

When you call await on an object , it  needs to be an awaitable object

So...

Defining a method with async def makes it a coroutine

An await expression is basically `yield from` but with restrictions of only working with awaitable objects (plain generators will not work with an await expression)

An async function is a coroutine that either has:

The other way to make a coroutine is to flag a generator with types.coroutine

  • return statement
  • await expressions
  • yield expressions are not allowed

async/await is really an API for asynchronous programming

asyncio is a framework that can utilize the async/await API for asynchronous programming

Pyton's Natural Selection

By Pavlin Gergov

Pyton's Natural Selection

  • 93