【问题标题】:How to modify elements of iterables with iterators? I.e. how to get write-iterators in Python?如何使用迭代器修改可迭代对象的元素? IE。如何在 Python 中获取写迭代器?
【发布时间】:2011-06-21 22:44:40
【问题描述】:

我非常喜欢 Python 语法,但由于我来自 C++,所以我对 Python 中的迭代器一无所知。在 C++ 中有 2 种迭代器 - 常量和修改(非常量)。在 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 运算符发现的那样,它只是制作惰性副本(请参阅Wikipedia),所以有没有办法说我希望它是一个直接修改的迭代器,而不是使用整洁的迭代器

for variable in iterable_object:

语法?

【问题讨论】:

    标签: python iterator sequence immutability


    【解决方案1】:

    语法

    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 中是不可变的,所以你不能修改它们。

    【讨论】:

    • 嗯,您的帖子中有一些见解,但这不是我的意思。可能我应该使用单词更改或替换而不是修改,因为我想更改整个元素。您可以在我的示例代码中看到它。我现在可以更改元素的类型,但据我所知,它们都是“对象”,所以没关系。
    【解决方案2】:
    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]
    

    【讨论】:

    • 这更多是我想要的,但恐怕不是每个可迭代都必须有这个 [] 运算符,考虑一个图(它不是 python 标准库的一部分),什么意思 [ ] 有它的对象吗?此外,您的解决方案也不是很简单,尽管它接近我想要的。
    • 问题是,你想要的实际上是不可能的。任何对象都可以定义一个迭代器并让它随心所欲地运行。因此,没有通用的方法来修改值(或者更确切地说:替换)一个不可变对象,您从其源中的迭代器返回 - 如果甚至存在这样的“源”。 (如果您(本机示例:生成器),“迭代器”可以只生成值)。这就是为什么每个这样的解决方法都必须专门设计为以一种或另一种方式匹配您正在使用的迭代。 ofc,这限制了您交换对象的方式。
    • 我明白了,我只是假设有一些元素迭代器,它会返回一些对对象的“引用”,所以它可以被交换。无论如何,感谢您的澄清。
    • 在 Python 中,只有标识符/名称(变量)是对对象的引用。您不能让标识符总是指代不同的标识符所指的内容(想想:指针)。相反,两个标识符可以指向同一个对象,但它们本身的标识符永远不会连接。
    • 好吧,正如我所见,标识符是由对象(数据)“连接”的,它们指的是,就像当你写 *ptr = new_value 时,两个指针都得到新值(旧的被重写)。而这个数据替换(取消引用之后)是我在 Python 中所缺少的。
    【解决方案3】:

    这里的问题实际上不是迭代器在 python 中的工作方式的问题,而不是赋值在大多数 python 类型上的工作方式的问题。当询问者试图覆盖 lett 的值时,它确实是 ab[index] 的别名并且在逻辑上应该可以工作,这实际上不是正在发生的事情,而是 lett(引用或指针 lett,而不是它指向的值to) 被重新分配以指向它的新值的常量,这与覆盖它指向的内存位置的字节完全不同。这种工作方式是必要的,以允许 python 的鸭子类型在变量名称可以随着时间的推移指向具有不同大小的不同类型的情况下工作。请参阅此页面以了解有关此处发生的情况的更多说明:http://gestaltrevision.be/wiki/python/aliases

    我们可以实现的最接近的是手工制作的“可变整数”类型,它将允许它的基础值与 python 整数不同。但是,在这里手动讨论这一点没有什么意义,因为它已经在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
    

    【讨论】:

      【解决方案4】:

      类似的场景...

      >>> 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 方法仍然是正确的方法。

      【讨论】:

        猜你喜欢
        • 2018-08-19
        • 1970-01-01
        • 1970-01-01
        • 2016-03-26
        • 2021-11-21
        • 1970-01-01
        • 2021-11-14
        • 1970-01-01
        • 2022-01-21
        相关资源
        最近更新 更多