Dog eat cat world
Dog eat cat world

Reputation: 760

Python equivialent of C programming techniques (while loops)

In the C programming language, I often have done the following:

while ((c = getch()) != EOF) {
 /* do something with c */
}

In Python, I have not found anything similar, since I am not allowed to set variables inside the evaluated expression. I usually end up with having to setup the evaluated expression twice!

c = sys.stdin.read(1)
while not (c == EOF):
 # Do something with c
 c = sys.stdin.read(1)

In my attempts to find a better way, I've found a way that only require to setup and the evaluated expression once, but this is getting uglier...

while True:
 c = sys.stdin.read(1)
 if (c == EOF): break
 # do stuff with c

So far I've settled with the following method for some of my cases, but this is far from optimal for the regular while loops...:

class ConditionalFileObjectReader:
 def __init__(self,fobj, filterfunc):
  self.filterfunc = filterfunc
  self.fobj = fobj
 def __iter__(self):
  return self
 def next(self):
  c = self.fobj.read(1)
  if self.filterfunc(c): raise StopIteration
  return c

for c in ConditionalFileObjectReader(sys.stdin,lambda c: c == EOF):
 print c

All my solutions to solve a simple basic programming problem has become too complex... Do anyone have a suggestion how to do this the proper way?

Upvotes: 12

Views: 874

Answers (3)

Ponkadoodle
Ponkadoodle

Reputation: 5967

I believe what you want to do is make use of the iter function.

for c in iter(getch, EOF):
     #inner loop

Iter is a very versatile function. In this case, you're telling it to repeatedly call getch (with no arguments) at the top of each loop until getch returns the sentinel value, EOF.

Upvotes: 14

Steve Jessop
Steve Jessop

Reputation: 279315

It's possible to write much simpler code in place of your ConditionalFileObjectReader, considering that EOF seems to be what you care about, rather than any arbitrary condition:

def readbytes(file):
    while True:
        c = file.read(1)
        if c == '':
            return
        yield c

for c in readbytes(sys.stdin):
    print c

So you still have 'while True ... break', which seems to be the preferred loop in Python[*], but at least you only have it once to solve the whole class of problem, "how to iterate over the bytes in a file-like object without blocking/buffering each line", and you have it in a short loop that doesn't "do stuff with c" - that's a separate concern.

Inspired by Wallacoloo's example with iter, similar to the above you could produce something more general than iter:

def until(nextvalue, pred):
    while True:
        value = nextvalue()
        if pred(value):
            return
        yield value

for c in until(lambda: sys.stdin.read(1), lambda x: x == ''):
    print c

I'm not sure whether I like this or not, but might be worth playing with. It tries to solve the general problem "iterate over the return values of some function, until a return value satisfies some condition".

[*] dare I say, the Pythonic equivalent of fancy loop syntax in other languages?

Upvotes: 3

Sven Marnach
Sven Marnach

Reputation: 602145

You would usually use a for loop in Python:

for c in sys.stdin.read():
    # whatever

If you don't want to buffer the whole stdin in memory at once, you can also add some buffering with a smaller buffer yourself.

Note that the constant EOF does not exist in Python. read() will simply return an empty string if no data is left in the stream.

Upvotes: 18

Related Questions