<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 <!--
27/10
Ordenamientos y búsquedas.
Excepciones. Funciones anónimas.(Pablo o Andres)
-->
Ordenamiento de listas
Las listas se pueden ordenar fácilmente usando la función sorted
Step1:  Pero, ¿y cómo hacemos para ordenarla de mayor a menor?. <br>
Simple, interrogamos un poco a la función
Step2:  ¿Y si lo que quiero ordenar es una lista de registros?. <br>
Podemos pasarle una función que sepa cómo comparar esos registros o una que sepa devolver la información que necesita comparar.
Step3:  Búsquedas en listas
Para saber si un elemento se encuentra en una lista, alcanza con usar el operador in
Step4:  También es muy fácil saber si un elemento no esta en la lista
Step5:  En cambio, si lo que queremos es saber es dónde se encuentra el número 3 en la lista es
Step6:  Ahora, para todos estos casos lo que hice fue buscar un elemento completo, es decir, que tenía que conocer todo lo que buscaba y no sólamente una parte, como podría ser el padrón de un alumno.
Step7:  Funciones anónimas
Hasta ahora, a todas las funciones que creamos les poníamos un nombre al momento de crearlas, pero cuando tenemos que crear funciones que sólo tienen una línea y no se usan en una gran cantidad de lugares se pueden usar las funciones lambda
Step8:  Si bien no son funciones que se usen todos los días, se suelen usar cuando una función recibe otra función como parámetro (las funciones son un tipo de dato, por lo que se las pueden asignar a variables, y por lo tanto, también pueden ser parámetros).
Por ejemplo, para ordenar los alumnos por padrón podríamos usar
Step9:  Excepciones
Una excepción es la forma que tiene el intérprete de que indicarle al programador y/o usuario que ha ocurrido un error. Si la excepción no es controlada por el desarrollador ésta llega hasta el usuario y termina abruptamente la ejecución del sistema. <br>
Por ejemplo
Step10:  Pero no hay que tenerle miedo a las excepciones, sólo hay que tenerlas en cuenta y controlarlas en el caso de que ocurran
Step11:  Pero supongamos que implementamos la regla de tres de la siguiente forma
Step12:  En cambio, si le pasamos 0 en el lugar de x
Step13:  Acá podemos ver todo el traceback o stacktrace, que son el cómo se fueron llamando las distintas funciones entre sí hasta que llegamos al error. <br>
Pero no es bueno que este tipo de excepciones las vea directamente el usuario, por lo que podemos controlarlas en distintos momentos. Se pueden controlar inmediatamente donde ocurre el error, como mostramos antes, o en cualquier parte de este stacktrace. <br>
En el caso de la regla_de_tres no nos conviene poner el try/except encerrando la línea x/y, ya que en ese punto no tenemos toda la información que necesitamos para informarle correctamente al usuario, por lo que podemos ponerla en
Step14:  Pero en este caso igual muestra 0, por lo que si queremos, podemos poner los try/except incluso más arriba en el stacktrace
Step15:  Todos los casos son distintos y no hay UN lugar ideal dónde capturar la excepción; es cuestión del desarrollador decidir dónde conviene ponerlo para cada problema.
Capturar múltiples excepciones
Una única línea puede lanzar distintas excepciones, por lo que capturar un tipo de excepción en particular no me asegura que el programa no pueda lanzar un error en esa línea que supuestamente es segura
Step16:  En esos casos podemos capturar más de una excepción de la siguiente forma
Step17:  Incluso, si queremos que los dos errores muestren el mismo mensaje podemos capturar ambas excepciones juntas
Step18:  Jerarquía de excepciones
Existe una <a href="https
Step19:  Y también como
Step20:  Si bien siempre se puede poner Exception en lugar del tipo de excepción que se espera, no es una buena práctica de programación ya que se pueden esconder errores indeseados. Por ejemplo, un error de sintaxis.
Además, cuando se lanza una excepción en el bloque try, el intérprete comienza a buscar entre todas cláusulas except una que coincida con el error que se produjo, o que sea de mayor jerarquía. Por lo tanto, es recomendable poner siempre las excepciones más específicas al principio y las más generales al final
Step21:  Pero entonces, ¿por qué no poner ese código dentro del try-except?. Porque tal vez no queremos capturar con las cláusulas except lo que se ejecute en ese bloque de código
Step22:  Lanzar excepciones
Hasta ahora vimos cómo capturar un error y trabajar con él sin que el programa termine abruptamente, pero en algunos casos somos nosotros mismos quienes van a querer lanzar una excepción. Y para eso, usaremos la palabra reservada raise
<ASSISTANT_TASK:>
Python Code:
lista_de_numeros = [1, 6, 3, 9, 5, 2]
lista_ordenada = sorted(lista_de_numeros)
print lista_ordenada
print lista_de_numeros
Explanation: <!--
27/10
Ordenamientos y búsquedas.
Excepciones. Funciones anónimas.(Pablo o Andres)
-->
Ordenamiento de listas
Las listas se pueden ordenar fácilmente usando la función sorted:
End of explanation
lista_de_numeros = [1, 6, 3, 9, 5, 2]
print sorted(lista_de_numeros, reverse=True)
Explanation: Pero, ¿y cómo hacemos para ordenarla de mayor a menor?. <br>
Simple, interrogamos un poco a la función:
```Python
print sorted.doc
sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list
``
Entonces, con sólo pasarle el parámetro de *reverse* enTrue` debería alcanzar:
End of explanation
def crear_curso():
    curso = [
        {'nombre': 'Rodriguez, Carlos', 'nota': 6, 'padron': 98128}, 
        {'nombre': 'Perez, Lucas', 'nota': 6, 'padron': 93453}, 
        {'nombre': 'Gonzalez, Ramiro', 'nota': 8, 'padron': 93716}, 
        {'nombre': 'Gonzalez, Carlos', 'nota': 6, 'padron': 90464}, 
        {'nombre': 'Lopez, Carlos', 'nota': 7, 'padron': 98569}
    ]
    
    return curso
