Mostrando entradas con la etiqueta else. Mostrar todas las entradas
Mostrando entradas con la etiqueta else. Mostrar todas las entradas

martes, 11 de enero de 2022

Tratamiento de las Excepciones en Python


imagen de código en python para tratar excepciones.







Excepciones.

Por definición las excepciones son errores que se producen durante la ejecución del código y que no son esperadas por el usuario.

Un ejemplo típico es el tener un programa que divide dos números y tratamos de dividir uno de ellos entre cero.

> print(8/0)
Traceback (most recent call last):
  File "<string>", line 1, in <module>
ZeroDivisionError: division by zero

Para gestionarlas y que no finalicen de forma inesperada un programa, podemos usar las siguientes instrucciones de Python:


try: 
    [instrucciones o código a ejecutar.]
except <tipo de excepción>, <tipo de excepción>...:
    [instrucciones o código a ejecutar si ocurre la excepción señalada. En 
    el caso de que no se indique una excepción en concreto y se deje en
    blanco, la excepción se ejecutará con cualquier tipo de error. También
    se pueden poner varios except seguidos y saltará el código del error 
    correspondiente.]
except: # Si ponemos excepciones especificas, está última debe ser la 
    genérica para recoger las que no se recojan previamente. La genérica
    SIEMPRE DEBE SER LA ÚLTIMA en colocarse. 
else: # esta instrucción es opcional
    [código a ejecutar solamente si no ocurre ninguna excepción.]
finally: # este instrucción es opcional
    [código a ejecutar tanto si ocurren excepciones como si no.]

Un ejemplo. Si creamos una función que muestre el resultado de dividir un número entre otro:

def division(numero_1, numero_2):
    try:
        print(numero_1/numero_2)
    except ZeroDivisionError:
        print("No puedes dividir un número entre cero.")
        return "operación errónea"
    else:
        print("División realizada correctamente.")
    finally:
        return "Función terminada."

y le pasamos un resultado correcto por ejemplo dividir 8 entre 2:

print(division(8,4))
obtendremos el siguiente resultado:

2.0
División realizada correctamente.
Función terminada.
Si por el contrario intentamos dividir entre cero

print(division(8,0))
la excepción saltará y nos mostrará el siguiente resultado:

No puedes dividir un número entre cero.
Función terminada.

También podemos poner "finally" sin los "except" previos que capturan el error. En este caso primero se ejecutaría el código que hubiésemos definido dentro del "finally" y luego el programa mostraría el error. Si prescindiéramos del "finally", entonces el error impide que siga el programa. 

Por ejemplo:

try:
    print(8/0)
finally:
    print("Ha habido un error")
Ha habido un error
Traceback (most recent call last):
  File "<string>", line 2, in <module>
ZeroDivisionError: division by zero
> 
Como ves se muestra el mensaje, aunque previamente se ha producido un error en el programa.

Excepciones propias - RAISE -


La instrucción "raise" en Python permite lanzar un mensaje de error con un texto personalizado. Se suele poner después de la instrucción "except", aunque también lo puedes poner donde quieras para llamar al error.

La síntesis para lanzar el error sería:

raise TipodeError("Texto explicativo del error") 

Siguiendo con el ejemplo que hemos usado hasta ahora

# Vamos a dividir 8 entre un número pero no queremos que se pueda
# utilizar el cero como divisor.
divisor = int(input("Introduce un numero como divisor: "))
if divisor == 0:
    raise ZeroDivisionError("¡No se puede dividir entre cero!")
        
print(8/divisor)
Introduce un numero como divisor: 0
Traceback (most recent call last):
  File "<string>", line 5, in <module>
ZeroDivisionError: ¡No se puede dividir entre cero!
> 


La instrucción assert.


Aunque en si misma no sirve para detectar tratar directamente excepciones si que nos va a servir para detectar errores de código y para testear nuestro programa. La estructura es:

assert condición, "texto a mostrar"


Imaginemos que creamos una función que solo calcula la raíz cuadrada de un número. La raíz cuadrada de un numero es ese mismo número elevado a 1/2 o 0.5. Para saber si la hemos construido bien sabemos que la raíz cuadrada de 25 es 5.0 por tanto podemos probar la función usando assert de la siguiente manera para ver si la función funciona correctamente:

def raiz_cuadrada(numero):
    return pow(numero, 0.5)
    
