我正在寻找用于解释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)允许循环修改非序列迭代的内容(在有意义的情况下的子集中)
注意:
原始容器应修改;关键是不要创建一个新的容器。
我非常熟悉Loop C失败的原因:你真的不需要向我解释为什么它不起作用。我正在寻找的是编写Loop D的更好方法。
我不是试图修改任何循环中的整个容器,只是它的一些单独包含的元素:你不需要向我解释在循环它时修改整个容器是一个坏的想法。
我敏锐地意识到一般不可能修改iterables的元素。但在某些特定情况下,这是可能的,这就是b)的部分内容。
答案 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;太难看了,并且不依赖于项目设置的可用性。