def imprimir_curso(lista):
    for idx, x in enumerate(lista):
        msg = '    {pos:2}. {padron} - {nombre}: {nota}'
        print msg.format(pos=idx, **x)
def obtener_padron(alumno):
    return alumno['padron']
curso = crear_curso()
print 'La lista tiene los alumnos:'
imprimir_curso(curso)
lista_ordenada = sorted(curso, key=obtener_padron)
print 'Y la lista ordenada por padrón:'
imprimir_curso(lista_ordenada)
Explanation: ¿Y si lo que quiero ordenar es una lista de registros?. <br>
Podemos pasarle una función que sepa cómo comparar esos registros o una que sepa devolver la información que necesita comparar.
End of explanation
lista = [11, 4, 6, 1, 3, 5, 7]
if 3 in lista:
    print '3 esta en la lista'
else:
    print '3 no esta en la lista'
if 15 in lista:
    print '15 esta en la lista'
else:
    print '15 no esta en la lista'
Explanation: Búsquedas en listas
Para saber si un elemento se encuentra en una lista, alcanza con usar el operador in:
End of explanation
lista = [11, 4, 6, 1, 3, 5, 7]
if 3 not in lista:
    print '3 NO esta en la lista'
else:
    print '3 SI esta en la lista'
Explanation: También es muy fácil saber si un elemento no esta en la lista:
End of explanation
lista = [11, 4, 6, 1, 3, 5, 7]
pos = lista.index(3)
print 'El 3 se encuentra en la posición', pos
pos = lista.index(15)
print 'El 15 se encuentra en la posición', pos
Explanation: En cambio, si lo que queremos es saber es dónde se encuentra el número 3 en la lista es:
End of explanation
curso = crear_curso()
print 'La lista tiene los alumnos:'
imprimir_curso(curso)
alumno_93716 = (alumno for alumno in curso if alumno['padron'] == 93716).next()
print 'El alumno de padron 93716 se llama {nombre}'.format(**alumno_93716)
Explanation: Ahora, para todos estos casos lo que hice fue buscar un elemento completo, es decir, que tenía que conocer todo lo que buscaba y no sólamente una parte, como podría ser el padrón de un alumno.
End of explanation
help("lambda")
mi_funcion = lambda x, y: x+y
resultado = mi_funcion(1, 2)
print resultado
print type(mi_funcion)
def mi_funcion2(x, y):
    return x + y
