An innocent question...

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

print 'Typing error'

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 ;-)

Comments

Comments powered by Disqus