--- author: '' category: '' date: 2009/09/17 16:59 description: '' link: '' priority: '' slug: BB829 tags: programming, python title: An innocent question... type: text updated: 2009/09/17 16:59 url_type: '' --- 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: .. code-block:: python 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: .. code-block:: python print 'I have no supercow powers' or maybe .. code-block:: python 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 `_: .. code-block:: python 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 `_ .. code-block:: python 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) `_: .. code-block:: python 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: .. code-block:: python 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: .. code-block:: python 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? .. code-block:: python 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: .. code-block:: python 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... .. code-block:: python 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! .. code-block:: python 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: .. code-block:: python 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 ;-) .. _thread: http://mx.grulic.org.ar/lurker/thread/20090917.195408.0ce3a4e7.es.html