There is a very funny thread currently in the PyAr (Python Argentina) mailing list.
It all started when "Galileo Galilei" asked about how to do a very simple thing. He presented this code:
age = int(raw_input('How old are you?'))
if age<18:
print 'You are underage'
else:
print 'You are a grownup'
Ok, the original was... not quite as polite, but the code is the same. So far, nothing strange. But then he asked this:
How can I make it so that if the user enters something that is not a number, it
does something like this:
print 'I have no supercow powers'
or maybe
You can probably imagine that asking this kind of thing should produce maybe two answers. Right?
That is indeed the case, and you can see that in the answer by Facundo Batista or Ezequiel.
Except... what if we wanted it to keep asking in case the user entered not-a-number?
Then, my friends... it's about taste, and it's all Juan Pedro Fisanotti's fault.
Here's my take:
while True:
edad=raw_input('¿Cuantos años tenes?')
if edad.isdigit():
break
print 'No ingresaste un numero!'
Yes, I admit, a bit old fashioned. And there was a cry of "no, break sucks, it's not right", which leads to this by Manuel Aráoz
age = raw_input('Your age?')
while not age.isdigit():
print "That's not a number!"
age = raw_input('Your age?')
Which caused cries of "Having raw_input twice is ugly!", which leads to (again by Manuel Aráoz):
get_age = lambda: raw_input('Your age?')
age = get_age()
while not age.isdigit():
print 'Not a number!'
age = get_age()
Here Patricio Molina digs up PEP 315.
And then Alejandro Santos says something like "This is easier in C, because we can assign a value to age in the while's condition". Please remember this.
Now Pablo Zilliani gives his version, which is, I must say, perfect in some ways:
age = reset = msg = 'Age?: '
while not age.isdigit():
age = raw_input(msg)
msg = "%r is not a number!, %s" % (age, reset)
print age
Here Gabriel Genellina decides to defend break by hitting everyone in the head using Knuth which should have a much stronger effect than Hitler.
And now, we start veering into weird waters. Here is what news proposes, which I must say, I admire... from a respectful distance.
First, the relevant code:
edad = "0" # Entra igual la primera vez
while firstTrue (not edad.isdigit()):
edad = raw_input ("¿Cuantos años tenes? ")
if not edad.isdigit():
print "No ingresaste un nro!"
But what, exactly, is firstTrue?
import inspect
def firstTrue(cond):
""" devuelve True siempre la primera vez que se la ejecuta,
las veces subsiguientes evalua la condicion """
stack = inspect.stack()[1] # El stack del programa llamador
line = stack[2] # Nro de linea desde la que llame a firstTrue
del stack
if not "line" in firstTrue.__dict__:
# Primera vez que llamo a la funcion
firstTrue.line = line
return True
elif firstTrue.line != line:
# Llame a la funcion desde otro punto del programa
firstTrue.line = line
return True
return cond
Then, I bring up generators, which leads to Claudio Freire's, which almost works, too:
age = ''
def invalidAge():
yield True
while not age.isdigit():
print "Not a number"
yield True
yield False
for i in invalidAge():
age = raw_input("Age please: ")
print age
And then Fabian Gallina is the second one to bring up C's assignments inside conditions.
You know, I can't accept that. I will not accept C being easier for this.
So, with a little help from the python cookbook...
age=[1]
while not age |asig| raw_input('Age? '):
print 'Not a number!'
print u'You are %s years old'%age[0]
You may ask, what's |asig|
? Glad you asked!
class Infix:
def __init__(self, function):
self.function = function
def __ror__(self, other):
return Infix(lambda x, self=self, other=other: self.function(other, x))
def __or__(self, other):
return self.function(other)
def __rlshift__(self, other):
return Infix(lambda x, self=self, other=other: self.function(other, x))
def __rshift__(self, other):
return self.function(other)
def __call__(self, value1, value2):
return self.function(value1, value2)
def opasigna (x,y):
x[0]=y
return y.isdigit()
asig=Infix(opasigna)
And then, Pablo posts this gem:
import inspect
def assign(var, value):
stack = inspect.stack()[1][0]
stack.f_locals [var] = value
del stack
return value
while not assign("age", raw_input('Age? ')).isdigit():
print u'Not a number!'
print u'You are %s years old' % age
Which is, IMVHO, about as far from trivial as you can get here.
Of course the thread is not dead yet ;-)