resultado = raiz_cuadrada(25)
assert resultado == 5.0, "La función no funciona."
print("el calculo es correcto")
Si la condición es True no hace nada y el programa continua normalmente, pero por el contrario si la condición es False lanza un error "AssertionError" y detiene el programa. Si ejecutamos el código tal como está:


 el calculo es correcto
>
Si por ejemplo ahora cambiamos el exponente por que nos hemos confundido al teclear y ponemos 0.6 y volvemos a ejecutar el programa:

def raiz_cuadrada(numero):
    return pow(numero, 0.6)
    
resultado = raiz_cuadrada(25)
assert resultado == 5.0, "La función no funciona"

print("el calculo es correcto")
Traceback (most recent call last):
  File "<string>", line 5, in <module>
AssertionError: La función no funciona
Vemos que nos muestra que la función no funciona como debería y nos funciona una pista para ver donde esta el fallo y poder depurarlo.

Si ya has visto el tema de la programación orientada a objetos de Python puedes seguir leyendo esta ampliación sobre las excepciones.

Las excepciones son Clases.


Los ejemplos anteriores se centraron en detectar un tipo específico de excepción y responder de manera apropiada. Ahora vamos a profundizar más y mirar dentro de la excepción misma.

Probablemente no te sorprenderá saber que las excepciones son clases. Además, cuando se genera una excepción, se crea una instancia de un objeto de la clase y pasa por todos los niveles de ejecución del programa, buscando el bloque "except" que está preparado para tratar con la excepción.

Tal objeto lleva información útil que puede ayudarte a identificar con precisión todos los aspectos de la situación pendiente. Para lograr ese objetivo, Python ofrece una variante especial de la cláusula de excepción: puedes encontrarla en el siguiente código.

try:
    int("Hola")
except Exception as e:
    print(e)
    print(e.__str__())

Salida:

invalid literal for int() with base 10: 'Hola'
invalid literal for int() with base 10: 'Hola'


Como puedes ver, la sentencia except se extendió y contiene una frase adicional que comienza con la palabra clave reservada as, seguida por un identificador. El identificador está diseñado para capturar la excepción con el fin de analizar su naturaleza y sacar conclusiones adecuadas.

Nota: el alcance del identificador solo es dentro del except, y no va más allá.

El ejemplo presenta una forma muy simple de utilizar el objeto recibido: simplemente imprímelo (como puedes ver, la salida es producida por el método del objeto __str__()) y contiene un breve mensaje que describe la razón.

Se imprimirá el mismo mensaje si no hay un bloque except en el código, y Python se verá obligado a manejarlo por sí mismo.

Todas las excepciones de Python forman una jerarquia de clases. Si quieres puedes verlas sin problemas. Observa el código.

def imprimir_arbol_excepciones(estaclase, anidado = 0):
    if anidado > 1:
        print("   |" * (anidado - 1), end="")
    if anidado > 0:
        print("   +---", end="")
    
    print(estaclase.__name__)
    for subclass in estaclase.__subclasses__():
        imprimir_arbol_excepciones(subclass, anidado + 1)
        
imprimir_arbol_excepciones(BaseException)  
Salida:

