Deuglify元素变异Python for循环

时间:2014-11-06 12:18:45

标签: python for-loop

我正在寻找用于解释Python for循环的习惯用法,这些循环代替迭代中迭代的(而不是mutate)元素。具体来说,请考虑以下四个(for-)循环:

循环A:由C程序员编写的只读Python循环

for i in range(len(iterable)):
    print(iterable[i])

循环B:由Python程序员创建的只读Python循环

for e in iterable:
    print(e)

循环C:尝试编写由经验不足的Python程序员编写的变异Python循环失败

for e in iterable:
    if condition(e):
        e = new_value(e)

循环D:循环C的修复,它可以恢复到循环A的丑陋风格

for i in range(len(iterable)):
    if condition(iterable[i]):
        iterable[i] = new_value((iterable[i]))

除了令人讨厌的丑陋之外,循环D对于非序列迭代也失败了。

我正在寻找成语和技巧

  • a)减轻循环D的丑陋

  • b)允许循环修改非序列迭代的内容(在有意义的情况下的子集中)

注意:

  1. 原始容器应修改;关键是不要创建一个新的容器。

  2. 非常熟悉Loop C失败的原因:你真的不需要向我解释为什么它不起作用。我正在寻找的是编写Loop D的更好方法。

  3. 我不是试图修改任何循环中的整个容器,只是它的一些单独包含的元素:你不需要向我解释在循环它时修改整个容器是一个坏的想法。

  4. 我敏锐地意识到一般不可能修改iterables的元素。但在某些特定情况下,这是可能的,这就是b)的部分内容。

1 个答案:

答案 0 :(得分:1)

回答我自己的问题,我提出了一个受enumerate启发的协议(称之为 the muteration protocol ),并允许将这些循环写入这种方式并不是那么难看。使用该协议的循环如下所示:

for setit, e in muterate(iterable):
    if e%2:
        setit(1000*e)

就像enumerate一样,muterate返回一个产生对的迭代:enumerate索引被替换为setter函数,该函数接受替换它的新值容器中的原始值。 muterate适用于实现muterate方法的任何类型的iterable,就像iter适用于实现适当__iter__方法的任何类型一样。这意味着对于任何支持静音的类型,可以用以相同的方式写入静音循环。

以下是概念验证实现。不能说我对此过于贪婪,但也许这是朝着正确方向迈出的一步。

from functools import partial
from itertools import chain

# The global muterate function which dispatches to the various
# implementations
def muterate(it):
    try:
        return muterate.builtins[type(it)](it)
    except KeyError:
        try:
            return it.muterate()
        except AttributeError:
            raise TypeError('No muterator available for {}'.format(type(it)))
muterate.builtins = {}

# As I can't implement the muterate method on the builtins, these have
# to be installed externally in some place where muterate can find
# them
def make_muterator(get_iterator):
    def muterator(container):
        def make_setter(locator):
            def setter(new_value):
                container[locator] = new_value
            return setter
        for locator, value in get_iterator(container):
            yield make_setter(locator), value
    return muterator
muterate.builtins[list] = make_muterator(enumerate)
muterate.builtins[dict] = make_muterator(dict.items)

# An iterable, mutable type which does not (AND SHOULD NOT) support
# item setting.
def make_linked_list(data):
    the_list = EmptyList()
    for datum in data:
        the_list = LinkedList(datum, the_list)
    return the_list

class EmptyList:

    def __iter__(self):
        return; yield

    def muterate(self):
        return; yield

class LinkedList:

    def __init__(self, head, tail):
        self._head = head
        self._tail = tail

    def __iter__(self):
        return chain((self._head,), self._tail)

    def muterate(self):
        def setter(new_value):
            self._head = new_value
        return chain(((setter, self._head),), self._tail.muterate())

    def __repr__(self):
        return "<{}>".format(', '.join(map(str,tuple(self))))
    __str__ = __repr__

# Putting the muterate protocol through its paces
l = range(10)
d = {letter:rank for (rank, letter) in enumerate('abcdefghij')}
ll = make_linked_list(reversed(range(10)))
for iterable in (l,d,ll):
    for setit, e in muterate(iterable):
        if e%2:
            setit(1000*e)
    print iterable

给出了输出:

[0, 1000, 2, 3000, 4, 5000, 6, 7000, 8, 9000]
{'a': 0, 'c': 2, 'b': 1000, 'e': 4, 'd': 3000, 'g': 6, 'f': 5000, 'i': 8, 'h': 7000, 'j': 9000}
<0, 1000, 2, 3000, 4, 5000, 6, 7000, 8, 9000>

换句话说,所有奇数元素都被原始容器中的1000 乘以一个静音循环,对于所涉及的所有类型看起来都是相同的,isn&# 39;太难看了,并且不依赖于项目设置的可用性。