如何用迭代器修改iterables的元素?即如何在Python中获得写迭代器?

时间:2011-06-21 22:44:41

标签: python iterator sequence immutability

我非常喜欢Python语法,但是由于我来自C ++,我在Python中没有得到关于迭代器的一点。在C ++中有两种迭代器 - 常量和修改(非常量)。在python中,似乎(对于我所见过的)只有第一种,如果你想修改元素,你必须使用索引,这对我来说感觉不舒服。 让我用一个简单的例子来说明:

ab = ["a", "b"]
for (index, lett) in enumerate(ab):
    print "Firstly, lett is ab[index]?", lett is ab[index]
    lett = str(index)
    print lett
    print ab[index]
    print "But after, lett is ab[index]?", lett is ab[index]

所以我无法使用迭代器修改列表。 它就像我在is运算符中发现的那样只是制作了Lazy副本(参见Wikipedia),所以有没有办法说我希望它是一个直接修改迭代器而不是使用整齐的

for variable in iterable_object:

语法?

4 个答案:

答案 0 :(得分:9)

语法

for x in iterable

不会创建任何惰性副本 - 它会将列表中的确切对象一个接一个地分配给x。如果这些对象是可变的,您可以修改它们:

a = [[1, 2], [3, 4]]
for x in a:
    x.append(5)
print a

打印

[[1, 2, 5], [3, 4, 5]]

您的示例使用字符串列表。字符串在Python中是不可变的,因此您无法修改它们。

答案 1 :(得分:3)

def iset(iterable):
    for i, x in enumerate(iterable):
        def setter(value):
            iterable[i] = value
        yield setter, x
a = range(10)
for set_x, x in iset(a):
    set_x(x * 2)
print a

打印

[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

答案 2 :(得分:0)

这里的问题实际上并不是迭代器如何在python中工作的问题,而是分配如何在大多数python类型上工作。当提问者试图覆盖lett的值时,这确实是ab [index]的别名并且看起来似乎逻辑上有效,这实际上并不是发生了什么,而是lett(引用或指针lett,而不是它指向的值) to)被重新分配以指向它的新值的常量,这与覆盖它指向的内存位置的字节完全不同。这种工作方式对于允许python的duck typing工作是必要的,其中变量名称可以随着时间的推移指向具有不同大小的不同类型。有关此处发生的更多解释,请参阅此页面:http://gestaltrevision.be/wiki/python/aliases

我们可以实现的最接近的是手工制作的“可变整数”类型,与python int不同,它允许它的基础值发生变化。然而,由于已经解释过in this question,因此在这里手动完成这一点没有什么意义。虽然问题完全不同,但它是同一个根本问题,解决方案同样可以接受。但是,如果您这样做,我会首先考虑您是否可以重新构建代码以避免这种情况,因为这是一种非常危险且容易出错的工作方式。

以下是来自an answer to the "increment int object" question的示例,有关如何一起解决此问题的完整说明,请参阅上面链接的问题,请注意此示例仅包括递减,其他运算符必须实现以同样的方式:

import sys
class FakeInt(int):
    def __init__(self, *arg, **kwarg):
        self._decr = False
        int.__init__(self, *arg, **kwarg)
    def __neg__(self):
        if self._decr:
            upLocals = sys._getframe(1).f_locals
            keys, values = zip(*upLocals.items())
            i = list(values).index(self)
            result = FakeInt(self-1)
            upLocals[keys[i]]=result
            return result
        self._decr = not self._decr
        return self

答案 3 :(得分:0)

类似的情况......

>>> a = b = 0
>>> a = 42
>>> a, b
(42, 0)

>>> a = b = [0]
>>> a[0] = 42
>>> a, b
([42], [42])

虽然python在内部使用引用,ab都指向同一个0对象,但a = 42替换a,而不是a enumerate引用。列表可以用作解决方法,但它绝不是优雅的。

迭代器就像一个,它是实际的东西,但没有办法“取消引用”,我可以想象添加这个功能会破坏其他一些东西。

我认为{{1}}方法仍然是正确的方法。