BaseException
   +---Exception
   |   +---TypeError
   |   +---StopAsyncIteration
   |   +---StopIteration
   |   +---ImportError
   |   |   +---ModuleNotFoundError
   |   |   +---ZipImportError
   |   +---OSError
   |   |   +---ConnectionError
   |   |   |   +---BrokenPipeError
   |   |   |   +---ConnectionAbortedError
   |   |   |   +---ConnectionRefusedError
   |   |   |   +---ConnectionResetError
   |   |   +---BlockingIOError
   |   |   +---ChildProcessError
   |   |   +---FileExistsError
   |   |   +---FileNotFoundError
   |   |   +---IsADirectoryError
   |   |   +---NotADirectoryError
   |   |   +---InterruptedError
   |   |   +---PermissionError
   |   |   +---ProcessLookupError
   |   |   +---TimeoutError
   |   |   +---UnsupportedOperation
   |   |   +---ItimerError
   |   +---EOFError
   |   +---RuntimeError
   |   |   +---RecursionError
   |   |   +---NotImplementedError
   |   |   +---_DeadlockError
   |   +---NameError
   |   |   +---UnboundLocalError
   |   +---AttributeError
   |   +---SyntaxError
   |   |   +---IndentationError
   |   |   |   +---TabError
   |   +---LookupError
   |   |   +---IndexError
   |   |   +---KeyError
   |   |   +---CodecRegistryError
   |   +---ValueError
   |   |   +---UnicodeError
   |   |   |   +---UnicodeEncodeError
   |   |   |   +---UnicodeDecodeError
   |   |   |   +---UnicodeTranslateError
   |   |   +---UnsupportedOperation
   |   +---AssertionError
   |   +---ArithmeticError
   |   |   +---FloatingPointError
   |   |   +---OverflowError
   |   |   +---ZeroDivisionError
   |   +---SystemError
   |   |   +---CodecRegistryError
   |   +---ReferenceError
   |   +---MemoryError
   |   +---BufferError
   |   +---Warning
   |   |   +---UserWarning
   |   |   +---DeprecationWarning
   |   |   +---PendingDeprecationWarning
   |   |   +---SyntaxWarning
   |   |   +---RuntimeWarning
   |   |   +---FutureWarning
   |   |   +---ImportWarning
   |   |   +---UnicodeWarning
   |   |   +---BytesWarning
   |   |   +---ResourceWarning
   +---GeneratorExit
   +---SystemExit
   +---KeyboardInterrupt
Este programa muestra todas las excepciones predefinidas en forma de árbol.

Anatomía detallada de las excepciones.


La clase BaseException introduce una propiedad llamada args. Es una tupla diseñada para reunir todos los argumentos pasados al constructor de la clase. Está vacío si la construcción se ha invocado sin ningún argumento, o solo contiene un elemento cuando el constructor recibe un argumento y así sucesivamente (no se considera argumento aquí el self)

Vamos a ver un poco esto a través del siguiente código.

def print_args(args):
    lng = len(args)
    if lng == 0:
        print("")
    elif lng == 1:
        print(args[0])
    else:
        print(str(args))

try:
    raise Exception
except Exception as e:
    print(e, e.__str__(), sep = " : ", end = " : ")
    print_args(e.args)
    
try:
    raise Exception("Mi excepción")
except Exception as e:
    print(e, e.__str__(), sep = " : ", end = " : ")
    print_args(e.args)
    
try:
    raise Exception("Mi", "excepción")
except Exception as e:
    print(e, e.__str__(), sep = " : ", end = " : ")
    print_args(e.args)
Hemos utilizado la función para imprimir el contenido de la propiedad args en tres casos diferentes, donde la excepción de la clase Exception es generada de tres maneras distintas. Para hacerlo más espectacular, también hemos impreso el objeto en sí, junto con el resultado de la invocación __str__().

El primer caso parece de rutina, solo hay el nombre Exception después de la palabra clave reservada raise. Esto significa que el objeto de esta clase se ha creado de la manera más rutinaria.

El segundo y el tercer caso pueden parecer un poco extraños a primera vista, pero no hay nada extraño, son solo las invocaciones del constructor. En la segunda sentencia raise, el constructor se invoca con un argumento, y en el tercero, con dos.

Como puedes ver, la salida del programa refleja esto, mostrando los contenidos apropiados de la propiedad args:

 :  : 
Mi excepción : Mi excepción : Mi excepción
('Mi', 'excepción') : ('Mi', 'excepción') : ('Mi', 'excepción')

¿Cómo crear tu propia excepción?

La jerarquía de excepciones no está cerrada ni terminada, y siempre puedes ampliarla si deseas o necesitas crear tu propio mundo poblado con tus propias excepciones.

Puede ser útil cuando se crea un módulo complejo que detecta errores y genera excepciones, y deseas que las excepciones se distingan fácilmente de cualquier otra de Python.

Esto se puede hacer al definir tus propias excepciones como subclases derivadas de las predefinidas.

Nota: si deseas crear una excepción que se utilizará como un caso especializado de cualquier excepción incorporada, derívala solo de esta. Si deseas construir tu propia jerarquía, y no quieres que esté estrechamente conectada al árbol de excepciones de Python, derívala de cualquiera de las clases de excepción principales, tal como: Exception.

