Effective Python 读书笔记: 第40条: 考虑用协程来并发地运行多个函数

# -*- encoding: utf-8 -*-

from collections import namedtuple

'''
第40条: 考虑用协程来并发地运行多个函数

关键:
1 线程
缺点:
1)多线程代码难懂
2)线程需要占用大量内存,每个线程大约8MB
3)线程启动开销较大

2 协程
作用: 使python程序看上去同时运行多个函数
实现方式: 对生成器地扩展
优点: 占据地内存大约1KB
原理: 
1)生成器函数执行到yield表达式,消耗生成器的代码,通过send方法给
    生成器传回一个值,生成器收到了send函数传进来的这个值后,会视为是yield
    表达式的执行结果
2)协程是独立函数,消耗外部环境传入的输入数据,并产生输出结果
与线程的不同点:
协程会在生成器函数中的每个yield表达式那里暂停,等到外界再次
调用send方法,才会继续执行到下一个yield表达式。
注意: 在生成器调用send方法前,需要先调用一次next函数,将生成器
        进入到第一条yield表达式

3 yield from
1)python2中没有yield from表达式
把两个生成器组合起来,需要在委派给另一个线程的地方,多写一层循环
2)python2的生成器不支持return语句,需要定义自己的异常类型。
并在需要返回某个值的时候,抛出该异常

4 总结
协程让程序卡上去能同时运行大量函数
对于生成器内的yield表达式来说,外部代码通过send方法传递给生成器
的那个值,就是该表达式具备的值
协会次呢个可以把程序逻辑和交互代码分离
python2不支持yield from,也不支持在生成器内通过return语句向外界返回某个值。

参考:
Effectiv Python 编写高质量Python代码的59个有效方法
'''

def myCoroutine():
    while True:
        received = yield
        print "Received: {value}".format(
            value=received
        )


def useCoroutine():
    it = myCoroutine()
    '''
    在生成器调用send方法前,需要先调用一次next函数,将生成器
    进入到第一条yield表达式
    '''
    next(it)
    it.send('First')
    it.send('Second')


def minimize():
    current = yield
    while True:
        value = yield current
        current = min(value, current)


def useMinimize():
    it = minimize()
    next(it)
    print it.send(10)
    print it.send(4)
    print it.send(22)
    print it.send(-1)

ALIVE = "*"
EMPTY= "-"

Query = namedtuple('Query', ('y', 'x'))

def countNeighbors(y, x):
    north = yield Query(y + 1, x  + 0)
    northEast = yield Query(y + 1, x + 1)
    east = yield Query(y, x + 1)
    eastSouth = yield Query(y - 1, x + 1)
    south = yield Query(y - 1, x)
    westSouth = yield Query(y - 1, x - 1)
    west = yield Query(y, x - 1)
    westNorth = yield Query(x - 1, y + 1)
    neighborStates = [
        north, northEast,
        east, eastSouth,
        south, westSouth,
        west, westNorth
    ]
    count = 0
    for state in neighborStates:
        if state == ALIVE:
            count += 1
    yield count


def useCountNeighbors():
    it = countNeighbors(10, 5)
    q1 = next(it)
    print "First yield: {value}".format(
        value=q1
    )
    q2 = it.send(ALIVE)
    print "Second yield: {value}".format(
        value=q2
    )
    q3 = it.send(ALIVE)
    print "Third yield: {value}".format(
        value=q3
    )
    q4 = it.send(EMPTY)
    print "Fourth yield: {value}".format(
        value=q4
    )
    q5 = it.send(EMPTY)
    print "Fifth yield: {value}".format(
        value=q5
    )
    q6 = it.send(EMPTY)
    print "Sixth yield: {value}".format(
        value=q6
    )
    q7 = it.send(EMPTY)
    print "Seventh yield: {value}".format(
        value=q7
    )
    q8 = it.send(EMPTY)
    print "Eighth yield: {value}".format(
        value=q8
    )
    try:
        count = it.send(EMPTY)
        print "count: {value}".format(value=count)
    except StopIteration as e:
        print "Count: {value}".format(
            value=e.value
        )

# Transition = namedtuple('Transition', ('y', 'x', 'state'))
#
# def gameLogic(state, neighbors):
#     if state == ALIVE:
#         if neighbors < 2:
#             return EMPTY
#         elif neighbors > 3:
#             return EMPTY
#     else:
#         if neighbors == 3:
#             return ALIVE
#     return state
#
# '''
# 作用: 通过参数接收当前细胞的网格坐标,对坐标产生Query对象,
# 以查询本细胞的初始状态。接下来运行countNeighbots协程,检查
# 本细胞周边的其他细胞,最后运行gameLogic函数,判断本细胞
# 在下一轮的状态。最后生成Transition对象,把本细胞
# 在下一轮所应有的状态,告诉外部代码
# '''
# def stepCell(y, x):
#     state = yield Query(y, x)
#     neighbors = yield from countNeighbors(y, x)
#     nextState = gameLogic(state, neighbors)
#     yield Transition(y, x, nextState)
#
#
# def useStepCell():
#     it = stepCell(10, 5)
#     q0 = next(it)
#     print "Me: {value}".format(value=q0)
#     q1 = it.send(ALIVE)
#     print "Q1: {value}".format(value=q1)
#     t1 = it.send(EMPTY)
#     print "Outcome: {value}".format(value=t1)


def delegated2():
    yield 1
    yield 2

def composed2():
    yield 'A'
    for value in delegated2():
        yield value
    yield 'B'


class MyReturn(Exception):
    def __init__(self, value):
        self.value = value


def delegated():
    yield 1
    raise MyReturn(2)
    yield 'Not reached'


def composed():
    try:
        for value in delegated():
            yield value
    except MyReturn as e:
        output = e.value
    yield output * 4


def process():
    useCoroutine()
    useMinimize()
    # useCountNeighbors()
    print list(composed())

if __name__ == "__main__":
    process() 

猜你喜欢

转载自blog.csdn.net/qingyuanluofeng/article/details/88962733