Pythonic enumeration of while loop

大兔子大兔子 提交于 2019-12-05 04:09:47

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.

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

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)

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.

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