Imagina que has creado una aritmética completamente nueva, regida por sus propias leyes y teoremas. Está claro que la división también se ha redefinido, y tiene que comportarse de una manera diferente a la división de rutina. También está claro que esta nueva división debería plantear su propia excepción, diferente de la incorporada ZeroDivisionError, pero es razonable suponer que, en algunas circunstancias, tu (o el usuario de tu aritmética) pueden tratar todas las divisiones entre cero de la misma manera.

Demandas como estas pueden cumplirse en la forma presentada en siguiente código.

class MiDivisionEntreCeroError(ZeroDivisionError):
    pass

def haz_la_division(mine):
    if mine:
        raise MiDivisionEntreCeroError("Peores Noticias")
    else:
        raise ZeroDivisionError("Malas Noticias")
        
for modo in [False, True]:
    try:
        haz_la_division(modo)
    except ZeroDivisionError:
        print('División entre cero')
        
for modo in [False, True]:
    try:
        haz_la_division(modo)
    except MiDivisionEntreCeroError:
        print('Mi división entre cero')
    except ZeroDivisionError:
        print('División entre cero original')

Salida:

División entre cero
División entre cero
División entre cero original
Mi división entre cero
Mira el código y analicémoslo:

Hemos definido nuestra propia excepción, llamada MiDivisionEntreCeroError , derivada de la incorporada ZeroDivisionError. Como puedes ver, hemos decidido no agregar ningún componente nuevo a la clase.

En efecto, una excepción de esta clase puede ser, dependiendo del punto de vista deseado, tratada como una simple excepción ZeroDivisionError, o puede ser considerada por separado.

La función haz_la_division() genera una excepción MiDivisionEntreCeroError o ZeroDivisionError dependiendo del valor del argumento.

La función se invoca cuatro veces en total, mientras que las dos primeras invocaciones se manejan utilizando solo un bloque except (la más general), las dos últimas invocan dos bloques diferentes, capaces de distinguir las excepciones (no lo olvides: el orden de los bloques hace una diferencia fundamental).

Vamo ahora a crear un sistema de excepciones que no tienen nada que ver con las que vienen por defecto en Python. Imagina que estás trabajando en un sistema para simular una pizzeria. Sería conveniente crear un sistema de excepciones por separado a las de Python. 

Podemos empezar a construirla definiendo una excepción general como una nueva clase base para cualquier otra excepción generalizada. 

class PizzaError(Exception):
    def __init__(self, pizza, mensaje):
        Exception.__init__(self, mensaje)
        self.pizza = pizza
Vamos a recopilar mas información de la que recoge normalmente una Exception, por ello le pasamos dos argumentos:

- Uno que especifica una pizza determinada como tema del proceso.
- Otro que contiene una descripción más o menos precisa del problema.

El segundo parámetro se lo pasamos al constructor de la superclase y guardamos el primero dentro de nuestra propiedad.

Un problema más especifico en una pizza como puede ser un exceso de queso puede requerir una Excepción más especifica:

class DemasiadoQueso(PizzaError):
    def __init__(self, pizza, queso, mensaje)
    super().__init__(pizza, mensaje)
    self.queso = queso

La excepción DemasiadoQueso necesita más información que la excepción regular PizzaError.  Por esto lo agregamos al constructor y guardamos el nombre queso para su uso posterior.

Vamos a combinar las dos excepciones anteriores en un pequeño ejemplo:

class PizzaError(Exception):
    def __init__(self, pizza, mensaje):
        Exception.__init__(self, mensaje)
        self.pizza = pizza
        
class DemasiadoQueso(PizzaError):
    def __init__(self, pizza, queso, mensaje):
        super().__init__(pizza, mensaje)
        self.queso = queso
    
def haciendo_pizza(pizza, queso):
    if pizza not in ['margarita','romana', 'calzone']:
        raise PizzaError(pizza, "No existe tal pizza en el menú.")
    if queso > 100:
        raise DemasiadoQueso(pizza, queso, "Demasiado queso en la pizza.")
    print("¡La pizza está lista!.")
        
for (pz, qs) in [('calzone', 10), ('margarita', 110), ('cazurra',20)]:
    try:
        haciendo_pizza(pz, qs)
    except DemasiadoQueso as dmqs:
        print(dmqs, ':', dmqs.queso)
    except PizzaError as pe:
        print(pe, ':', pe.pizza)
Salida:

¡La pizza está lista!.
Demasiado queso en la pizza. : 110
No existe tal pizza en el menú. : cazurra

