简体   繁体   中英

How do I yield a character from a string

I would like to have a callable function (because iter() needs it) that gives me a sequential character from a string.

This function has stalled my pc, I had to reset the computer.

cnt=0
def myfunc():
    global cnt
    cnt+=1
    yield "abczdef"[cnt]

list(iter(myfunc,'z'))

You can use the string as an iterable directly:

def characters(string, sentinel):
    for c in string:
        if c == sentinel:
            break
        yield c
>>> list(characters('abczdef', 'z'))
['a', 'b', 'c']

A more functional approach, like you had in mind, would be:

from itertools import takewhile

def characters(string, sentinel):
    return takewhile(lambda c: c != sentinel, string))

Using the iter(callable, sentinel) form can be made to work, too, but I find the other solutions more readable:

def characters(string, sentinel):
    chars = iter(string)
    return iter(lambda: next(chars), sentinel)

Why your attempt freezes the computer

 list(iter(myfunc,'z')) 

is equivalent to:

result = []
while True:
    x = myfunc()
    if x == 'z':
        break
    result.append(x)

If you look at what happens when you call myfunc() :

>>> myfunc()
<generator object myfunc at 0x7f1e0f1020a0>
>>> myfunc()
<generator object myfunc at 0x7f1e0f1020f8>
>>> myfunc()
<generator object myfunc at 0x7f1e0f1020a0>

you see that it creates a new generator object each time it is called.

Obviously none of these objects is equal to 'z' , so this creates an infinite loop.

With the syntax iter(f, sentinel) , the first argument f must be a callable object that return s the result , not yield -ing it.

From Python documentation :

If the second argument, sentinel , is given, then object must be a callable object. The iterator created in this case will call object with no arguments for each call to its __next__() method;

So, this is the answer you're looking for:

cnt=0

def myfunc():
    global cnt
    cnt += 1
    return "abczdef"[cnt]
#   ^^^^^^

list(iter(myfunc, 'z'))

Which, as expected, gives ['b', 'c'] as the result (note on the absence of 'a' : you incremented cnt before using it as the string index, so you're never accessing "abczdef"[0] ):

Python 3.6.7 (default, Oct 22 2018, 11:32:17)
[GCC 8.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> cnt=0
>>> def myfunc():
...     global cnt
...     cnt += 1
...     return "abczdef"[cnt]
...
>>> list(iter(myfunc, 'z'))
['b', 'c']
>>>

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