resultado = mi_funcion2(1, 2)
print resultado
print type(mi_funcion2)
Explanation: Funciones anónimas
Hasta ahora, a todas las funciones que creamos les poníamos un nombre al momento de crearlas, pero cuando tenemos que crear funciones que sólo tienen una línea y no se usan en una gran cantidad de lugares se pueden usar las funciones lambda:
End of explanation
curso = crear_curso()
print 'Curso original'
imprimir_curso(curso)
lista_ordenada = sorted(curso, key=lambda alumno: (-alumno['nota'], alumno['padron']))
print 'Curso ordenado'
imprimir_curso(lista_ordenada)
Explanation: Si bien no son funciones que se usen todos los días, se suelen usar cuando una función recibe otra función como parámetro (las funciones son un tipo de dato, por lo que se las pueden asignar a variables, y por lo tanto, también pueden ser parámetros).
Por ejemplo, para ordenar los alumnos por padrón podríamos usar:
Python
sorted(curso, key=lambda x: x['padron'])
Ahora, si quiero ordenar la lista anterior por nota decreciente y, en caso de igualdad, por padrón podríamos usar:
End of explanation
print 1/0
Explanation: Excepciones
Una excepción es la forma que tiene el intérprete de que indicarle al programador y/o usuario que ha ocurrido un error. Si la excepción no es controlada por el desarrollador ésta llega hasta el usuario y termina abruptamente la ejecución del sistema. <br>
Por ejemplo:
End of explanation
dividendo = 10
divisor = '0'
print 'Intentare hacer la división de {}/{}'.format(dividendo, divisor)
try:
    resultado = dividendo / divisor
    print resultado
except ZeroDivisionError:
    print 'No se puede hacer la división ya que el divisor es 0.'
except TypeError:
    print 'Alguno de los parametros no es un número'
print 'Algo'
Explanation: Pero no hay que tenerle miedo a las excepciones, sólo hay que tenerlas en cuenta y controlarlas en el caso de que ocurran:
End of explanation
def dividir(x, y):
    return x/y
def regla_de_tres(x, y, z):
    return dividir(z*y, x)
# Si de 28 alumnos, aprobaron 15, el porcentaje de aprobados es de...
porcentaje_de_aprobados = regla_de_tres(28, 15, 100)
print 'Porcentaje de aprobados: {0:.2f}%'.format(porcentaje_de_aprobados)
Explanation: Pero supongamos que implementamos la regla de tres de la siguiente forma:
End of explanation
resultado = regla_de_tres(0, 13, 100)
print 'Porcentaje de aprobados: {0:.2f}%'.format(resultado)
Explanation: En cambio, si le pasamos 0 en el lugar de x:
End of explanation
def dividir(x, y):
    return x/y
def regla_de_tres(x, y, z):
    resultado = 0
    try:
        resultado = dividir(z*y, x)
    except ZeroDivisionError:
        print 'No se puede calcular la regla de tres ' \
              'porque el divisor es 0'
        
    return resultado
    
    
print regla_de_tres(0, 1, 2)
Explanation: Acá podemos ver todo el traceback o stacktrace, que son el cómo se fueron llamando las distintas funciones entre sí hasta que llegamos al error. <br>
Pero no es bueno que este tipo de excepciones las vea directamente el usuario, por lo que podemos controlarlas en distintos momentos. Se pueden controlar inmediatamente donde ocurre el error, como mostramos antes, o en cualquier parte de este stacktrace. <br>
En el caso de la regla_de_tres no nos conviene poner el try/except encerrando la línea x/y, ya que en ese punto no tenemos toda la información que necesitamos para informarle correctamente al usuario, por lo que podemos ponerla en:
End of explanation
def dividir(x, y):
    return x/y
def regla_de_tres(x, y, z):
    return dividir(z*y, x)
    
    
try:
    print regla_de_tres(0, 1, 2)
except ZeroDivisionError:
    print 'No se puede calcular la regla de tres ' \
          'porque el divisor es 0'
Explanation: Pero en este caso igual muestra 0, por lo que si queremos, podemos poner los try/except incluso más arriba en el stacktrace:
End of explanation
def dividir_numeros(x, y):
    try:
        resultado = x/y
        print 'El resultado es: %s' % resultado
    except ZeroDivisionError:
        print 'ERROR: Ha ocurrido un error por dividir por 0'
        
dividir_numeros(1, 0)
dividir_numeros(10, 2)
dividir_numeros("10", 2)
Explanation: Todos los casos son distintos y no hay UN lugar ideal dónde capturar la excepción; es cuestión del desarrollador decidir dónde conviene ponerlo para cada problema.
Capturar múltiples excepciones
Una única línea puede lanzar distintas excepciones, por lo que capturar un tipo de excepción en particular no me asegura que el programa no pueda lanzar un error en esa línea que supuestamente es segura:
En algunos casos tenemos en cuenta que el código puede lanzar una excepción como la de ZeroDivisionError, pero eso puede no ser suficiente:
End of explanation
def dividir_numeros(x, y):
    try:
        resultado = x/y
        print 'El resultado es: %s' % resultado
    except TypeError:
        print 'ERROR: Ha ocurrido un error por mezclar tipos de datos'
    except ZeroDivisionError:
        print 'ERROR: Ha ocurrido un error de división por cero'
    except Exception:
        print 'ERROR: Ha ocurrido un error inesperado'
        