Una de las excepciones es generada dentro de la función haciendo_pizza() cuando ocurra alguna de estas situaciones erróneas:

- Una pizza con demasiado queso.
- Una pizza que no está en la carta.

Nota:
  • Si removemos el bloque except DemasiadoQueso, todas las excepciones que se produzcan serán PizzaError.
  • El remover el bloque que comienza con except PizzaError provocará que la excepción DemasiadoQueso no pueda ser manejada, y hará que el programa finalize.
Aunque la solución anterior funciona se necesita pasar los argumentos requeridos o sino las excepciones no funcionan. Esto se puede solucionar estableciendo valores predeterminados para todos los parámetros del constructor.

La solución sería:

class PizzaError(Exception):
    def __init__(self, pizza = 'desconocida', mensaje = ''):
        Exception.__init__(self, mensaje)
        self.pizza = pizza
        
class DemasiadoQueso(PizzaError):
    def __init__(self, pizza = 'desconocida', queso = '>100', mensaje = ''):
        super().__init__(pizza, mensaje)
        self.queso = queso
    
def haciendo_pizza(pizza, queso):
    if pizza not in ['margarita','romana', 'calzone']:
        raise PizzaError(pizza, "No existe tal pizza en el menú.")
    if queso > 100:
        raise DemasiadoQueso(pizza, queso, "Demasiado queso en la pizza.")
    print("¡La pizza está lista!.")
        
for (pz, qs) in [('calzone', 10), ('margarita', 110), ('cazurra',20)]:
    try:
        haciendo_pizza(pz, qs)
    except DemasiadoQueso as dmqs:
        print(dmqs, ':', dmqs.queso)
    except PizzaError as pe:
        print(pe, ':', pe.pizza)









domingo, 17 de octubre de 2021

Bucles en Python con for y while




Bucles en Python con For y While.


Bucles Determinados (for).

Un bucle for se utiliza para recorrer una secuencia de datos ya sea una lista, una tupla, un diccionario, un conjunto o una cadena de caracteres.

Con el bucle for podemos ejecutar un conjunto de instrucciones, una vez por cada elemento de una lista, una tupla, etc.

Una estructura típica de un bucle for sería:

for variable in elementos_a_recorrer(lista, tupla, rango etc)
    |        "
    |    Cuerpo del bucle
    |        "
    |        "

Por ejemplo podemos recorrer una palabra,  que es una cadena de caracteres, con este tipo de bucles.

> letras = "Hola"
> for letra in letras:
>    print(letra)
H
o
l
a

Otras veces se utiliza este tipo de bucle para ver los diferentes elementos que están dentro de un objeto de Python. Por ejemplo, el tipo range(n) sirve para que de forma interna se cree un objeto que contenga n elementos. Esto nos sirve para crear una especie de contador. 

> for i in range(5):
> 	print(i, end=" ")

0 1 2 3 4

Como vemos, range(5) crea un objeto que contiene 5 elementos, empezando desde el cero. También podemos indicarle que cree un objeto que empiece en un número y termine en otro. Si queremos crear un objeto con los números del 5 al 10, haríamos lo siguiente:

> for i in range(5, 11):
>	print(i, end=" ")

5 6 7 8 9 10

Lo que le hemos dicho al programa es que cree un objeto con 6 elementos (11-5) empezando desde el número 5. Incluso le podemos decir que se salte números introducciendo un tercer argumento llamado "salto", por ejemplo para que nos calcule todos los números impares del 1 al 10 podemos usar la siguiente instrucción:

> for i in range(1,11,2):
> 	print(i, end=" ")

1 3 5 7 9

Haciendo un inciso y ya que se puede recorrer sus objetos con un bucle for, vamos a ver el módulo de Python "itertoools". Este módulo como su nombre indica nos proporciona ciertas herramientas de iteración. Principalmente contiene las siguientes:

- count:    crea un objeto que nos permite contar desde un determinado valor hasta el infinito si no lo parasemos.

> from itertools import count
> for numero in count(1):
>	print(numero, end = " ")
>	if numero == 10:
>		break

Comenzamos importando el módulo count y recorriendo el objeto que genera desde el número 1.  Para que no siga hasta el infinito a través de un condicional le decimos que cuando llegue al número 10, rompa el bucle. (un poco más adelante veremos de nuevo la sentencia break un poco más explicada). Un segundo argumento indicaría el salto de un número al siguiente. La salida del programa sería:

