Todo empezó cuando "Galileo Galilei" preguntó como hacer una cosa muy simple. Él mostró este código:
age = int(raw_input('Cual es tu edad?'))
if age<18:
print 'Sos menor'
else:
print 'Sos mayor'
En realidad el original... era un poco más guarango, pero el código es básicamente el mismo. hasta ahí nada raro. Pero entonces preguntó esto:
Cómo puedo hacer que si el usuario entra algo que no es un número, haga algo tipo:
print 'No tengo poderes de supervaca'
o
Uno se imaginaría que esa clase de pregunta puede producir una o dos respuestas. ¿No?
Efectivamente así es, y se ven las respuestas de Facundo Batista o Ezequiel.
Pero... que pasaría si queremos seguir preguntando cuando el usuario entra un no-número?
Entonces amigos... es una cuestión de gusto, y es todo culpa de Juan Pedro Fisanotti.
Acá está mi idea:
while True:
edad=raw_input('¿Cuantos años tenes?')
if edad.isdigit():
break
print 'No ingresaste un numero!'
Sí, lo admito, un poco a la antigua. Y hubu gritos de "no, break es una porquería, no está bien", lo que lleva a ésto, de Manuel Aráoz
age = raw_input('Tu edad?')
while not age.isdigit():
print "No es un número!"
age = raw_input('Tu edad?')
Lo que lleva a llantos de "Tener dos raw_input es feo!", lo que a su vez provoca esto (nuevamente, Manuel Aráoz:
get_age = lambda: raw_input('Tu edad?')
age = get_age()
while not age.isdigit():
print 'No es un numero!'
age = get_age()
Acá Patricio Molina pela la PEP 315.
Y entonces Alejandro Santos dice algo como "Esto es más facil en C porque podemos asignar valores a edad en la condición del while". Acuérdense de esto.
Ahora Pablo Zilliani da su versión, que, debo decir, es perfecta en cierta forma:
age = reset = msg = 'Edad?: '
while not age.isdigit():
age = raw_input(msg)
msg = "%r no es un numero!, %s" % (age, reset)
print age
Entonces Gabriel Genellina decide defender el uso de break pegándonos a todos en la cabeza con Knuth lo que debería tener un efecto mucho más potente que mencionar a Hitler.
Y vamos llegando a aguas poco navegables. Acá está la propuesta de news , que admiro. A una respetuosa distancia.
Primero el código relevante:
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!"
¿Pero qué, exactamente, es 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
Entonces yo menciono generadores, lo que lleva a esto, por Claudio Freire, que casi funciona:
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
Y entonces Fabian Gallina es el segundo en mencionar que en C podés asignar en condiciones.
No pienso aceptarlo. C no puede ser más fácil para esto!
Así que con una ayudita del cookbook...
edad=[1]
while not edad |asig| raw_input('Edad? '):
print u'Poné un número!'
print u'Tenes %s años'%edad[0]
¿Pero qué es |asig|
? ¡Qué buena pregunta!
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)
Y entonces, Pablo postea esta joyita:
import inspect
def assign(var, value):
stack = inspect.stack()[1][0]
stack.f_locals [var] = value
del stack
return value
while not assign("edad", raw_input('Edad? ')).isdigit():
print u'No es un numero!'
print u'Tenes %s años' % edad
Que es, me parece a mí, lo menos trivial que se puede llegar con este problema. Claro que el hilo no se murió todavía ;-)