回答我的问题,我想出了这是由enumerate
启发,并允许写入的方式,是不是所有的丑这样的循环协议(称之为的muteration协议) 。使用协议A环是这样的:
for setit, e in muterate(iterable):
if e%2:
setit(1000*e)
就像enumerate
,muterate
返回一个可迭代产生对:enumerate
“s指数被替换为setter函数,它接受与它将替换的新值容器中的原始值。muterate
将适用于实施muterate
方法的任何类型的迭代,就像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>
换句话说,所有的奇数元素已被muterating环,它看起来相同所有乘以1000 原始容器内涉及的类型不是太难看,也不依赖于项目设置的可用性。
“允许循环修改**非序列可迭代**的内容”,我认为可迭代序列是序列并且没有**非序列可迭代**。 – 2014-11-06 12:32:46
@VishnuUpadhyay:我怀疑OP会喜欢一种通用的技术,允许遍历字典和其他没有简单整数索引的映射。 – 2014-11-06 13:07:19
@VishnuUpadhyay下面是一些可迭代的例子,但不是序列:'open('/ etc/passwd')','dict(a = 7,b = 3)','iter('hello') ','def x():yield 1',接着是'x()','itertools.cycle()'等等,这些例子都不适合在循环中修改;这将需要比评论允许的更多空间。虽然有人可能会争辩说,基于“dict(a = 1,b = 2).values()”的东西是候选人。 – jacg 2014-11-06 16:49:30