Pythonic enumeration of while loop

萝らか妹 提交于 2020-01-02 02:11:48

问题


Python has an elegant way of automatically generating a counter variable in for loops: the enumerate function. This saves the need of initializing and incrementing a counter variable. Counter variables are also ugly because they are often useless once the loop is finished, yet their scope is not the scope of the loop, so they occupy the namespace without need (although I am not sure whether enumerate actually solves this).

My question is, whether there is a similar pythonic solution for while loops. enumerate won't work for while loops since enumerate returns an iterator. Ideally, the solution should be "pythonic" and not require function definitions.

For example:

x=0
c=0
while x<10:
  x=int(raw_input())
  print x,c
  c+=1

In this case we would want to avoid initializing and incrementing c.

Clarification:

This can be done with an endless for loop with manual termination as some have suggested, but I am looking for a solution that makes the code clearer, and I don't think that solution makes the code clearer in this case.


回答1:


Improvement (in readability, I'd say) to Ignacio's answer:

x = 0
for c in itertools.takewhile(lambda c: x < 10, itertools.count()):
    x = int(raw_input())
    print x, c

Advantages:

  • Only the while loop condition is in the loop header, not the side-effect raw_input.
  • The loop condition can depend on any condition that a normal while loop could. It's not necessary to "import" the variables referenced into the takewhile, as they are already visible in the lambda scope. Additionally it can depend on the count if you want, though not in this case.
  • Simplified: enumerate no longer appears at all.



回答2:


Again with the itertools...

import itertools

for c, x in enumerate(
    itertools.takewhile(lambda v: v < 10,
      (int(raw_input()) for z in itertools.count())
    )
  ):
  print c, x



回答3:


If you want zero initialization before the while loop, you can use a Singleton with a counter:

class Singleton(object):
    _instance = None
    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            cls._instance = super(Singleton, cls).__new__(
                                cls, *args, **kwargs)
            cls.count=0
        else:
            cls.count+=1                            
        return cls._instance

Then there will only be one instance of Singleton and each additional instance just adds one:

>>> Singleton().count    # initial instance
0
>>> Singleton().count
1
>>> Singleton().count
2
>>> Singleton().count
3

Then your while loop becomes:

while Singleton():
    x=int(raw_input('x: '))
    if x>10: break

print 'While loop executed',Singleton().count,'times' 

Entering 1,2,3,11 it prints:

x: 1
x: 2
x: 3
x: 11
While loop executed 4 times

If you do not mind a single line initialization before the while loop, you can just subclass an interator:

import collections
class WhileEnum(collections.Iterator):
    def __init__(self,stop=None):
        self.stop=stop
        self.count=0

    def next(self):    # '__next__' on Py 3, 'next' on Py 2
        if self.stop is not None:
            self.remaining=self.stop-self.count
            if self.count>=self.stop: return False
        self.count+=1    
        return True

    def __call__(self):
        return self.next()

Then your while loop becomes:

enu=WhileEnum()
while enu():
    i=int(raw_input('x: '))
    if i>10: break

print enu.count

I think the second is the far better approach. You can have multiple enumerators and you can also set a limit on how many loops to go:

limited_enum=WhileEnum(5)



回答4:


I don't think it's possible to do what you want in the exact way you want it. If I understand right, you want a while loop that increments a counter each time through, without actually exposing a visible counter outside the scope of the loop. I think the way to do this would be to rewrite your while loop as a nonterminating for loop, and check the end condition manually. For your example code:

import itertools

x = 0
for c in itertools.count():
    x = int(raw_input())
    print x, c
    if x >= 10:
        break

The problem is that fundamentally you're doing iteration, with the counter. If you don't want to expose that counter, it needs to come from the loop construct. Without defining a new function, you're stuck with a standard loop and an explicit check.

On the other hand, you could probably also define a generator for this. You'd still be iterating, but you could at least wrap the check up in the loop construct.



来源:https://stackoverflow.com/questions/17137490/pythonic-enumeration-of-while-loop

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!