2
\$\begingroup\$

In python, I sometimes write a while statement like

i = 0
while <condition that involves i>:
    i += 1

The goal is, of course, to check the number of time the condition is true. I was wondering if there's a more simple, pythonic way to do it ? something like i = while <condition that involves i>, maybe with the walrus operator ?

Here's a more concrete example:

def get_word_num_with_character_index(string: str, index: int) -> int:
    """
    returns the number of the word containing a character, specified by an index

    :param string: the string to analyze
    :param index: the index of the character
    :return: the number of the word containing the specified character
    """

    split_string = string.split(" ")

    i = 0
    while len(" ".join(split_string[:i])) <= index:
        i += 1

    return i - 1


print(get_word_num_with_character_index("jean de la fontaine", 8))
# prints 2, the index of the word containing the 8th character

How to simplify it ?

\$\endgroup\$
8
  • \$\begingroup\$ You can use the walrus operator here, but I don't think you should: i = -1, while len(" ".join(split_string[:(i := i + 1)])) <= index: pass. It's neither simpler nor clearer, it just moves even more logic into the condition without saving a line. \$\endgroup\$ Aug 31 at 11:07
  • \$\begingroup\$ Just to confirm, you're not looking into using enumerate (returns a counter as part of a for loop) or using slicing, such as "jean de la fontaine"[8] - correct? \$\endgroup\$
    – C. Harley
    Aug 31 at 11:09
  • \$\begingroup\$ thanks @riskypenguin, but as you pointed out, it only makes the code more complex, so it's not really what I'm looking for. I was just mentioning the walrus operator as a possible idea \$\endgroup\$
    – Kyrela
    Aug 31 at 11:34
  • \$\begingroup\$ @C.Harley no, this is just an exemple but i'm trying to get the value of i in only one line, using the given condition, in order to get the number of the word in the string where the specified letter is (here, ̀"jean de la fontaine"[8]`). Sorry, I know it's not very clear but I don't see how I can explain the function more clearly than in its docstring \$\endgroup\$
    – Kyrela
    Aug 31 at 11:44
  • 1
    \$\begingroup\$ sum(1 for _ in itertools.takewhile(lambda i: PREDICATE, itertools.count())) \$\endgroup\$ Aug 31 at 11:55
6
\$\begingroup\$

You could replace your

    i = 0
    while len(" ".join(split_string[:i])) <= index:
        i += 1

with the oneliner using itertools.count

    i = next(i for i in count() if len(" ".join(split_string[:i])) > index)

or more readably spread over two physical lines (still just one logical line:

    i = next(i for i in count()
             if len(" ".join(split_string[:i])) > index)

Both have advantages. The while solution has less clutter, the next solution makes it clearer that the whole point of the snippet is to compute i.

If you already have a function for the condition, a good alternative would be itertools.dropwhile:

    def condition(i):
        return len(" ".join(split_string[:i])) <= index

    i = next(dropwhile(condition, count()))

As a oneliner, just to see how long/ugly it is in this case:

    i = next(dropwhile(lambda i: len(" ".join(split_string[:i])) <= index, count()))

So I'd likely really only use it if I had it as a function already.

\$\endgroup\$
4
  • \$\begingroup\$ Thanks a lot! itertools.count() is what I needed right now, but I keep dropwhile() in mind, it is also very relevant. I'm always open to other ideas. But the word "could" in your message suggests that this is not a good idea. It's true that it's not very clear. So you recommend not to use it, but to leave the "while" as it is? \$\endgroup\$
    – Kyrela
    Aug 31 at 13:00
  • \$\begingroup\$ I meant that as there's nothing wrong with the while loop. In this particular case, I'm undecided which I prefer, while or next. So you could switch, but you really don't have to. \$\endgroup\$ Aug 31 at 13:05
  • \$\begingroup\$ Ok, I will keep this in mind, thank you ! \$\endgroup\$
    – Kyrela
    Aug 31 at 13:09
  • 2
    \$\begingroup\$ Put differently: Many Python beginners will write or see while loops and see experienced coders point out that they're horrible and that it should be done differently, and misunderstand that as "while loops are always horrible" rather than the meant "this particular while loop is horrible". Similar to the misconception "to be pythonic, I must write this code as a oneliner" :-) \$\endgroup\$ Aug 31 at 13:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.