1 2 3 4 5 6 7 8 9 10 

- cycle : realiza una iteración infinita a través de un iterable.

> from itertools import cycle
> iterable = "hola"
> for i in cycle(iterable):
>	print(i, end = " ")

Con este bucle se repetiría la palabra "hola" de forma infinita.

o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l
a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h
o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h ol
a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a h o l a...

- repeat(valor, num): repite un elemento de forma infinita o si le proporcionamos un segundo argumento un número determinado de veces.

Por ejemplo, si quisiéramos repetir el número 15, cuatro veces, haríamos lo siguiente:

> from itertools import repeat
> for i in repeat(25,4):
>	print(i)

25
25
25
25

- accumulate(objeto [, función]): este iterador usa dos argumentos, el objeto iterable y la función que se seguiría en cada iteración. Si no se pasa ninguna función, la suma se realiza de forma predeterminada. 

> # Importamos el modulo itertools para trabajar con él.
> from itertools import accumulate
 
> # Creamos una lista.
> lista = [1,2,3,4,5]

> # Creamos el iterador y lo asignamos a una variable
> resultado = accumulate(lista)

> # Imprimimos el resultado
> for i in resultado:
> 	print(i)

1
3
6
10
15

La explicación del funcionamiento es que cada número se suma al anterior y su resultado al siguiente y así sucesivamente.

1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15

- takewhile(expresión, iterable). Este iterador va cogiendo un elemento del iterable hasta que la primera vez que la expresión nos devuelve False, en la que termina. Lo vemos mejor con un ejemplo. Vamos a coger números de una lista hasta el momento en que nos encontremos con un número impar:

from itertools import takewhile
def es_par(x):
	return(x%2==0)
	
lista = [2,6,8,7,10,12]

nueva_lista = takewhile(es_par, lista)

for i in nueva_lista:
	print(i)

2 6 8

- chain(*iterables). Esta función coge varios iterables y los une en uno solo. 

from itertools import chain
 
# una lista de números impares
odd = [1, 3, 5, 7, 9]
 
# una lista de números pares
even = [2, 4, 6, 8, 10]
 
# encadena los números pares con los impares.
numbers = list(chain(odd, even))
 
print(numbers)

[1, 3, 5, 7, 9, 2, 4, 6, 8, 10]

product(*iterables, repeat=1) Nos realiza una combinación con repetición de n elementos. Por ejemplo si queremos saber todas las combinaciones posibles de dos listas de números: 

from itertools import product
 
# una lista de números 
una = [1, 2]
 
# otra lista de números 
otra = [2, 4, 6, 8, 10]
 
# combina los elementos de una con los de otras.
for i in product(una, otra):
	print(i, end=" ")

(1, 2) (1, 4) (1, 6) (1, 8) (1, 10) (2, 2) (2, 4) (2, 6) (2, 8) (2, 10)

- permutations(iterable, r=None) Retorna permutaciones de elementos sucesivos de longitud r en el iterable. 

from itertools import permutations
 
# una lista de números 
lista=[2,5,7]
 
# realiza todas las permutaciones de los numeros de la lista
for i in permutations(lista):
	print(i)

(2, 5, 7)
(2, 7, 5)
(5, 2, 7)
(5, 7, 2)
(7, 2, 5)
(7, 5, 2)

Existen muchas más funciones interesantes en itertools que puedes encontrar en la documentación de Python.


Otro cosa interesante cuando se usa un bucle es la función len(). Esta función nos devuelve el número de elementos de un objeto y nos puede venir bien para realizar una iteración en función del número de elementos que tiene un objeto.

cadena = "Daenerys"
print(f'la palabra {cadena} tiene {len(cadena)} letras')
for i in range(len(cadena)):
	print(i,'letra', cadena[i])

la palabra Daenerys tiene 8 letras
0 letra D
1 letra a
2 letra e
3 letra n
4 letra e
5 letra r
6 letra y
7 letra s


Bucles Indeterminados.


Con los bucles while, podemos ejecutar un conjunto de instrucciones mientras que la condición que se evalúa sea verdadera.

Por ejemplo:

Imprimamos la variable de control i siempre que esta sea menor de 6.

