简体   繁体   中英

Incrementing iterator (iter) in Python

I have the following string for example:

s = "string"

I'm trying to maintain an iterator to this string object.

it = iter(s)

I know that I can use a loop and increment the it using next call

for i in range(0, len(s)):
    print(next(it))

This is going to print all the characters in this string.

But I have couple of things that I would like the iterator to do as I do using C++ iterator on std::list .

1.) To return the element that iterator currently points to.

   // In C++, I would do,

   std::string str = "string";

   std::string::iterator it = str.begin();
   *it; // gets the value of element

2.) To get the index of element the iterator is at.

So that I'm able to get the sub-string using Python slicing method:

Eg:

s = "string"
s[0:iter_index_in_integer]

In C++, I could use iterators:

   std::string str = "string";
   std::string::iterator it = str.begin();
   std::string(it, it+3);

Is it possible to increment the iterator in Python like that? If not then I could use the index of element the iterator currently points to and how would I do that?

Python's built-in do not provide anything that allows you to get the "current element" of an iterator. The only operation that an iterator must support is next to get the following element and advance the iteration.

However it's quite simple to write your own iterator that implements other operations:

class AugmentedIterator(object):
    _sentinel = object()

    def __init__(self, iterator):
        self.iterator = iterator
        self.value = self._sentinel

    def __iter__(self):
        return self

    def __next__(self):
        if self.value is not self._sentinel:
            self.value, val = self._sentinel, self.value
            return val
        return next(self.iterator)

    def peek(self, default=_sentinel):
        if self.value is not self._sentinel:
            return self.value
        try:
            self.value = next(self.iterator)
        except StopIteration:
            if default is not self._sentinel:
                return default
            raise
        else:
            return self.value

Now, given any iterator it you can wrap it into an AugmentedIterator and whenever you want you can call peek() to check the current element.

Note that there is no such thing as end() , since iterators can easily be infinite. The only way to know whether the iterator doesn't have any more elements is to call next and see if it raises a StopIteration .

For your second request you can use itertools.islice to get a slice of the iterator. Note however that islice does python slicing, which allows out-of-bounds indexes:

In [19]: list(islice('string', 0, 1000))
Out[19]: ['s', 't', 'r', 'i', 'n', 'g']

Here the index 1000 since it's bigger then the string length is just taken to mean: until the end. This is consistent with slicing:

In [20]: 'string'[:1000]
Out[20]: 'string'

In C++ you'd get an error trying to use it+1000 if the string has length 6. (Not 100% sure, but since pointers are iterables you'd surely get some troubles at least in some circumstances).


In general the itertools package contains quite a few useful functions (at the end of the documentation there are some recipes using them). Python also provides some iterator related functions such as:

  • enumerate : to iterate over elements and get the relative indices.
  • map / filter / reduce
  • iter(callable, sentinel) : allows you to obtain an iterator given a function without arguments:

     for chunk in iter(lambda: file_object.read(4096), ''): handle(chunk) 

    Is equivalent to:

     while True: chunk = file_object.read(4096) if chunk == '': break handle(chunk) 

You can use built-in enumerate() function:

for index, element in enumerate(mystring):
    print index
    print element

You could do something like:

import itertools
s = "mystring"
s2 = ''.join(itertools.islice(s, 0, 3))

But really, rather than attempt to directly translate C++ idioms, you should find the Pythonic way to do whatever it is you want, which will probably end up being more concise anyway.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM