Reputation: 760
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
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
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
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