i = 1
while i < 6:
  print(i)
  i += 1
1
2
3
4
5

Con estos bucles hay que tener cierta precaución. En el ejemplo anterior si hubiésemos olvidado incrementar la variable en uno en cada giro del bucle, este se hubiese repetido de forma infinita ya que la condición siempre hubiese sido verdadera.


Instrucciones tanto para bucles for como para while.


La instrucción break

Solo se permite dentro del cuerpo del bucle. Cuando se ejecuta el bucle finaliza. Por lo general una sentencia break esta dentro de alguna clausula de una sentencia if en el cuerpo del bucle, para que break se ejecute con condiciones.

Ejemplo de un bucle que termina cuando la cuenta llega a 3.

i = 1
while i < 6:
  print(i)
  if i == 3:
    break
  i += 1

1
2
3

La instrucción continue.

Al igual que el anterior solo se admite dentro del cuerpo de un bucle. Cuando se ejecuta, la iteración actual del cuerpo del bucle finaliza, y la ejecución continua con la próxima iteración del bucle. 

Ejemplo de un bucle en el que se cuenta del 1 al 6 saltando el numero 3.

i = 0
while i < 6:
  i += 1
  if i == 3:
    continue
  print(i)

# Date cuenta como el número 3 no está en el resultado.

1
2
4
5
6


La instrucción else en los bucles.

Las sentencias while y for pueden opcionalmente incluir una clausula else posterior y al mismo nivel, porque se trata como un conjunto. La sentencia o el bloque que se situa debajo de else se ejecuta de manera natural siguiendo el flujo del programa (al final del iterador for , o cuando la condición del bucle while se convierte en falsa) pero esto no sucede cuando el bucle finaliza antes de tiempo (mediante break o una excepción por ejemplo).

email = input("Introduce un email: ")
for i in email:
	if i=="@":
		arroba = True; break
		# ; varias sentencias en una sola línea
else:
	arroba = False
print(arroba)


La sentencia pass.

El cuerpo de una sentencia compuesta en Python no puede esta vacía (como por ejemplo un bucle for, una funcion o una sentencia if), debe contener al menos una sentencia, una instrucción. Podemos usar una sentencia pass, que no realiza ninguna acción simplemente pasa, para solventar este problema.

for x in [012]:
  pass

Si lo ejecutas, veras que el bucle funciona ¡Pero no hace nada!.



sábado, 20 de marzo de 2021

Condicionales en Python



Los condicionales son sentencias de código que solo se ejecutarán si se cumple la condición establecida (operadores lógicos).

Por ejemplo, si tenemos un número almacenado en la variable x, podemos hacer el siguiente condicional:

1 x = 30

2 if  x < 20:

3     print(f"{x} es menor que 20")

4 elif x < 25:

    print(f'{x} es mayor que 20 pero menor que 25')

6 else:

    print(f"{x} es mayor que 25")


En este ejemplo el valor almacenado en la variable x es 30. El programa lee la instrucción de la línea 2 if (si) que se podría traducir como si x es menor que 20 entonces ejecuta el código que está indentado en la linea 3. Como esto no es verdad, no se ejecuta el código y se pasa a la siguiente línea, a la instrucción elif.

La palabra clave elif (de lo contrario Si) es la forma que tiene Python de decir "Si las condiciones anteriores no son verdaderas entonces prueba esta condición". En el ejemplo como x no es menor que 25 la condición es falsa, no se ejecuta el código identado y se pasa a la siguiente instrucción.

La instrucción final es else (de lo contrario), que captura cualquier cosa que no sea detectada por las condiciones anteriores, es decir que si todo lo anterior no se cumple (else = Si no) se ejecute el código identado en la línea 7.


Cuando utilizamos un condicional sin más (if Expresión:) sin poner ninguna condición, lo que se quiere decir en realidad es que si algo es True se ejecute el código de la condición. Por ejemplo:

>>> x = 30
>>> if x:
>>>     print("x es True")
True        

La expresión de la sentencia if se evalúa a False cuando en la expresión a evaluar se dan alguna de las siguientes circunstancias:

- Cualquier número igual a cero.

- Cualquier contenedor que este vacío. (lista, tupla, conjunto, diccionario)

- La expresión False o None.


Otros operadores que podemos usar son:


Operadores de identidad en Python.

