gpt4 book ai didi

python - 我可以从实例方法中产生吗

转载 作者:太空宇宙 更新时间:2023-11-03 11:00:29 25 4
gpt4 key购买 nike

在类的实例方法中使用 yield 语句可以吗?例如,

# Similar to itertools.islice
class Nth(object):
def __init__(self, n):
self.n = n
self.i = 0
self.nout = 0

def itervalues(self, x):
for xi in x:
self.i += 1
if self.i == self.n:
self.i = 0
self.nout += 1
yield self.nout, xi

Python 对此并不提示,简单的案例似乎也能奏效。但是,我只看到了常规函数产生 yield 的示例。

当我尝试将它与 itertools 函数一起使用时,我开始遇到问题。例如,假设我有两个大数据流 X 和 Y,它们存储在多个文件中,我想通过一次数据循环来计算它们的和与差。我可以像下图那样使用 itertools.teeitertools.izip

data flow

在代码中它会是这样的(抱歉,它很长)

from itertools import izip_longest, izip, tee
import random

def add(x,y):
for xi,yi in izip(x,y):
yield xi + yi

def sub(x,y):
for xi,yi in izip(x,y):
yield xi - yi

class NthSumDiff(object):
def __init__(self, n):
self.nthsum = Nth(n)
self.nthdiff = Nth(n)

def itervalues(self, x, y):
xadd, xsub = tee(x)
yadd, ysub = tee(y)
gen_sum = self.nthsum.itervalues(add(xadd, yadd))
gen_diff = self.nthdiff.itervalues(sub(xsub, ysub))
# Have to use izip_longest here, but why?
#for (i,nthsum), (j,nthdiff) in izip_longest(gen_sum, gen_diff):
for (i,nthsum), (j,nthdiff) in izip(gen_sum, gen_diff):
assert i==j, "sum row %d != diff row %d" % (i,j)
yield nthsum, nthdiff

nskip = 12
ns = Nth(nskip)
nd = Nth(nskip)
nsd = NthSumDiff(nskip)
nfiles = 10
for i in range(nfiles):
# Generate some data.
# If the block length is a multiple of nskip there's no problem.
#n = random.randint(5000, 10000) * nskip
n = random.randint(50000, 100000)
print 'file %d n=%d' % (i, n)
x = range(n)
y = range(100,n+100)
# Independent processing is no problem but requires two loops.
for i, nthsum in ns.itervalues(add(x,y)):
pass
for j, nthdiff in nd.itervalues(sub(x,y)):
pass
assert i==j
# Trying to do both with one loops causes problems.
for nthsum, nthdiff in nsd.itervalues(x,y):
# If izip_longest is necessary, why don't I ever get a fillvalue?
assert nthsum is not None
assert nthdiff is not None
# After each block of data the two iterators should have the same state.
assert nsd.nthsum.nout == nsd.nthdiff.nout, \
"sum nout %d != diff nout %d" % (nsd.nthsum.nout, nsd.nthdiff.nout)

但这会失败,除非我将 itertools.izip 换成 itertools.izip_longest,即使迭代器具有相同的长度。这是最后一个被命中的断言,输出如下

file 0 n=58581
file 1 n=87978
Traceback (most recent call last):
File "test.py", line 71, in <module>
"sum nout %d != diff nout %d" % (nsd.nthsum.nout, nsd.nthdiff.nout)
AssertionError: sum nout 12213 != diff nout 12212

编辑:我想从我写的例子来看这并不明显,但输入数据 X 和 Y 仅在 block 中可用(在我的实际问题中,它们在文件中分块)。这很重要,因为我需要维护 block 之间的状态。在上面的玩具示例中,这意味着 Nth 需要产生等同于

>>> x1 = range(0,10)
>>> x2 = range(10,20)
>>> (x1 + x2)[::3]
[0, 3, 6, 9, 12, 15, 18]

不等同于

>>> x1[::3] + x2[::3]
[0, 3, 6, 9, 10, 13, 16, 19]

我可以使用 itertools.chain 提前加入 block ,然后调用 Nth.itervalues,但我想了解有什么问题在调用之间的 Nth 类中维护状态(我的真实应用程序是涉及更多保存状态的图像处理,而不是简单的 Nth/add/subtract)。

我不明白当它们的长度相同时,我的 Nth 实例如何以不同的状态结束。例如,如果我给 izip 两个等长的字符串

>>> [''.join(x) for x in izip('ABCD','abcd')]
['Aa', 'Bb', 'Cc', 'Dd']

我得到相同长度的结果;为什么我的 Nth.itervalues 生成器似乎得到了不相等数量的 next() 调用,即使每个生成器产生相同数量的结果?

最佳答案

Gist repo with revisions | Quick link to solution

快速回答

您永远不会在 class Nth 中重置 self.iself.nout。另外,你应该使用这样的东西:

# Similar to itertools.islice
class Nth(object):
def __init__(self, n):
self.n = n

def itervalues(self, x):
for a,b in enumerate(islice(x, self.n - 1, None, self.n)):
self.nout = a
yield a,b

但是因为你甚至不需要nout,你应该使用这个:

def Nth(iterable, step):
return enumerate(itertools.islice(iterable, step - 1, None, step))

长答案

你的代码有一种不一致的味道,这让我想到了 NthSumDiff.itervalues() 中的这一行:

for (i,nthsum), (j,nthdiff) in izip(gen_sum, gen_diff):

