您的位置:首页 > 编程语言 > Python开发

Python Coroutine 初探

2015-11-22 16:06 671 查看
原文链接:http://whosemario.github.io/2015/11/22/python-coroutine/

从Python2.5开始

首先引用PEP342开篇的一段话

This PEP proposes some enhancements to the API and syntax of generators, to make them usable as simple coroutines.

在Python2.x,我们可以用Generator来实现Coroutine的功能。

def coroutine(func):
def wrapper(*args, **kwargs):
cr = func(*args, **kwargs)
try:
cr.next()
except StopIteration: pass
return cr
return wrapper


上面是一个coroutine的修饰器,如下是使用此修饰器的方法。

@coroutine
def grep(pattern):
print "Looking for %s" % pattern
while True:
line = (yield)
if pattern in line:
print line,

g = grep("hello")
g.send("hello world")
g.send("test hello")
g.send("test world")


其实python在编译grep这段代码的时候,发现有yield关键字,func_code的co_flags会存在CO_GENERATOR标志位,因此在PyEval_EvalCodeEx执行的过程中会返回一个generator实例,而不是去执行这个函数的func_code。generator实例中会保存对应的PyFrameObject。

PyObject *
PyGen_New(PyFrameObject *f)
{
PyGenObject *gen = PyObject_GC_New(PyGenObject, &PyGen_Type);
... ...
gen->gi_frame = f;
Py_INCREF(f->f_code);
gen->gi_code = (PyObject *)(f->f_code);
gen->gi_running = 0;
gen->gi_weakreflist = NULL;
_PyObject_GC_TRACK(gen);
return (PyObject *)gen;
}


在generator的send方法中会继续运行此时保存的PyFrameObject。

static PyObject *
gen_send_ex(PyGenObject *gen, PyObject *arg, int exc)
{
PyThreadState *tstate = PyThreadState_GET();
PyFrameObject *f = gen->gi_frame;
PyObject *result;
... ...
result = PyEval_EvalFrameEx(f, exc);
... ...
return result
}


Python3.3的yield from

Python3.3针对Generator加入了新的语法yield from,个人感觉新的语法可以让某些功能的实现更新简洁(其实就是少写点代码啊,,,),但真正本质上并没有改善Generator。这篇文章对yield from语法做了一定的解释。

Python3.5 async def

Python3.5引入了async def语法,定义一个coroutine。

import asyncio

async def compute(x, y):
print("Compute %s + %s ..." % (x, y))
await asyncio.sleep(1.0)
return x + y

async def print_sum(x, y):
result = await compute(x, y)
print("%s + %s = %s" % (x, y, result))

loop = asyncio.get_event_loop()
loop.run_until_complete(print_sum(1, 2))
loop.close()


当async def函数里面调用await的时候,这个coroutine将会被挂起(Suspended)。但看Python3.5的源码后发现,coroutine的内部实现和generator的内部实现基本相同。

# genobject.c
PyTypeObject PyCoro_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"coroutine",                                /* tp_name */
...
coro_methods,                               /* tp_methods */
...
};


上面是coroutine的PyTypeObject的定义,其实一开始的示例代码中print_sum函数返回的正是一个coroutine实例(是不是跟generator很像呢?!)。我们在看一下coro_methods的定义。

static PyMethodDef coro_methods[] = {
{"send",(PyCFunction)_PyGen_Send, METH_O, coro_send_doc},
{"throw",(PyCFunction)gen_throw, METH_VARARGS, coro_throw_doc},
{"close",(PyCFunction)gen_close, METH_NOARGS, coro_close_doc},
{NULL, NULL}        /* Sentinel */
};


send方法的实现就是_PyGen_Send,和generator的实现一模一样,await后面可以紧跟一个新的coroutine实例,这个功能则归功于Python3.3开始出现的yield from语法。

references:

PEP342 - https://www.python.org/dev/peps/pep-0342/

PEP380 - https://docs.python.org/3/whatsnew/3.3.html#pep-380

A Curious Course on Coroutines and Concurrency - http://www.dabeaz.com/coroutines/Coroutines.pdf
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: