我有一个多线程程序,在其中创建生成器函数,然后将其传递给新线程。 我希望它本质上是共享的/全局的,以便每个线程都可以从生成器获取下一个值。
使用这样的生成器是否安全,还是会遇到从多个线程访问共享生成器的问题/情况?
如果不是,是否有更好的方法来解决该问题? 我需要可以遍历列表并为任何线程调用它生成下一个值的东西。
如何使用Python C API复制以下Python代码?
class Sequence():
def __init__(self, max):
self.max = max
def data(self):
i = 0
while i < self.max:
yield i
i += 1
到目前为止,我有这个:
#include <Python/Python.h>
#include <Python/structmember.h>
/* Define a new object class, Sequence. */
typedef struct {
PyObject_HEAD
size_t max;
} SequenceObject;
/* Instance variables */
static PyMemberDef Sequence_members[] = {
{"max", T_UINT, offsetof(SequenceObject, max), 0, NULL},
{NULL} /* Sentinel */
};
static int Sequence_Init(SequenceObject *self, PyObject *args, PyObject *kwds)
{
if (!PyArg_ParseTuple(args, "k", &(self->max))) {
return -1;
}
return 0;
}
static PyObject *Sequence_data(SequenceObject *self, PyObject *args);
/* Methods */
static PyMethodDef Sequence_methods[] = {
{"data", (PyCFunction)Sequence_data, METH_NOARGS,
"sequence.data() -> iterator object\n"
"Returns iterator of range [0, sequence.max)."},
{NULL} /* Sentinel */
};
/* Define new object type */
PyTypeObject Sequence_Type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Sequence", /* tp_name */
sizeof(SequenceObject), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
"Test generator object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
Sequence_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Sequence_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
static PyObject *Sequence_data(SequenceObject *self, PyObject *args)
{
/* Now what? */
}
但我不确定下一步要去哪里。 有人可以提供一些建议吗?
我想我遇到的主要问题是模拟StopIteration
语句。 据我了解,这是一个看起来很简单但实际上很复杂的语句-它使用自己的2994411917034390590529和2994411917034390590530方法创建了一个生成器,这些方法会自动调用。 搜索文档,它似乎与PyGenObject相关联; 但是,如何创建该对象的新实例尚不清楚。 PyGen_New()
将PyFrameObject
作为其参数,我可以找到的唯一参考是StopIteration
,这似乎不是我想要的(或者我弄错了吗?)。 有谁可以分享这些经验吗?
当我(基本上)扩展Python在幕后所做的工作时,我发现这一点更加清楚:
class IterObject():
def __init__(self, max):
self.max = max
def __iter__(self):
self.i = 0
return self
def next(self):
if self.i >= self.max:
raise StopIteration
self.i += 1
return self.i
class Sequence():
def __init__(self, max):
self.max = max
def data(self):
return IterObject(self.max)
从技术上讲,顺序是一一完成的,但是您可以理解。
唯一的问题是,每当需要一个生成器时创建一个新对象就很烦人。由于定义新类型会带来怪异,因此在Python中使用C生成的对象比使用C生成的对象还要多。 由于C没有闭包,因此C中不能有StopIteration
语句。 相反,我所做的是(因为我无法在Python API中找到它-如果已经存在,请指向一个标准对象!)是创建一个简单的通用生成器对象类,该类针对每个next()
方法调用都调用一个C函数 。 在这里(请注意,我尚未尝试编译它,因为它不完整-见下文):
#include <Python/Python.h>
#include <Python/structmember.h>
#include <stdlib.h>
/* A convenient, generic generator object. */
typedef PyObject *(*callback)(PyObject *callee, void *info) PyGeneratorCallback;
typedef struct {
PyObject HEAD
PyGeneratorCallback callback;
PyObject *callee;
void *callbackInfo; /* info to be passed along to callback function. */
bool freeInfo; /* true if |callbackInfo| should be free'()d when object
* dealloc's, false if not. */
} GeneratorObject;
static PyObject *Generator_iter(PyObject *self, PyObject *args)
{
Py_INCREF(self);
return self;
}
static PyObject *Generator_next(PyObject *self, PyObject *args)
{
return self->callback(self->callee, self->callbackInfo);
}
static PyMethodDef Generator_methods[] = {
{"__iter__", (PyCFunction)Generator_iter, METH_NOARGS, NULL},
{"next", (PyCFunction)Generator_next, METH_NOARGS, NULL},
{NULL} /* Sentinel */
};
static void Generator_dealloc(GenericEventObject *self)
{
if (self->freeInfo && self->callbackInfo != NULL) {
free(self->callbackInfo);
}
self->ob_type->tp_free((PyObject *)self);
}
PyTypeObject Generator_Type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Generator", /* tp_name */
sizeof(GeneratorObject), /* tp_basicsize */
0, /* tp_itemsize */
Generator_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
0, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
/* Returns a new generator object with the given callback function
* and arguments. */
PyObject *Generator_New(PyObject *callee, void *info,
bool freeInfo, PyGeneratorCallback callback)
{
GeneratorObject *generator = (GeneratorObject *)_PyObject_New(&Generator_Type);
if (generator == NULL) return NULL;
generator->callee = callee;
generator->info = info;
generator->callback = callback;
self->freeInfo = freeInfo;
return (PyObject *)generator;
}
/* End of Generator definition. */
/* Define a new object class, Sequence. */
typedef struct {
PyObject_HEAD
size_t max;
} SequenceObject;
/* Instance variables */
static PyMemberDef Sequence_members[] = {
{"max", T_UINT, offsetof(SequenceObject, max), 0, NULL},
{NULL} /* Sentinel */
}
static int Sequence_Init(SequenceObject *self, PyObject *args, PyObject *kwds)
{
if (!PyArg_ParseTuple(args, "k", &self->max)) {
return -1;
}
return 0;
}
static PyObject *Sequence_data(SequenceObject *self, PyObject *args);
/* Methods */
static PyMethodDef Sequence_methods[] = {
{"data", (PyCFunction)Sequence_data, METH_NOARGS,
"sequence.data() -> iterator object\n"
"Returns generator of range [0, sequence.max)."},
{NULL} /* Sentinel */
};
/* Define new object type */
PyTypeObject Sequence_Type = {
PyObject_HEAD_INIT(NULL)
0, /* ob_size */
"Sequence", /* tp_name */
sizeof(SequenceObject), /* tp_basicsize */
0, /* tp_itemsize */
0, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
"Test generator object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
Sequence_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Sequence_init, /* tp_init */
0, /* tp_alloc */
PyType_GenericNew, /* tp_new */
};
static PyObject *Sequence_data(SequenceObject *self, PyObject *args)
{
size_t *info = malloc(sizeof(size_t));
if (info == NULL) return NULL;
*info = 0;
/* |info| will be free'()d by the returned generator object. */
GeneratorObject *ret = Generator_New(self, info, true,
&Sequence_data_next_callback);
if (ret == NULL) {
free(info); /* Watch out for memory leaks! */
}
return ret;
}
PyObject *Sequence_data_next_callback(PyObject *self, void *info)
{
size_t i = info;
if (i > self->max) {
return NULL; /* TODO: How do I raise StopIteration here? I can't seem to find
* a standard exception. */
} else {
return Py_BuildValue("k", i++);
}
}
但是,不幸的是,我还没有完成。 我剩下的唯一问题是:如何使用C API引发StopIteration
异常? 我似乎找不到标准例外中列出的内容。 另外,也许更重要的是,这是解决此问题的正确方法吗?
感谢任何仍在遵循此规则的人。
假设我有一个列表,希望不返回而是从中产生值。 最Python的方式是什么?
这就是我的意思。 多亏了一些非延迟计算,我才计算出了列表2989579180238238701568,但是我在整个项目中的代码使用了延迟计算,因此我想从函数中产生值,而不是返回整个列表。
我目前将其编写如下:
my_list = ['a', 'b', 'c', 'd']
for item in my_list:
yield item
但这对我来说并不好用。
我正在使用生成器在类似于以下简单示例的列表中执行搜索:
>>> a = [1,2,3,4]
>>> (i for i, v in enumerate(a) if v == 4).next()
3
(仅举例说明一下,与上面的列表相比,我使用的列表要长得多,并且条目比None
还要复杂一些。我这样做是为了避免每次遍历整个列表 我搜索他们)
现在,如果我改为将其更改为None
,它将返回try ... except
,因为在a
中找不到任何666
条目。
如何使它返回None
? 我当然可以将其包装在try ... except
子句中,但是还有更多的Python方式可以做到这一点吗?
(这个问题与此相关,但是这些问题是在发电机前进行的,这正是我要避免的问题)
我想将生成器拆分为多个块。 要求是:
我尝试了以下代码:
def head(iterable, max=10):
for cnt, el in enumerate(iterable):
yield el
if cnt >= max:
break
def chunks(iterable, size=10):
i = iter(iterable)
while True:
yield head(i, size)
# Sample generator: the real data is much more complex, and expensive to compute
els = xrange(7)
for n, chunk in enumerate(chunks(els, 3)):
for el in chunk:
print 'Chunk %3d, value %d' % (n, el)
这以某种方式起作用:
Chunk 0, value 0
Chunk 0, value 1
Chunk 0, value 2
Chunk 1, value 3
Chunk 1, value 4
Chunk 1, value 5
Chunk 2, value 6
^CTraceback (most recent call last):
File "xxxx.py", line 15, in <module>
for el in chunk:
File "xxxx.py", line 2, in head
for cnt, el in enumerate(iterable):
KeyboardInterrupt
Buuuut ...因为2964171943525745745665,它永远不会停止(我必须按chunks
)。无论何时使用完发电机,我都想停止该循环,但是我不知道如何检测这种情况。 我试图提出一个异常:
class NoMoreData(Exception):
pass
def head(iterable, max=10):
for cnt, el in enumerate(iterable):
yield el
if cnt >= max:
break
if cnt == 0 : raise NoMoreData()
def chunks(iterable, size=10):
i = iter(iterable)
while True:
try:
yield head(i, size)
except NoMoreData:
break
# Sample generator: the real data is much more complex, and expensive to compute
els = xrange(7)
for n, chunk in enumerate(chunks(els, 2)):
for el in chunk:
print 'Chunk %3d, value %d' % (n, el)
但是然后仅在使用方的上下文中引发异常,这不是我想要的(我想保持使用方代码干净)
Chunk 0, value 0
Chunk 0, value 1
Chunk 0, value 2
Chunk 1, value 3
Chunk 1, value 4
Chunk 1, value 5
Chunk 2, value 6
Traceback (most recent call last):
File "xxxx.py", line 22, in <module>
for el in chunk:
File "xxxx.py", line 9, in head
if cnt == 0 : raise NoMoreData
__main__.NoMoreData()
如何在chunks
功能中检测到发电机已耗尽,而没有行走?
如何创建使用Eclipse在Android中生成随机数,然后在TextView
字段中显示结果的应用程序? 随机数必须在用户选择的范围内。 因此,用户将输入范围的最大值和最小值,然后输出答案。
我有一个生成器和一个使用它的函数:
def read():
while something():
yield something_else()
def process():
for item in read():
do stuff
如果生成器引发异常,我想在使用者函数中处理该异常,然后继续使用迭代器,直到耗尽为止。 请注意,我不想在生成器中有任何异常处理代码。
我想到了类似的东西:
reader = read()
while True:
try:
item = next(reader)
except StopIteration:
break
except Exception as e:
log error
continue
do_stuff(item)
但这对我来说看起来很尴尬。
我有一个python列表,其中元素可以重复。
>>> a = [1,2,2,3,3,4,5,6]
我想从列表中获得第一个n
个独特元素。因此,在这种情况下,如果我想要前5个唯一元素,它们将是:
[1,2,3,4,5]
我想出了一个使用生成器的解决方案:
def iterate(itr, upper=5):
count = 0
for index, element in enumerate(itr):
if index==0:
count += 1
yield element
elif element not in itr[:index] and count<upper:
count += 1
yield element
正在使用:
>>> i = iterate(a, 5)
>>> [e for e in i]
[1,2,3,4,5]
我怀疑这是否是最佳解决方案。 有没有一种我可以实现的替代策略,可以用更加Python化和高效的方式编写它方式?
在Python 2中,函数定义中return和yield一起出现错误。 但是对于Python 3.3中的这段代码
def f():
return 3
yield 2
x = f()
print(x.__next__())
没有错误,return与函数一起使用yield。 但是,当调用函数__next__
时,将引发StopIteration异常。 为什么不仅有返回值3
? 这个回报以某种方式被忽略了吗?
如何使用Sphinx-Napoleon在Google样式的文档字符串上指示列表类型,可选参数和生成器的返回类型?
我试过了
List[type]
list of type
Optional[type]
type, optional
和
Yields:
type:
分别; 但是所有生成的输出都不令人满意,这与生成的文档的其余部分不一致。 例如
Optional[type]
只是给
可选[类型]
没有type
的任何链接。
我尝试了所有内置主题,并且遇到了相同的问题。
我应该如何在Sphinx-Napoleon中使用Google样式的文档字符串记录这些元素?
有什么方法可以让生成器进入node.js?
我目前在用回调伪造它们,但我必须记住检查生成器函数内部的回调响应,这会产生大量if (callback(arg) === false) return;
我想要类似python的内容:
for p in primes():
if p > 100: break
do_something(p)
我正在像这样在节点中做的事情:
primes(function(p) {
if (p > 100) return false;
do_something(p)
});
也许像coffeescript这样的东西可以帮助您?
我想要一个函数.gi_running
,该函数的行为如下:
>>> def gen(): yield 0; yield 1
>>> a = gen()
>>> is_just_started(a)
True
>>> next(a)
0
>>> is_just_started(a)
False
>>> next(a)
1
>>> is_just_started(a)
False
>>> next(a)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
StopIteration
>>> is_just_started(a)
False
如何实现此功能?
我查看了.gi_running
属性,但它似乎用于其他用途。
如果我知道需要发送到生成器的第一个值,则可以执行以下操作:
def safe_send(gen, a):
try:
return gen.send(a)
except TypeError as e:
if "just-started" in e.args[0]:
gen.send(None)
return gen.send(a)
else:
raise
但是,这似乎很可恶。
PEP 342(通过增强型生成器进行协程)向生成器对象添加了throw()
方法,该方法允许调用方在生成器内引发异常(就像它是由yield
表达式抛出的一样)。
我想知道此功能的用例是什么。
考虑这种情况:
#!/usr/bin/env python # -*- coding: utf-8 -*- import os walk = os.walk('/home') for root, dirs, files in walk: for pathname in dirs+files: print os.path.join(root, pathname) for root, dirs, files in walk: for pathname in dirs+files: print os.path.join(root, pathname)
我知道这个示例有点多余,但是您应该考虑到我们需要多次使用相同的walk2 = walk
数据。 我有一个基准场景,必须使用相同的walk
数据才能获得有用的结果。
我已经尝试在第二个迭代中克隆和使用walk2 = walk
,但没有成功。 问题是...我该如何复制? 有可能吗?
先感谢您。
根据对这个问题的回答,C#中的yield break等同于python中的return。 在正常情况下,“返回”确实会停止发电机。 但是,如果您的函数只执行返回操作,则将获得None而不是空的迭代器,该迭代器由C#中的yield break返回
def generate_nothing():
return
for i in generate_nothing():
print i
您将收到TypeError:'NoneType'对象不可迭代。但是,如果我在返回前添加了永不运行的收益,则此函数将返回我期望的结果。
def generate_nothing():
if False: yield None
return
如果可行,但似乎有线。 谁有更好的主意?
谢谢,
我正在尝试编写一些代码来测试一堆输入参数的笛卡尔积。
我看过itertools
,但是它的product
功能并不是我想要的。 是否有一种简单明了的简单方法来制作一个字典,每个字典中有任意数量的键和任意数量的元素,然后生成具有下一个排列的字典?
输入:
options = {"number": [1,2,3], "color": ["orange","blue"] }
print list( my_product(options) )
输出示例:
[ {"number": 1, "color": "orange"},
{"number": 1, "color": "blue"},
{"number": 2, "color": "orange"},
{"number": 2, "color": "blue"},
{"number": 3, "color": "orange"},
{"number": 3, "color": "blue"}
]
我今天在浏览我的代码库,发现了这一点:
def optionsToArgs(options, separator='='):
kvs = [
(
"%(option)s%(separator)s%(value)s" %
{'option' : str(k), 'separator' : separator, 'value' : str(v)}
) for k, v in options.items()
]
return list(
reversed(
list(
(lambda l, t:
(lambda f:
(f((yield x)) for x in l)
)(lambda _: t)
)(kvs, '-o')
)
)
)
似乎要听一些参数,然后将它们变成shell命令的参数列表。 看来它正在生成器理解中使用yield,我认为这是不可能的...?
>>> optionsToArgs({"x":1,"y":2,"z":3})
['-o', 'z=3', '-o', 'x=1', '-o', 'y=2']
它是如何工作的?
我在Nodejs v0.11.2中使用了生成器,我想知道如何检查函数的自变量是生成器函数。
我找到了typeof f === 'function' && Object.getPrototypeOf(f) !== Object.getPrototypeOf(Function)
的这种方式,但是我不确定这是否很好(并且将来会继续工作)。
您对此问题有何看法?
有没有一种语法更简洁的方式编写以下内容?
gen = (i for i in xrange(10))
index = 5
for i, v in enumerate(gen):
if i is index:
return v
生成器应该具有gen[index]
表达式,这似乎是很自然的,该表达式充当列表,但在功能上与上述代码相同。
我知道yield将函数转换为生成器,但是yield表达式本身的返回值是多少? 例如:
def whizbang():
for i in range(10):
x = yield i
执行此函数时变量x
的值是什么?
我已经阅读了Python文档:[http://docs.python.org/reference/simple_stmts.html#grammar-token-yield_stmt],并且似乎没有提及yield表达式本身的值。