如果你交换 gen_sumgen_diff,你会发现 gen_diff 永远是 nout > 大一。这是因为 izip() 在从 gen_diff 拉取之前从 gen_sum 拉取。 gen_sumgen_diff 甚至在最后一次迭代中尝试之前引发 StopIteration 异常。

例如,假设您选择了 N 个样本,其中 N % step == 7。在每次迭代结束时,第 N 个实例的 self.i 应该等于 0。但是在最后一次迭代中, gen_sum 中的 self.i 将增加到 7,然后 x 中将不再有元素。它将引发 StopIteration。不过,gen_diff 仍然位于 self.i 等于 0。

如果将 self.i = 0self.nout = 0 添加到 Nth.itervalues() 的开头,问题就会消失。

类(class)

你遇到这个问题只是因为你的代码太复杂而且不是 Pythonic。如果您发现自己在循环中使用大量计数器和索引,那么这是一个好兆头(在 Python 中)退后一步,看看您是否可以简化您的代码。我有很长的 C 编程历史,因此,我仍然不时发现自己在 Python 中做同样的事情。

更简单的实现

言出必行......

from itertools import izip, islice
import random

def sumdiff(x,y,step):
# filter for the Nth values of x and y now
x = islice(x, step-1, None, step)
y = islice(y, step-1, None, step)
return ((xi + yi, xi - yi) for xi, yi in izip(x,y))

nskip = 12
nfiles = 10
for i in range(nfiles):
# Generate some data.
n = random.randint(50000, 100000)
print 'file %d n=%d' % (i, n)
x = range(n)
y = range(100,n+100)
for nthsum, nthdiff in sumdiff(x,y,nskip):
assert nthsum is not None
assert nthdiff is not None
assert len(list(sumdiff(x,y,nskip))) == n/nskip

问题的更多解释

回应 Brian 的评论:

This doesn't do the same thing. Not resetting i and nout is intentional. I've basically got a continuous data stream X that's split across several files. Slicing the blocks gives a different result than slicing the concatenated stream (I commented earlier about possibly using itertools.chain). Also my actual program is more complicated than mere slicing; it's just a working example. I don't understand the explanation about the order of StopIteration. If izip('ABCD','abcd') --> Aa Bb Cc Dd then it seems like equal-length generators should get an equal number of next calls, no? – Brian Hawkins 6 hours ago

你的问题太长了,我错过了关于来自多个文件的流的部分。让我们看看代码本身。首先,我们需要真正清楚 itervalues(x) 的实际工作原理。

# Similar to itertools.islice
class Nth(object):
def __init__(self, n):
self.n = n
self.i = 0
self.nout = 0

def itervalues(self, x):
for xi in x:
# We increment self.i by self.n on every next()
# call to this generator method unless the
# number of objects remaining in x is less than
# self.n. In that case, we increment by that amount
# before the for loop exits normally.
self.i += 1
if self.i == self.n:
self.i = 0
self.nout += 1
# We're yielding, so we're a generator
yield self.nout, xi
# Python helpfully raises StopIteration to fulfill the
# contract of an iterable. That's how for loops and
# others know when to stop.

在上面的 itervalues(x) 中,对于每个 next() 调用,它都会在内部将 self.i 递增 self。 n 然后产生 OR 它将 self.i 递增 x 中剩余的对象数,然后退出 for 循环,然后退出生成器(itervalues( ) 是一个生成器,因为它产生)。当 itervalues() 生成器退出时,Python 会引发 StopIteration 异常。

因此,对于用 N 初始化的 class Nth 的每个实例,self.i 在耗尽 itervalues(X) 中的所有元素后的值> 将是:

self.i = value_of_self_i_before_itervalues(X) + len(X) % N

现在,当您遍历 izip(Nth_1, Nth_2) 时,它将执行如下操作:

def izip(A, B):
try:
while True:
a = A.next()
b = B.next()
yield a,b
except StopIteration:
pass

因此,假设 N=10len(X)=13。在对 izip() 的最后一次 next() 调用中,A 和 B 的状态都是 self.i==0A.next() 被调用,递增 self.i += 3,用完 X 中的元素,退出 for 循环,返回,然后 Python 引发 >停止迭代。现在,在 izip() 中,我们直接进入异常 block ,完全跳过 B.next()。所以,A.i==3B.i==0 在最后。

第二次尝试简化(具有正确的要求)

这是将所有文件数据视为一个连续流的另一个简化版本。它使用链式、小型、可重复使用的生成器。我会非常非常推荐观看这个 PyCon '14 talk about generators by David Beazley .从你的问题描述来看,应该是100%适用的。

from itertools import izip, islice
import random

def sumdiff(data):
return ((x + y, x - y) for x, y in data)

def combined_file_data(files):
for i,n in files:
# Generate some data.
x = range(n)
y = range(100,n+100)
for data in izip(x,y):
yield data

def filelist(nfiles):
for i in range(nfiles):
# Generate some data.
n = random.randint(50000, 100000)
print 'file %d n=%d' % (i, n)
yield i, n

def Nth(iterable, step):
return islice(iterable, step-1, None, step)

nskip = 12
nfiles = 10
filedata = combined_file_data(filelist(nfiles))
nth_data = Nth(filedata, nskip)
for nthsum, nthdiff in sumdiff(nth_data):
assert nthsum is not None
assert nthdiff is not None

关于python - 我可以从实例方法中产生吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33818422/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com