如何修改 python 中生成器的最后一个元素?

How to modify last element of a generator in python?

我有一个生成器,我想修改生成器的最后一个元素。 我想用另一个元素替换最后一个元素。我知道如何检索最后一个元素,但不知道如何修改它。

解决这个问题的最佳方法是什么?

有关更多上下文,这就是我想要做的:

for child in alexnet.children():
    for children_of_child in child.children():
         print(children_of_child);

我的发电机 object 是:children_of_child 第二个 child 它的所有 children 是:

Dropout(p=0.5)
Linear(in_features=9216, out_features=4096, bias=True)
ReLU(inplace)
Dropout(p=0.5)
Linear(in_features=4096, out_features=4096, bias=True)
ReLU(inplace)
Linear(in_features=4096, out_features=1000, bias=True)

我想用我自己的回归网替换最后一层Linear(in_features=4096, out_features=1000, bias=True)。 `

由于您使用的是相当小的列表(就 RAM 而言,即使 ResNet-150 也是 "reasonably small"),我会使其易于理解和维护。没有 "obvious" 方法可以检测到您离耗尽发电机还差一步。

  1. 耗尽当前发电机,列出其输出。
  2. 根据需要替换最后一个元素。
  3. 围绕这个改变的列表包装一个新的生成器。

"nice" (?) 方法是编写一个包装器生成器,在原始文件中使用单元素前瞻:在每次调用 N 时,您已经 在包装器中包含 元素N。您从 "real" 生成器(您发布的代码)中获取元素 N+1。如果该元素存在,那么您通常 return 元素 N。如果该生成器已用完,则将最后一个元素替换为您想要的元素,然后 return 进行更改。

示例:

为简单起见,我使用 range 代替了您原来的生成器。

def new_tail():
    my_list = list(range(6))
    my_list[-1] = "new last element"
    for elem in my_list:
        yield elem

for item in new_tail():
    print(item)

输出:

0
1
2
3
4
new last element

有帮助吗?

执行此操作的方法是向前迭代一步,并在进行时跟踪前一个值。对于每个值,产生前一个值。当你到达终点时,不是产生最后一个先前的值,而是产生替换值:

def new_tail(it, tail):
    sentinel = prev = object()
    for value in it:
        if prev is not sentinel:
            yield prev
        prev = value
    yield tail

或者您可以特殊处理第一个元素而不是使用标记:

def new_tail(it, tail):
    it = iter(it)
    prev = next(it)
    for value in it:
        yield prev
        prev = value
    yield tail

您可能想考虑一个完全空的迭代器会发生什么。我不确定您是想不产生任何结果、产生替换值还是引发异常。第一个版本产生替代价值;第二个......好吧,它应该引发异常,但从 3.7 开始,它发出 DeprecationWarning 并且什么都不产生,这可能不是你想要的行为。

无论如何,您可以使用具有 sentinel 默认值的 next,或 except StopIteration: next。然后你想做的三件事都很容易。


但是,如果您更抽象地考虑它,则可以使它变得更简单:如果您拥有所有相邻的元素对,那么每对元素中的第一个元素都会为您提供除最后一个元素之外的所有元素。所以,使用 the pairwise recipe from the itertools docs:

def new_tail(it, tail):
    for x, _ in pairwise(it):
        yield x
    yield tail

或者,如果您愿意,您甚至可以使用 itertools.chain and operator.itemgetter 将其设为单个表达式,尽管这可能有点傻:

def new_tail(it, tail):
    return chain(map(itemgetter(0), pairwise(it)), (tail,))