Se usan para comparar los objetos, no si son iguales,  sino si en realidad son el mismo objeto, con la misma ubicación en memoria.

Operador is:

Significa una prueba de identidad, ambos lados de la expresión condicional deben ser el mismo objeto.

a = 1
b = 1
if a is b:
  print("a es idéntico que b")

SALIDA:
a es idéntico que b 

Operador is not:

Devuelve True si ambas variables no son el mismo objeto.

x = ["manzana", "plátano"]
y = ["manzana", "plátano"]
z = x

print(x is not z)

# devuelve False porque z es el mismo objeto que x

print(x is not y)

# devuelve True porque x no es el mismo objeto que y, aunque ambas tengan el mismo contenido.

print(x != y)

# para que veas la diferencia entre "is not" and "!=": Esta comparación devuelve False 
# porque x tiene el mismo contenido que y


Operadores de pertenencia en Python:

Operador in:

El operador in, significa, que para cualquier colección (lista, tupla etc) el elemento del lado izquierdo está en el lado derecho del operador.

b = [1,2,3]
if 2 in b:
   print("2 esta en la lista b")

SALIDA:
2 esta en la lista b

Operador not in:

Es el contrario del anterior, y devuelve True cuando el elemento no esta en una secuencia de datos.

b = [1,2,3]
if 5 not in b:
  print("5 no esta en la lista b")
else: 
  print("5 está en la lista b")

SALIDA:
5 no esta en la lista b


El operador not (lo contrario) devuelve True si las declaraciones no son verdaderas y False si lo son.

x = 5

print(not(x > 3 and x < 10))

SALIDA:
False
# devuelve False porque not se utiliza para dar la vuelta al resultado.


También se pueden unir varias condiciones usando los operadores lógicos:


and  (Traducido como "y")

a = 200
b = 33
c = 500
if a > b and c > a:
  print("Todas las condiciones son verdaderas")

Salida:
Todas las condiciones son verdaderas


Or (Traducido como "o")

a = 200
b = 33
c = 500
if a > b or a > c:
  print("Al menos una de las condiciones es verdadera")

Salida:
Al menos una de las condiciones es verdadera


Podemos asignar un valor a una variable de forma abreviada usando una condición es lo que se conoce como Ternary Operator u Operador Ternario ( de 3 elementos: variable, valor True y valor False). Si la condición es cierta se le asignará un valor a la variable y si es Falsa otro distinto. 

a = 7
b = 5 if a > 6 else 2
print(b)

Salida:
5
# La variable valdría 2 si la variable a no fuera mayor que 6


Los operadores de comparación que se pueden utilizar tanto en condicionales como en bucles son:

Igual: a==b
No igual: a!=b
Menor que: a < b
Menor que o igual que: a <= b
Más grande que: a > b
Más grande o igual que: a >= b


Puedes encontrar más información sobre operadores de Python en esta página de w3schools.


Uso de la declaración Switch de otros lenguajes en Python.


Hasta la versión 3.10, Python nunca tuvo una función que implementara lo que hace la instrucción switch en otros lenguajes de programación.

Entonces, si querías ejecutar varias declaraciones condicionales, tendrías que usar la palabra clave elif de esta manera:

edad = 120

if edad > 90:
    print("Ya tienes una edad considerable.")
elif edad < 0:
    print("Aun no has ni nacido")
elif edad >= 18:
    print("¿Ya puedes ir de fiesta?")
else: 
    print("Aun te queda mucho por delante.")

# Salida: Ya tienes una edad considerable.

Desde la versión 3.10 en adelante, Python ha implementado esta característica con las palabras clave "match" y "case

Para escribir declaraciones de este tipo se puede seguir la siguiente estructura:

match término:
    case patrón-1:
         acción-1
    case patrón-2:
         acción-2
    case patron-3:
         acción-3
    case _:
        acción_por_defecto

Ten en cuenta que el símbolo de subrayado es lo que se usa para definir un caso por defecto en Python.

A continuación vamos a ver un ejemplo con esta nueva funcionalidad.

vocal = input("Introduzca una vocal: ")

match vocal:
    case 'a':
        print("la vocal es la a")
    case 'e':
        print("la vocal es la e")
    case 'i':
        print("la vocal es la i")
    case 'o':
        print("la vocal es la o")
    case _:
        print("la vocal es la u u otra cosa")