首先,我快速查看c ++样式迭代器。例如:
//--- Iterating over vector with iterator.
vector<int> v;
. . .
for (vector<int>::iterator it = v.begin(); it!=v.end(); ++it) {
cout << *it << endl;
}
很灵活。很容易改变底层容器类型。例如,您可能稍后决定插入和删除的数量是如此之高,以至于列表比矢量更有效。它还有许多有用的成员函数。向量的许多成员函数使用迭代器,例如,赋值,插入或擦除。此外,我们可以使用双向性(如果支持)bidirectionaly,例如++, - 。这对于像对象一样解析流非常有用。
python的问题是: 1:目前,python for循环语法的灵活性不如c ++ for。 (好吧,更安全) 2:而不是“it!= iter.end()”样式,当next()不再有时,python将抛出异常。它不灵活。
问题1:我的想法是否正确?
行。这是我的问题,如何实现像c ++迭代器一样强大的python迭代器?目前,python for循环语法不如c ++灵活。我还找到了一些可能的解决方案,例如http://www.velocityreviews.com/forums/t684406-pushback-iterator.html。但是它要求用户push_back一个东西,而不是问iterator - 。
问题2:在python中实现双向迭代器的最佳方法是什么?就像http://www.cplusplus.com/reference/std/iterator/BidirectionalIterator/一样。 伪代码如下:
it = v.begin();
while( it!=v.end()) {
//do sth here
if (condition1)
++it;//suppose this iterator supports ++
if(condition2)
--it;//suppose this iterator supports --
}
主要特点是:1)双向,2)更简单的“结束”检查。 “++”或“ - ”运算符或常用函数无关紧要(无论如何都没有语义差异)。
谢谢,
更新: 我从答案中得到了一些可能的解决方案:
i = 0
while i < len(sequence): # or i < len and some_other_condition
star_it = sequence[i]
if condition_one(star_it):
i += 1
if condition_two(star_it):
i = max(i - 1, 0)
但是,与数组不同,列表的随机访问应为O(n)。我想python内部的“list”对象是使用链接列表之类的东西实现的。因此,这种while循环解决方案效率不高。但是,在c ++中,我们有“随机迭代器”,“双向迭代器”。我该如何获得更好的解决方案?感谢。
答案 0 :(得分:5)
对于大多数情况,Python的for
和迭代器是最简单的事情。这是他们的目标,他们不应该为了灵活性而妥协 - 他们缺乏灵活性不是问题。
对于一些无法使用for
循环的情况,C ++迭代器可能更简单。但总有一种方法可以在Python中实现它,而不是使用C ++迭代器 更复杂。
如果你需要将迭代器与循环分开,只需使用while
循环:
it = iter(obj)
try:
while True: # or some secondary break condition other than StopIteration
star_it = next(it)
if condition_one(star_it):
star_it = next(it)
except StopIteration:
pass # exhausted the iterator
我只能想到--it
在Python中有意义的两种情况。
首先是你在迭代一个序列。在这种情况下,如果你需要倒退,根本不要使用迭代器 - 只需使用带while
循环的计数器:
i = 0
while i < len(sequence): # or i < len and some_other_condition
star_it = sequence[i]
if condition_one(star_it):
i += 1
if condition_two(star_it):
i = max(i - 1, 0)
第二个是你在迭代一个双向链表。在这种情况下,再次,不要使用迭代器 - 只是正常遍历节点:
current = node
while current: # or any break condition
if condition_one(current):
current = current.next
if condition_two(star_it):
current = current.prev
可能认为有意义的情况,但您不能使用上述任何一种方法,使用无序集合,例如set
或dict
。但是,--it
在这种情况下没有意义。由于集合在语义上是无序的,因此先前到达的任何项目都是合适的 - 而不仅仅是实际的先前项目。
因此,为了知道要返回的正确对象,您需要通过迭代mydict.values()
或tuple(myset)
之类的序列并使用计数器,或者通过组合序列来获取内存之前的值,并使用while
循环和next
,而不是for
循环。
答案 1 :(得分:1)
您提到的一些情况的解决方案:
您想要替换基础容器中的对象。对于词典,迭代键或项,而不仅是值:
for key, value in my_dict.iteritems():
if conditiion(value):
my_dict[key] = new_value
对于列表,请使用enumerate()
:
for index, item in enumerate(my_list):
if condition(item):
my_list[index] = new_item
您想要一个具有一个“预见”值的迭代器。你可能会使用适合特定情况的东西,但这是一般情况的配方:
def iter_with look_ahead(iterable, sentinel=None):
iterable, it_ahead = itertools.tee(iterable)
next(it_ahead, None)
return izip_longest(iterable, it_ahead, fillvalue=sentinel)
for current, look_ahead in iter_with look_ahead(tokens):
# whatever
您想要反向迭代。对支持它的容器使用reversed()
。
您想要随机访问。只需将您的iterable转换为列表并使用索引:
my_list = list(my_iterable)
答案 2 :(得分:0)
实际上,C ++迭代器系统并不是那么好。迭代器类似于指针,他们有他们的困境:
v.end()
无法安全取消引用std::for_each(end, begin, func);
std::for_each(v0.begin(), v2.end(), func);
Python方法在这方面要好得多(尽管一开始使用异常非常令人惊讶,它确实有助于定义嵌套迭代器),因为与其名称相反,Python迭代器更类似于{{1} }。
Range
的概念比C ++ 11更好地引入了range-for循环结构:
Range
使用迭代器可以实现任何可能的范围,尽管可能需要一些时间来实现它,并且对于我们这些受过C ++指针式迭代器教育的人来说,一些翻译似乎是超现实主义者。例如,可以完美地表达子范围:
for (Object& o: range) {
}
其中for (Object& o: slice(range, 2, 9)) {
}
将slice
内的所有元素放在[2, 9)
位置。
因此,你应该深入研究它并接受它的风格,而不是与你的语言(Python)作斗争。与语言作斗争通常是一场失败的战斗,学习它的习语,变得高效。
答案 3 :(得分:0)
您可以使用python对象实现类似的C ++方式:
class Iterable(object):
class Iterator(object):
def __init__(self, father, pos=0):
self.father = father
self.pos = pos
def __getitem__(self, pos=0):
return self.father[self.pos + pos]
def __setitem__(self, pos, value):
self.father[self.pos + pos] = value
def __iadd__(self, increment):
self.pos += increment
return self
def __isub__(self, decrement):
self.pos -= decrement
return self
def __ne__(self, other):
return self.father != other.father or self.pos != other.pos
def __eq__(self, other):
return not (self != other)
def begin(self):
return self.Iterator(self)
def end(self):
return self.Iterator(self, len(self))
class Vector(list, Iterable):
pass
v = Vector([54, 43, 32, 21])
counter = 0
it = v.begin()
print it, it[0]
while it != v.end():
counter += 1
print it[0]
if counter == 2:
it += 1; # suppose this iterator supports ++
if counter == 1:
it -= 1; # suppose this iterator supports --
it += 1
这会将*it
替换为it[0]
(也类似于C ++),将it++
替换为it += 1
,但实际上它几乎保持不变。
如果你这样做,你会离开Pythonic方式; - )
答案 4 :(得分:0)
请注意,Python中的列表对象是一个数组,因此问题中提到的效率问题实际上不是问题。