dividir_numeros(1, 0)
dividir_numeros(10, 2)
dividir_numeros("10", 2)
Explanation: En esos casos podemos capturar más de una excepción de la siguiente forma:
End of explanation
def dividir_numeros(x, y):
    try:
        resultado = x/y
        print 'El resultado es: %s' % resultado
    except (ZeroDivisionError, TypeError):
        print 'ERROR: No se puede calcular la división'
        
dividir_numeros(1, 0)
dividir_numeros(10, 2)
dividir_numeros("10", 2)
Explanation: Incluso, si queremos que los dos errores muestren el mismo mensaje podemos capturar ambas excepciones juntas:
End of explanation
try:
    print 1/0
except ZeroDivisionError:
    print 'Ha ocurrido un error de división por cero'
Explanation: Jerarquía de excepciones
Existe una <a href="https://docs.python.org/2/library/exceptions.html">jerarquía de excepciones</a>, de forma que si se sabe que puede venir un tipo de error, pero no se sabe exactamente qué excepción puede ocurrir siempre se puede poner una excepción de mayor jerarquía:
<img src="excepciones.png"/>
Por lo que el error de división por cero se puede evitar como:
End of explanation
try:
    print 1/0
except Exception:
    print 'Ha ocurrido un error inesperado'
Explanation: Y también como:
End of explanation
def dividir_numeros(x, y):
    try:
        resultado = x/y
        print 'El resultado es {}'.format(resultado)
    except ZeroDivisionError:
        print 'Error: División por cero'
    else:
        print 'Este mensaje se mostrará sólo si no ocurre ningún error'
    finally: 
        print 'Este bloque de código se muestra siempre'
        
dividir_numeros(1, 0)
print '-------------'
dividir_numeros(10, 2)
Explanation: Si bien siempre se puede poner Exception en lugar del tipo de excepción que se espera, no es una buena práctica de programación ya que se pueden esconder errores indeseados. Por ejemplo, un error de sintaxis.
Además, cuando se lanza una excepción en el bloque try, el intérprete comienza a buscar entre todas cláusulas except una que coincida con el error que se produjo, o que sea de mayor jerarquía. Por lo tanto, es recomendable poner siempre las excepciones más específicas al principio y las más generales al final:
Python
def dividir_numeros(x, y):
    try:
        resultado = x/y
        print 'El resultado es: %s' % resultado
    except TypeError:
        print 'ERROR: Ha ocurrido un error por mezclar tipos de datos'
    except ZeroDivisionError:
        print 'ERROR: Ha ocurrido un error de división por cero'
    except Exception:
        print 'ERROR: Ha ocurrido un error inesperado'
Si el error no es capturado por ninguna clausula se propaga de la misma forma que si no se hubiera puesto nada.
Otras cláusulas para el manejo de excepciones
Además de las cláusulas try y except existen otras relacionadas con las excepciones que nos permiten manejar de mejor manera el flujo del programa:
* else: se usa para definir un bloque de código que se ejecutará sólo si no ocurrió ningún error.
* finally: se usa para definir un bloque de código que se ejecutará siempre, independientemente de si se lanzó una excepción o no.
End of explanation
def dividir_numeros(x, y):
    try:
        resultado = x/y
        print 'El resultado es {}'.format(resultado)
    except ZeroDivisionError:
        print 'Error: División por cero'
    else:
        print 'Ahora hago que ocurra una excepción'
        print 1/0
    finally: 
        print 'Este bloque de código se muestra siempre'
        
dividir_numeros(1, 0)
print '-------------'
dividir_numeros(10, 2)
Explanation: Pero entonces, ¿por qué no poner ese código dentro del try-except?. Porque tal vez no queremos capturar con las cláusulas except lo que se ejecute en ese bloque de código:
End of explanation
def dividir_numeros(x, y):
    if y == 0:
        raise Exception('Error de división por cero')
    
    resultado = x/y
    print 'El resultado es {0}'.format(resultado)
    
try:
    dividir_numeros(1, 0)
except ZeroDivisionError as e:
    print 'ERROR: División por cero'
except Exception as e:
    print 'ERROR: ha ocurrido un error del tipo Exception'
print '----------'
dividir_numeros(1, 0)
Explanation: Lanzar excepciones
Hasta ahora vimos cómo capturar un error y trabajar con él sin que el programa termine abruptamente, pero en algunos casos somos nosotros mismos quienes van a querer lanzar una excepción. Y para eso, usaremos la palabra reservada raise:
End of explanation
<END_TASK>