我一直在研究Ruby和Python生成器之间的相似之处/差异(Enumerators在Ruby中称为),并且据我所知,它们几乎相同.
然而,我注意到的一个区别是Python Generators支持一种close()方法,而Ruby Generators则不支持.从Python文档中可以看出该close()方法执行以下操作:
在生成器功能暂停的位置引发GeneratorExit.如果生成器函数然后引发StopIteration(通过正常退出,或者由于已经关闭)或GeneratorExit(通过不捕获异常),则关闭返回其调用者.
Ruby Enumerators不支持该close()方法有充分的理由吗?还是意外遗漏?
我还发现Ruby Enumerators支持一种rewind()方法,而Python生成器却没有......这也是有原因的吗?
谢谢
我有两个发电机说A()和B().我想一起迭代两个生成器.就像是:
for a,b in A(),B(): # I know this is wrong
#do processing on a and b
Run Code Online (Sandbox Code Playgroud)
一种方法是将两个函数的结果存储在列表中,然后遍历合并列表.像这样的东西:
resA = [a for a in A()]
resB = [b for b in B()]
for a,b in zip(resA, resB):
#do stuff
Run Code Online (Sandbox Code Playgroud)
如果您想知道,那么两个函数都会产生相同数量的值.
但是我不能使用这种方法因为A()/B()返回了这么多的值.将它们存储在列表中会耗尽内存,这就是我使用生成器的原因.
有没有办法一次循环两个发电机?
我正在尝试为顺序遍历编写递归生成器.
class Tree {
*inOrderTraversal() {
function* helper(node) {
if (node.left !== null) {
// this line is executed, but helper is not being called
helper(node.left);
}
yield node.value;
if (node.right !== null) {
helper(node.right);
}
}
for (let i of helper(this.root)) {
yield i;
}
}
// other methods omitted
}
Run Code Online (Sandbox Code Playgroud)
我这样称呼发电机:
const tree = new Tree();
tree.add(2);
tree.add(1);
tree.add(3);
for (let i of tree.inOrderTraversal()) {
console.log(i); // only prints 2
}
Run Code Online (Sandbox Code Playgroud)
为什么发电机只能屈服2?为什么它至少没有产生1之前2? …
从Python 3.3开始,如果生成器函数返回一个值,那么它将成为引发的StopIteration异常的值.这可以通过多种方式收集:
yield from表达式的值,暗示封闭函数也是一个生成器.next()或.send()中.但是,如果我只是想在for循环中迭代生成器 - 最简单的方法 - 似乎没有办法收集StopIteration异常的值,从而收集返回值.我使用一个简单的例子,其中生成器产生值,并在最后返回某种摘要(运行总计,平均值,时间统计等).
for i in produce_values():
do_something(i)
values_summary = ....??
Run Code Online (Sandbox Code Playgroud)
一种方法是自己处理循环:
values_iter = produce_values()
try:
while True:
i = next(values_iter)
do_something(i)
except StopIteration as e:
values_summary = e.value
Run Code Online (Sandbox Code Playgroud)
但这会抛弃for循环的简单性.我无法使用,yield from因为这需要调用代码本身就是一个生成器.有没有比上面显示的roll-one-own for循环更简单的方法?
结合@Chad S.和@KT的答案,最简单的似乎是使用迭代器协议将我的生成器函数转换为类:
class ValueGenerator():
def __iter__(self):
yield 1
yield 2
# and so on
self.summary = {...}
vg = ValueGenerator()
for i in vg:
do_something(i)
values_summary = vg.summary
Run Code Online (Sandbox Code Playgroud)
如果我不能重构价值生产者,@ Ferdinand Beyer的答案是最简单的.
是否可以在自定义类中运行nose测试生成器?我试图将示例转换为基于类的简单版本:
file: trial.py
>>>>>>>>>>>>>>
class ATest():
def test_evens(self):
for i in range(0, 5):
yield self.check_even, i, i * 3
def check_even(self, n, nn):
assert n % 2 == 0 or nn % 2 == 0
Run Code Online (Sandbox Code Playgroud)
这导致了
$ nosetests -v trial.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
Run Code Online (Sandbox Code Playgroud)
我查看了更改日志,并认为这应该从版本0.9.0a1开始工作.
我哪里错了?
这一点Python不起作用:
def make_incrementer(start):
def closure():
# I know I could write 'x = start' and use x - that's not my point though (:
while True:
yield start
start += 1
return closure
x = make_incrementer(100)
iter = x()
print iter.next() # Exception: UnboundLocalError: local variable 'start' referenced before assignment
Run Code Online (Sandbox Code Playgroud)
我知道如何解决这个错误,但请耐心等待:
这段代码工作正常:
def test(start):
def closure():
return start
return closure
x = test(999)
print x() # prints 999
Run Code Online (Sandbox Code Playgroud)
为什么我可以读取start闭包内的变量而不是写入它?导致这种start变量处理的语言规则是什么?
更新:我发现这个SO帖子相关(答案不仅仅是问题):读/写Python闭包
我有现有的代码,看起来类似于:
IEnumerable<SomeClass> GetStuff()
{
using (SqlConnection conn = new SqlConnection(connectionString))
using (SqlCommand cmd = new SqlCommand(sql, conn)
{
conn.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
SomeClass someClass = f(reader); // create instance based on returned row
yield return someClass;
}
}
}
Run Code Online (Sandbox Code Playgroud)
看来我可以通过使用获益reader.ReadAsync().但是,如果我只修改一行:
while (await reader.ReadAsync())
Run Code Online (Sandbox Code Playgroud)
编译器通知我await只能在标async有的方法中使用,并建议我修改方法签名为:
async Task<IEnumerable<SomeClass>> GetStuff()
Run Code Online (Sandbox Code Playgroud)
但是,这样做会导致GetStuff()无法使用,因为:
body
GetStuff()不能是迭代器块,因为Task<IEnumerable<SomeClass>>它不是迭代器接口类型.
我确信我错过了异步编程模型的关键概念.
问题:
ReadAsync()在我的迭代器中使用吗?怎么样?当不再使用发电机时,应该进行垃圾收集,对吧?我尝试了以下代码,但我不确定哪个部分我错了.
import weakref
import gc
def countdown(n):
while n:
yield n
n-=1
cd = countdown(10)
cdw = weakref.ref(cd)()
print cd.next()
gc.collect()
print cd.next()
gc.collect()
print cdw.next()
Run Code Online (Sandbox Code Playgroud)
在最后一行,我打电话给垃圾收集器,因为没有再打电话cd了.gc应该是自由的cd.但是当我打电话时cdw.next(),它仍然打印8.我再试了几次cdw.next(),它可以成功打印所有其余的,直到StopIteration.
我试过这个是因为我想了解生成器和协同程序是如何工作的.在David Beazley的PyCon演讲"关于协同程序和并发的好奇课程"的幻灯片28中,他说一个协程可以无限期地运行,我们应该用.close()它来关闭它.然后他说垃圾收集器会打电话.close().根据我的理解,一旦我们打电话给.close()自己,gc就会.close()再次打电话.会gc收到警告说它无法调用.close()已经关闭的协程吗?
谢谢你的任何投入.
我今天在node.js聚会,我遇到的人说node.js有es6生成器.他说,这是对回调式编程的巨大改进,并将改变节点格局.Iirc,他说了一些关于调用堆栈和异常的内容.
我查了一下它们,但还没有找到任何以初学者友好的方式解释它们的资源.什么是生成器的高级概述,与回调的不同(或更好?)有何不同?
PS:如果你能提供一段代码来强调常见场景的差异(发出http请求或db调用),那将非常有用.
generator ×10
python ×7
ecmascript-6 ×2
javascript ×2
ado.net ×1
async-await ×1
c# ×1
closures ×1
coroutine ×1
enumerator ×1
for-loop ×1
loops ×1
node.js ×1
nose ×1
recursion ×1
ruby ×1
try-catch ×1
yield ×1
yield-return ×1