Apuntes_Python/01_curso/Modulo_2/README.md
2022-12-24 22:41:20 -03:00

2492 lines
59 KiB
Markdown

**Ir a:**
[*Repositorio*](https://gitea.kickto.net/devfzn/Apuntes_Python),
[*Modulo 1*](https://gitea.kickto.net/devfzn/Apuntes_Python/src/branch/master/01_curso/Modulo_1#modulo-1-python-basico),
[*Modulo 3*](https://gitea.kickto.net/devfzn/Apuntes_Python/src/branch/master/01_curso/Modulo_3#modulo-3-python-basico)
## Modulo 2 - Python basico
#### [Indice](#modulo-2-python-basico)
- [Strings Format](#strings)
- [Decodificadores de strings](#decodificadores-de-strings)
- [Función join](#join)
- [Metodos de Strings](#metodos-de-strings)
- [Constantes de la libreria string](#constantes-que-define-la-libreria-string)
- [Formateo de strings](#formateo-de-strings)
- [Fecha y Hora](#fechas-date)
- [Datetime](#datetime-hora-y-fechas)
- [Conversion a string](#tiempo-a-string)
- [Excepciones](#excepciones)
- [Excepcion personalizada](#excepción-personalizada)
- [Context manager](#context-manager)
- [Manejo de excepción IndexError](#manejo-de-excepción-indexerror)
- [Otro ejm. excepción personalizada](#ejemplo-de-excepción-personalizada)
- [Input/Output strings](#input-output-string)
- [Leer y escribir archivos](#leer-y-escribir-archivos)
- [Leer Archivos](#leer-archivos)
- [Escribir en archivos](#escribiendo-en-archivos)
- [Leer, Escribir CSV](#leer-y-escribir-csv)
- [Datos estructurados JSON](#json)
- [Matrices, Conjuntos](#matrices)
- [Listas, Pilas, Colas, Stacks](#listas)
- [Funciones](#algunas-funciones)
- [Listas como Pilas](#listas-como-pilas)
- [Listas como Colas](#listas-como-colas)
- [Listas por comprensión](#listas-por-comprension)
- [Buscar en lista](#buscar-elemento-en-una-lista)
- [Listas por comprensión 2](#comprension-de-listas-2)
- [Tuplas y secuencias](#tuplas)
- [Diccionarios](#diccionarios)
- [Operaciones sobre diccionarios](#las-operaciones-principales-son)
- [Acceso a elementos](#acceso-a-elementos-por-claves)
- [Metodos](#metodos-en-los-diccionarios)
- [Iterando diccionarios](#iterando-diccionarios)
- [Resumen](#resumen-diccionarios)
- [Generadores](#generadores)
- [Ejemplos](#uso-de-generadores)
- [Decoradores](#decoradores)
- [Ejercicios](#ejercicios-modulo-2)
- [Verdurería](#verdureria)
- [El juego del Gato](#el-gato)
----
## Strings
Cadenas(*secuencia*) de caracteres
```python
'Str' "Str"
"""
Para multilinea usar 3 pares de cualquier tipo
"""
a_str = 'Hola Mundo!'
# * Acceso a caracter según posición
a_str[0] # retorna H
a_str[-1] # retorna !
# * Slicinf de un string
a_str[:4] # retorna HOLA
a_str[5:9] # retorna Mundo
# * Longitud String
len(a_str) # retorna 11
```
Caracter de escape **` \ `**, para acceder a las secuencias de escape comunes.
Ej.: **` \' `**, **` \" `**, **` \n `**, **` \r `**, **` \t `**.
### Decodificadores de strings
ej. codificar (de unicode a un byte array con codif. determinada)
decodificar ( de un byte array con una codif. determ. a unicode)
```python
a_str = 'Otoño'
# utf-8 encode
str_codific = a_str.encode('utf-8') # Respuesta: b'Oto\\xc3\\xb1o'
# utf-8 decode
str_codific.decode('utf-8') # Respuesta: 'Otoño'
# Unicode es la ley
# ASCCI no tiene estos caracteres.. lo cual da error
# str2 = "áéíóú"
# str3 = str2.encode('ascii')
```
### Strings y variables
```python
nombre = "Reberte"
"Hola %s" % nombre
# 'Hola Reberte'
"Un nro. %d" % 5
# 'Un nro. 5'
"Nr0. de 2 digitos %02d" % 5
# 'Nr0. de 2 digitos 05'
"Un float %f" % 6.4
# 'Un float 6.400000'
"Float 2 digitos %.2f" % 2.54
# 'Float 2 digitos 2.54'
"Hola %(name)s" % {'name': nombre }
# 'Hola Reberte'
"Hola {}".format(nombre)
# 'Hola Reberte'
"{0} + {1} es {2}".format(7,2,7+2)
# '7 + 2 es 9'
```
### join
Construye strings concatenando lista de valores
```python
' ',join(["Hola", nombre])
', '.join(['1','2','3','4'])
```
## Metodos de Strings
python -i
`>>> help(str)`
```
METODOS DE LOS STRINGs
NOMBRE ej. Resultado
capitalize 'word'.capitalize() 'Word'
center 'word'.center(10,'*') '**word**'
count 'word'.countd(d) 1
encode 'word'.encode('utf-8') b'word'
endswith 'word'.endswith('d') True
find 'wordw'.find('w') 0
rfind 'wordw'.rfind('w') 4
format 'Hola {}'.format('Elmo') 'Hola Elmo'
index 'wordw'.index('w') 0
rindex 'wordw'.rindex('w') 4
isalnum 'word'.isalnum() True
isalpha 'word'.isalpha() True
isdecimal '10'.isdecimal() True
isdigit '10'.isdigit() True
islower 'woRd'.islower() False
isnumeric '23'.isnumeric() True
isprintable 'word'.isprintable() True
isspace ' '.isspace() True
islitle 'Two Words'.islitle() True
isupper 'WORD'.isuper() True
lower 'woRd'.lower() 'word'
replace 'woRd'.replace('R','rl') 'world'
split '1-2-3-4'.split('-') ['1', '2', '3', '4']
startwith 'word'.startwith('w') True
strip ' word '.strip() 'word'
lstrip
rstrip
ljust 'word'.ljust(8) 'word '
rjust
swapcase 'WoRd'.swapcase() 'wOrD'
title 'a title'.title() 'A Title'
upper 'word'.upper() 'WORD'
zfill 'word'.zfill(10) '000000word'
rindex 'word'.
```
#### Constantes que define la libreria string
- **ascii_letters** :
La concatenación de las letras minúsculas y letras mayúsculas.
`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ`
- **ascii_lowercase** :
Las letras minúsculas. `abcdefghijklmnopqrstuvwxyz`
- **ascii_uppercase** :
Las letras mayúsculas. `ABCDEFGHIJKLMNOPQRSTUVWXYZ`
- **digits** :
Los dígitos del sistema decimal. `0123456789`
- **hexdigits** :
Los dígitos del sistema hexadecimal. `0123456789abcdefABCDEF`
- **octdigits** :
Los dígitos del sistema octal. `01234567`
- **punctuation** :
Símbolos de puntuación. `!"#$%&\'()*+,-./:;<=>?@[\\]^_\`{|}~`
- **printable** :
Todos los caracteres considerados imprimibles.
```
0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJK
MNOPQRSTUVWXYZ!"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~
\t\n\r\x0b\x0c
```
- **whitespace** :
Todos los caracteres considerados espacios en blanco. `\t\n\r\x0b\x0c`
### Formateo de strings
```python
name = 'Reberte'
print('Hola {}'.format(name))
# 'Hola Reberte'
print('{} + {} = {}'.format(2,5,7))
# '2 + 5 = 7'
print('{1} + {2} = {0}'.format(7,5,2))
# '5 + 2 = 7'
print('Hola {nombre}'.format(nombre = name))
# 'Hola Reberte'
tupla = 4, 3
type(tupla)
# <class 'tuple'>
print('X: {0[0]}; Y: {0[1]}'.format(tupla))
# 'X: 4; Y: 3'
print('{0:f} + {1:f} = {result:f}'.format(2, 5, result=7))
'2.000000 + 5.000000 = 7.000000'
print('{0:.3f} + {1:.3f} = {result:.3f}'.format(2, 5, result=7))
# '2.000 + 5.000 = 7.000'
print('{:d}'.format(25))
# '25'
# '{:d}'.format(25.5)
# Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# ValueError: Unknown format code 'd' for object of type 'float'
print('{:.0f}'.format(25.50))
# '26'
print('Hola {nombre:16}'.format(nombre=name))
# 'Hola Reberte '
print('Hola {nombre:<16}'.format(nombre=name))
# 'Hola Reberte '
print('Hola {nombre:>16}'.format(nombre=name))
# 'Hola Reberte'
print('Hola {nombre:^16}'.format(nombre=name))
# 'Hola Reberte '
print('Hola {nombre:*^16s}'.format(nombre=name))
# 'Hola ****Reberte*****'
```
----
## Fechas (date)
```python
import datetime
# date object
# 18-Jun-20
fecha = datetime.date(2020, 6, 18)
fecha.year # 2020
fecha.month # 6
fecha.day # 18
fecha.weekday() # Lunes 0 -> Domingo 6
fecha.isoweekday() # Lunes 1 -> Domingo 7
fecha.isocalendar() # tuple( año, semana, dia semana)
fecha.isoformat() # YYY-MM-DD
```
### timedelta()
```python
hoy = datetime.date.today()
ayer = hoy - datetime.timedelta(days=1)
delta = hoy - ayer # 1 day, 0:00:00
```
### Datetime ( hora y fechas )
```python
fecha_hora = datetime.datetime(2020, 12, 2, 9, 15, 30)
# (YYYY, MM, D, h, m , s )
print(fecha_hora.date()) # 2020-12-02
# formato local
ahora = datetime.datetime.now()
# UTC
ahora = datetime.datetime.utcnow()
```
### Tiempo a string
```python
hora = datetime.time(10, 40, 35)
hora.hour
hora.minute
hora.second
hora.microsecond
fecha = datetime.datetime(2020, 4, 30, 11, 25, 30)
```
- Conversion DateTime a String # strftime
- `fecha.strftime('%Y-%m-%d')`
- `fecha.strftime('%Y-%m-%d' T%H:%M:%S')`
- `fecha.strftime('%Y-%m-%d %H:%M:%S')`
- Conversion String a DateTime # strptime
- `fecha.strptime('2020-01-10', '%Y-%m-%d')`
- `fecha.strptime('2020-01-10 T11:30:25', '%Y-%m-%d' T%H:%M:%S')`
- `fecha.strptime('2020-01-10 11:30:25','%Y-%m-%d %H:%M:%S')`
----
## Excepciones
Hay principalmente 2 tipos de errores: de sintaxis y excepciones
```python
while True :
try:
x = int(input('Excepcion si no se ingresa un ENTERO ->'))
break
except ValueError:
print('Se puede salir con Ctrl-C')
```
```python
while True :
try:
x = int(input('Excepcion si no se ingresa un ENTERO ->'))
break
except (ValueError, KeyboardInterrupt):
print('Ya, no se puede salir y con Ctrl-C')
```
```
... except (RuntimeError, TypeError, NameError):
... pass
```
```python
class B(Exception):
pass
class C(B):
pass
class D(C):
pass
for cls in [B, C, D]:
try:
raise cls()
except D:
print("D", D)
except C:
print("C", C)
except B:
print("B", B)
print('')
```
```python
for cls in [B, C, D]:
try:
raise cls()
except B:
( print("B")
except D:
print("D")
except C:
print("C")
print('')
```
```python
import sys
try:
f = open('mifile.txt')
s = f.readline()
i = int(s.strip())
except OSError as err:
print("Error OS: {0}".format(err))
except ValueError:
print("No pude convertir el dato a entero.")
except:
print("Error inesperador: ", sys.exc_info()[0])
raise
#for arg in sys.argv[1:]:
# try:
# f = open(arg, 'r')
# except OSError:
# print('no pude abrir', arg)
# else:
# print(arg, 'tiene', len(f.readlines()), 'lineas')
# f.close()
```
*python -i*
```python
try:
raise Exception('carne', 'huevos')
except Exception as inst:
print(type(inst))
print(inst.args)
print(inst)
#x, y = inst
#print('x: ',x ,'y: ',y)
```
```python
>>> raise RuntimeError('Error error bip bap bip! no computa! bip bap!')
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
RuntimeError: Error error bip bap bip! no computa! bip bap!
```
### Relanzando la excepción para ser manejada en otro contexto.
```python
try:
raise NameError('y bueno un error común')
except NameError:
print('ee.. salto un except!')
raise
```
-----
### Excepción Personalizada
```python
class Error(Exception):
"""Clase base para excepciones en el módulo"""
pass
```
### Acciones de limpieza
```python
class EntradaError(Error):
"""Excepción lanzada por error en las entradas.
Atributos:
expresion -- expresión de entrada en la que ocurre el error
mensaje -- explicación del error
"""
def __init__(self, expresion, mensaje):
self.expresion = expresion
self.mensaje = mensaje
```
```python
class TransicionError(Error):
"""Lanzada por operación que intenta transición de estado no permitida.
Atributos:
previo -- estado al conmenzar la transición
siguiente -- nuevo estado intentado
mensaje -- explicación de transición no permitida
"""
def __init__(self, previo, siguiente, mensaje):
self.previo = previo
self.siguiente = siguiente
self.mensaje = mensaje
```
### finally
```python
try:
#raise KeyboardInterrupt
pass
finally:
print('hasta pronto!')
def dividir(x, y):
try:
res = x / y
except ZeroDivisionError:
print("¡división por cero!")
else:
print("el resultado es: ", res)
finally:
print('bloque finally')
dividir(2, 1)
dividir(2, 0)
```
### Context manager
Esta sentencia deja el archivo abierto tras usarlo
```python
for linea in open("miarchivo.txt"):
print(linea, end="")
# with asegura liberar el recurso tras su uso
with open("miarchivo.txt") as f:
for linea in f:
print(linea, end="")
```
### Manejo de excepción IndexError
```python
def find(elemento, lista):
"""Devuelve el indice del @elemento en @lista.
Si no lo encuentra retorna -1
"""
index = 0
while True:
try:
if lista[index] == elemento:
return index
except IndexError:
return -1
index += 1
# ejemplos de find
print(find(4,[2,3,4,5])) # 2
print(find(2,[2,3,4,5])) # 0
print(find(9,[2,3,4,5])) # -1
```
### Ejemplo de excepción personalizada
```python
class MiExcepcion(Exception):
"Excepcion personalizada"
pass
def fun_ejm(numero):
if numero <= 0:
raise MiExcepcion("nro negativ. o cero")
return 1/numero
print(fun_ejm(10))
print(fun_ejm(-4))
```
----
## Input Output String
- **repr(), rjust()**
```python
for x in range(1, 11):
print(repr(x).rjust(2), repr(x * x).rjust(3), repr(x * x * x).rjust(4))
# 1 1 1
# 2 4 8
# 3 9 27
# 4 16 64
# 5 25 125
# 6 36 216
# 7 49 343
# 8 64 512
# 9 81 729
# 10 100 1000
```
```python
for x in range(1, 11):
print('{0:2d} {1:3d} {2:4d}'.format(x, x * x, x * x * x))
# 1 1 1
# 2 4 8
# 3 9 27
# 4 16 64
# 5 25 125
# 6 36 216
# 7 49 343
# 8 64 512
# 9 81 729
# 10 100 1000
```
- **zfill()**
```python
print('12'.zfill(5), end='\n' * 2)
# 00012
```
- **format()**
```python
print('Somos {}, pero otras "{}!"'.format('celestes', 'magenta'))
# Somos celestes, pero otras "magenta!"
print('Somos {1}, cantamos "{0}"'.format('aves', 'pio-pio'))
# Somos pio-pio, cantamos "aves"
print('Este {cosa} es {adjetivo}.'.format(cosa='loro', adjetivo='azul'))
# Este loro es azul.
print('Este {0} es {1}!, le pongo un {nota}'.format('color', 'muy llamativo', nota=7.456))
# Este color es muy llamativo!, le pongo un 7.456
```
**Convertir valor antes que se formatee**
```python
# !a apply()
# !s str()
# !r repr()
var = 'ratas'
print('mi yate esta lleno de {!r}.'.format(var))
# mi yate esta lleno de ratas.
```
```python
from math import pi
print('El valor de PI es aprox. {0:.3f}.'.format(pi), '\n'*2)
# El valor de PI es aprox. 3.142.
print('\n El valor de Pi es app. %.2f.' % pi)
# El valor de Pi es app. 3.14.
tabla = {'Sjoerd': 4127, 'Jack': 4098, 'Dcab': 76789877}
for nombre, telefono in tabla.items():
print('{0:8} ==> {1:8d}'.format(nombre, telefono))
# Sjoerd ==> 4127
# Jack ==> 4098
# Dcab ==> 76789877
print('\n', 'Jack: {0[Jack]:d}; Sjoerd: {0[Sjoerd]:d}; '
'Dcab: {0[Dcab]:d}'.format(tabla))
# Jack: 4098; Sjoerd: 4127; Dcab: 76789877
print('\nJack: {Jack:d}; Sjoerd: {Sjoerd:d}; '
'Dcab: {Dcab:d}'.format(**tabla))
# Jack: 4098; Sjoerd: 4127; Dcab: 76789877
```
### Leer y escribir archivos
```python
mi_file = open('nombre_archivo', 'modo')
-r read (default)
-r+ read/write
-w write
-a append
-x ?
```
- **read()**
```python
# El context manager cierra el archivo despues de usarlo
with open('archivo') as f:
datos_leidos = f.read()
```
- **readline()**
lee linea a linea
- **f.close()**
cierre de archivo aribitrario.
```python
print(f.closed) <-- 'True' si el archivo se cerro.
list(f) ó f.readlines() <-- pare lee todas las lineas del archivo.
f.write('cadena a escribir\n')
18
s = str(valor) # valor = ('respuesta', 16)
f.write(s)
15
```
- **f.tell()**
Devuelve la posición actual en el archivo, representada como número
de bytes desde elcomienzo del archivo en modo binario y
un número opaco en modo texto.
- **f.seek()**
*python -i*
```python
f.seek(desplazamiento, desde_donde)
>>> f =open('archivodetrabajo', 'rb+')
>>> f.write(b'0123456789abcdef')
>>> f.seek(5)
# Va al sexto byte en el archivo
5
>>> f.read(1)
b'5'
>>> f.seek(-3, 2) # Va al tercer byte antes del final
13
>>> f.read(1)
b'd'
```
otros:
- isatty()
- truncate()
-----
## Leer Archivos
```python
# Abrir archivo
arch = open('./python_subprocess_bash', 'r')
# Leer contenido
arch.read()
# Cerrar el arrchivo
arch.close()
```
> **Context manager:**
> en este caso **with** abre y cierra el archivo
> **read()** lee todo el contenido del archivo
```python
with open('./python_subprocess_bash', 'r') as archivo:
print(archivo.read())
```
### Lee por linea
```python
with open('./python_subprocess_bash', 'r') as archivo:
print(archivo.readline())
```
### Genera una lista con las lineas del archivo
```python
with open('./python_subprocess_bash', 'r') as archivo:
print(archivo.readlines())
```
### Genera una lista con las lineas del archivo
```python
with open('./python_subprocess_bash', 'r') as archivo:
print(list(archivo))
```
### For recorre linea a linea
```python
with open('./python_subprocess_bash', 'r') as archivo:
for linea in archivo:
print(linea)
```
----
## Escribiendo en archivos
```python
import subprocess
arch = 'python_subprocess_bash'
with open(arch, 'w') as archivo:
archivo.write('Hola mundo')
subprocess.check_call(["cat", arch])
print('')
```
### Escribir multiples lineas.
```python
with open(arch, 'w') as archivo:
archivo.writelines(['Linea 1.\n', 'Linea 2.\n', 'Linea 3.\n', 'Linea 4.\n'])
subprocess.check_call(["cat", arch])
```
### Anexadon al final de archivo. 'a'
```python
with open(arch, 'a') as archivo:
archivo.write('!odnuM aloH')
subprocess.check_call(["cat", arch])
```
----
## Leer y escribir CSV
### Leer CSV
`csv.reader(archivo.csv)`
```python
import csv
with open('ejm.csv', 'r') as csv_arch:
reader = csv.reader(csv_arch)
for row in reader:
#print('\t'.ljust(2).join(row))
print('{0:8}\t{1:8}\t{2:8}\t{3:8}'.format(*row))
#print('{0:8}\t{1:8}\t{2:8}\t{3:8}'.format(row[0], row[1], row[2], row[3]
#print('\n', help(row)) # las filas son listas
```
### Escribir CSV
`csv.writer(arch.csv).writerow(['elementos','a','escribir'])`
```python
with open('ejm.csv', 'a') as csv_arch:
writer = csv.writer(csv_arch)
writer.writerow(['Elvira', 'Zápáté', '34467845', 'lervielpare@zumail.zen'])
```
----
## JSON
**Java Scrit Object Notation**
El módulo estandar llamado json puede tomar datos de Python con una jerarquía, y convertirlo
a representaciones de cadena de caracteres. Este proceso es llamado **serializing**.
Reconstruir los datos desde la representación de cadena de caracteres es llamado **deserializing**.
El formato JSON es comunmente usado por aplicaciones modernas para permitir intercambiar datos.
```python
import json
```
### Serializar un objeto
```python
# Transforma el mismo a una representacion de caracteres.
json.dumps([1, 2, 3])
# '[1, 2, 3]'
```
### Deserializar una cadena
```python
# Transforma la cadena a un objeto json
json.loads('[1, 2, 3]')
# [1, 2, 3]
```
### Escribir como json a un archivo
```python
with open('archivo.json', 'w') as arch:
json.dump([1, 2, 3, 4], arch)
```
### Leer un json
```python
with open('archivo.json', 'r') as arch:
print( json.load(arch) )
```
### Otro ejemplo
```python
x = [1, 'simple', 'lista']
f = open('test_file_json', 'r+')
json.dumps([1, 'simple','lista'])
# Suponiendo q f es un archivo de texto abierto para escritura:
json.dump(x, f)
f.close()
f = open('test_file_json', 'r')
# Suponiendo q f fue abierto para lectura:
print(json.load(f))
f.close()
```
----
## Matrices
Se pueden considerar como listas de listas,
lo visto en listas aplica tambien en matrices
### Definción de matriz de 3 filas y 4 columnas.
```python
matriz = [[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]
# Acceso a elementos matriz[ fila o lista1 ] [ columna o lista2 ]
matriz[0][0]
# 1
matriz[1][2]
# 7
# Ejm. suma de matrices
def suma_matr(A, B):
"""
Suma 2 matrices.
A: len(A) == len(B), integers.
B: len(A) == len(B), integers.
returns: Matriz con resulta de la suma de
los elementos de A y B
"""
filas, colums, C = len(A), len(A[0]), []
for fila in range(filas):
fila_temp = []
for columna in range(colums):
fila_temp.append(A[fila][columna] + B[fila][columna])
C.append(fila_temp)
return C
print(suma_matr(matriz, matriz))
# [[2, 4, 6, 8], [10, 12, 14, 16], [18, 20, 22, 24]]
```
> Libreria **NumPy** para trabajar con matrices
## Conjuntos
Un conjunto esta entre llaves no tiene elementos repetidos.
```python
frutas = {'manzana', 'naranja', 'manzana', 'pera', 'naranja', 'banana', 'kiwi'}
print(frutas) #{'kiwi', 'naranja', 'manzana', 'banana', 'pera'}
print('pera' in frutas, 'yerba' in frutas) # True False
```
### Creacion de conjuntos
```python
conj_a = set()
type(conj_a) #<class 'set'>
a = set('abracadabra') #{'r', 'a', 'b', 'd', 'c'}
b = set('alacazam') #{'l', 'm', 'a', 'z', 'c'}
print('\n a =',a ,' b =',b)
```
### Operaciones de conjuntos
```python
a - b # {'d', 'b', 'r'} elementos de a menos elementos de b
a | b # {'l', 'd', 'a', 'z', 'm', 'c', 'b', 'r'} elementos de a y b
a & b # {'c', 'a'} elementos en común (INTERSECCION)
a ^ b # {'l', 'z', 'b', 'm', 'd', 'r'} elementos únicos de cada set
```
### Comprensión de conjuntos
```python
a = {x for x in 'abracadabra' if x not in 'abc'}
a.add('z')
a.remove('z')
print('\n', a)
# {'r', 'd'}
```
### Ayuda sobre la clase set
python -i `>>>help(set)`
```
class set(object)
set() -> new empty set object
set(iterable) -> new set object
Build an unordered collection of unique elements.
Methods defined here:
add(...)
Add an element to a set.
This has no effect if the element is already present.
clear(...)
Remove all elements from this set.
copy(...)
Return a shallow copy of a set.
difference(...)
Return the difference of two or more sets as a new set.
(i.e. all elements that are in this set but not the others.)
difference(...)
Return the difference of two or more sets as a new set.
(i.e. all elements that are in this set but not the others.)
difference_update(...)
Remove all elements of another set from this set.
discard(...)
Remove an element from a set if it is a member.
If the element is not a member, do nothing.
intersection(...)
Return the intersection of two sets as a new set.
(i.e. all elements that are in both sets.)
intersection_update(...)
Update a set with the intersection of itself and another.
isdisjoint(...)
Return True if two sets have a null intersection.
issubset(...)
Report whether another set contains this set.
issuperset(...)
Report whether this set contains another set.
pop(...)
Remove and return an arbitrary set element.
Raises KeyError if the set is empty.
remove(...)
Remove an element from a set; it must be a member.
If the element is not a member, raise a KeyError.
symmetric_difference(...)
Return the symmetric difference of two sets as a new set.
(i.e. all elements that are in exactly one of the sets.)
symmetric_difference_update(...)
Update a set with the symmetric difference of itself and another.
union(...)
Return the union of sets as a new set.
(i.e. all elements that are in either set.)
update(...)
Update a set with the union of itself and others.
```
----
## Listas
Permiten guardar todo tipo de objetos
```python
lista = [3, 7.5, 'Hola', 7j + 5, [1, 2]]
# Acceso mediante indexacion
lista[0] # 3
lista[2] # 'Hola'
lista[-1] # [1, 2]
# Slicing
lista[1:] # [7.5, 'Hola', (5+7j), [1, 2]]
lista[1:2] # [7.5]
lista[1:3] # [7.5, 'Hola']
lista[:2] # [3, 7.5]
lista[:] # [3, 7.5, 'Hola', (5+7j), [1, 2]]
```
### Algunas funciones
```python
# Cantidad de elementos
len(lista)
# 5
# Agrega al final
lista.append(2)
# Extiende la lista con elementos de otra lista
# "o tupla" lista.extend((1, 2))
lista.extend([3, 4])
# Insertar elemento en posición
lista.insert(4, 'Intercalado')
lista.insert(12, 'Fuera de Rango')
lista.insert(-1, 'Hacia atrás')
# Cuenta coincidencias con argumento
lista.count(3)
# Elimina el 1er elemento encontrado
lista.remove(3)
# Copia la lista superficialmente (mutable)
copia_lista = lista.copy()
# Saca ultimo elemento, o el indicado en el indice
lista.pop()
# Limpiar lista
lista.clear()
```
```python
# Lista ls
ls = [1, 2, 3, 4]
x = 1
ls.append(x)
itr = range(10)
ls.extend(itr)
i = 0 # indice
ls.insert(i, -1)
# Elimina el primer valor encontrado (error si no ecuentra)
ls.remove(3)
# Retorna y remueve según indice
ls.pop(2)
# Borrar lista
ls.clear()
ls = [1, 2, 3, 4]
x = 2
# Devuelve la posicion del primer obejto X encontrado:
# ls.index(x, desde[0], hasta[len(ls)]) [default]
ls.index(x)
# Cantidad de incidencias de x
ls.count(x)
# Ordena lista segun alguna funcion key=nombrefuncion
ls.sort(key=None, reverse=False)
# Invierte orden lista
ls.reverse()
# Copia lista equiv. ols = ls[:]
ols = ls.copy()
```
### Funciones listas y strings
Tanto para **listas (mutables)** como para **strings (inmutables)**
```python
name = 'Reberte'
lista = list(name)
# indexado
name[0]
lista[0]
# Slicing
name[:4]
lista[:4]
# len
len(name)
len(lista)
# in
'R' in name
'R' in lista
# not
'z' not in name[0]
'z' not in lista[0]
# for
for letra in name:
print(letra)
for letra in lista:
print(letra)
# String son inmutables
lista[2] = 'o'
#name[2] = '0' # TypeError
'Hola' + name
name + '!!'
name[:2] + 'to' + name[2:]
```
### Listas como pilas
Funcionamiento de "**pilas**" (apilar)
> Último en entrar, Primero en salir **FiFo**
> **LiFo** (last in first out)
```python
pila = [1, 2, 3]
# insterar elemento
pila.append(4)
pila.append(5)
# sacar elemento
pila.pop() #5
pila.pop() #4
pila.pop() #3
```
### Listas como colas
> **FiFo** (first in first Out)
> Diseñado para trabajar con ambos extremos de la "lista"
> Se usa "collections.deque"
```python
from collections import deque
cola = deque(["Javier","Jimena","Jorge"])
cola.append("Jaqueline")
cola.append("Jose")
cola.popleft() # "Javier"
cola.popleft() # "Jimena"
cola # ["Jorge","Jaqueline","Jose"]
# LISTAS COMO COLAS 2
# FiFo (First in First Out)
cola = [1, 2, 3]
cola.append(4)
cola.append(5)
cola.pop() #1
cola.pop() #2
# Colas eficientes en libreria estandar
cola = deque([1, 2, 3])
# Agregar
cola.append(4)
cola.append(5)
cola.popleft() #1
cola.popleft() #2
```
### Listas por comprension
> Usualmente, se crean para generar listas donde cada elemento es el resultado
> de aplicar una o mas operaciones a cada miembro de una secuencia o itreable
```pyhton
# Lista de Cuadrados
cuads = []
for x in range(10):
cuads.append(x**2)
# Lista por comprension
cuads2 = [x ** 2 for x in range(10)]
# Utilizando la funcion map
cuads3 = list(map(lambda x: x**2, range(10)))
lista = [-4, -2, 0, 2, 4]
# Lista por comprensión contiene los nrs. positivos de 'lista'
[x for x in lista if x >= 0] #[0, 2, 4]
# Lista con los positivos usando funcion filter [2, 4]
list(filter(lambda x: x > 0, lista))
# Pares y su cuadrado [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
[(x, x ** 2) for x in range(6)]
# Lista de pares combinados
pares = [(x, y) for x in [1, 2, 3] for y in [3, 1, 4] if x != y]
```
### Buscar elemento en una lista
```python
ls = [1, 2, 3, 4]
# Retorna el indice si encuentra el elemento
ls.index(4)
# lanza excepecion ValueError si no
# ls.index(9)
# Indicando una sublista
# ls.index(x, desde[0], hasta[len(ls)])
ls.index(4, 1)
#ls.index(4, 0, 2) # ValueError: 4 is not in list
ls.index(4, 1, 4)
```
### Metodos de ordenamiento
```python
ls = [3, 1, 2, 9, 5, 4, 7, 8, 6]
# Ordena < a >
ls.sort()
ls.sort(reverse=True)
ls = [(1, 9), (1, 3), (1, 4), (1, 2)]
ls.sort(key=lambda x: x[1])
# Revertir el orden de los elementos
ls = [3, 1, 2, 9, 5, 4, 7, 8, 6]
ls.reverse()
# Retorna lista ordenada
sorted(ls)
sorted(ls, reverse=True)
ls = [(1, 9), (1, 3), (1, 4), (1, 2)]
sorted(ls, key=lambda x: x[1])
# Ordenar según la suma de pares de elementos, en la lista en sí, ascendente (sort(default))
ls = [(6, 2), (1, 5), (2, 3), (4, 1), (5, 2), (1, 3)]
ls.sort(key=lambda x: x[0]+x[1])
```
### Comprension de listas 2
```python
# Creando una lista de los primeros 10 cuadrados matematicos
cuads = []
for x in range(10):
cuads.append(x**2)
cuads # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
# Mas eficiente:
cuads = list(map(lambda x: x**2, range(10)))
# aún mas:
cuads = [x ** 2 for x in range(10)]
```
```python
# Esta lista de comprensión combina los elementos de 2 listas
# si no son iguales
[(x, y) for x in [1, 2, 3] for y in [3, 1, 2] if x != y]
# [(1, 3), (1, 2), (2, 3), (2, 1), (3, 1), (3, 2)]
# Equivalente a:
cuads = []
for x in [1, 2, 3]:
for y in [3, 1, 4]:
if x != y:
cuads.append((x, y)) #tupla
#append recive un argumento
vec = [-4, -2, 0, 2, 4]
# Lista con valores*2
[x * 2 for x in vec] # [-8, -4, 0, 4, 8]
# Filtrar excluyendo nros. negativos
[x for x in vec if x >= 0] #[0, 2, 4]
# Aplicar funcion a elementos
[abs(x) for x in vec] #[4, 2, 0, 2, 4]
# Metodo a elemento
fruta = [' Banana ',' Murtilla ',' Tuna ']
[arma.strip() for arma in fruta] #['banana', 'mora de Logan', 'maracuya']
# Lista de Tuplas de dos elementps (nro y nro al cuadrado)
[(x, x ** 2) for x in range(6)]
# [(0, 0), (1, 1), (2, 4), (3, 9), (4, 16), (5, 25)]
# Aplanar una lista (con 2 for)
vec = [[1, 2, 3], [4, 5, 6],[7, 8, 9]]
[num for elem in vec for num in elem]
# [1, 2, 3, 4, 5, 6, 7, 8, 9]
# Pueden tener expresiones complejas y funciones anidadas
from math import pi
[str(round(pi, i)) for i in range(1, 6)]
# ['3.1', '3.14', '3.142', '3.1416', '3.14159']
```
### Listas por comprension anidadas
> La expresión inicial de una comprensión de listas puede ser cualquier expresión arbitraria,
> incluyendo otra comprensión de listas.
```python
matriz =[[1, 2, 3, 4], # ej. matrix 3x4
[5, 6, 7, 8], # lista de 3 listas de largo 4
[9,10,11,12]]
# Tansponer filas y columnas:
[[fila[i] for fila in matriz] for i in range(4)]
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
transp = []
for i in range(4):
transp.append([fila[i] for fila in matriz])
transp
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
# es lo mismo que:
transp = []
for i in range(4):
# comprension de listas anidada
fila_tra= []
for fila in matriz:
fila_tra.append(fila[i])
transp.append(fila_tra)
transp
# [[1, 5, 9], [2, 6, 10], [3, 7, 11], [4, 8, 12]]
# Existen funciones predefinidias a declaraciones con flujo complejo
# por ej. zip(), para el caso anterior:
list(zip(*matriz)) # ** desempaquetando lista de argumentos
print(list(zip(*matriz)))
# [(1, 5, 9), (2, 6, 10), (3, 7, 11), (4, 8, 12)]
```
### La instrucción del
> Elimina uno o mas items de la lista según indice, elimina sección o
> lista completa, también se usa para eliminar variables.
```python
a = [-1, 1, 66.25, 333, 333, 1234.5]
del a[0] # quita -1
del a[2:4] # quita 333 y 333
del a[:] # vacía la lista
del a # elimina la variable
```
----
## Tuplas
Una tupla consiste en valores separados por coma, una tupla va entre parentesis
```python
tupla = 12345, 54321, 'hola!'
tupla[0] #12345
tupla #(12345, 54321, 'hola!')
# las tuplas pueden anidarse
u = tupla, (1,2,3,4,5,6)
# las tuplas son inmutables
#tupla[0] = 88888
#TypeError
# Pero pueden contener objetos mutables
# Tuplas con objetos mutables como listas
# TUPLAS INMUTABLES
# LISTAS MUTABLES
# Creación de tupas
vacia = ()
single = 'hola',
len(vacia) #0
len(single) #1
# Empaquetado de tupla
tupla = 1234, 3421, 'hola!'
# Tb es posible asignar los elementos a variables
# Desempaquetado de secuencias
x, y, z = tupla
# Las tuplas son secuencias ordenadas de valores
# ejemplos
tupla = (1, 2.5, 'Hola')
tupla[0] #1
tupla[1] #2.5
tupla[2] #'Hola
tupla[:2] # (1, 2.5)
# Tulpa vacia
tupla_vacia1 = ()
tupla_vacia2 = tuple()
# Tupla de un elemento
tupla_2 = (5, )
# numero_no_tupla = (5)
# longitud de tupla
len(tupla)
```
### Empaquetado y desempaquetado de tuplas
```python
# Empaquetado
a, b, c, = 20, "T", "A"
tupla = a, b, c
# Desempaquetado
x, y, z = tupla
```
----
## Diccionarios
Los diccionarios también pueden ser conocidos como "memorias asociativas o "arreglos asociativos".
Los diccionarios se indexan con claves, pueden ser cualquiera de tipo inmutable, en vez de
un rango numérico como en las secuencias.
Las tuplas pueden usarse como claves si solamente contienen cadenas, números o
tuplas; si una tupla contiene cualquier objeto mutable directa o indirectamente,
no puede usarse como clave.
Ni tampoco listas, ya que se modifican con **append()** y **extend()**.
Los diccionarios se pueden pensar como un conjunto no ridenado de pares **clave: valor**
siendo las claves **UNICAS**.
```python
gente = {} vacia
gente = {'pepe': 123, 'joel': 234 }
```
### Las operaciones principales son
- Guardar con una clave
```python
gente['raul'] = 345
```
- Extraer de un dict. con una clave
```python
gente['joel'] # 234
```
- Borrar con del clave
```python
del gente['pepe']
```
- Listar claves
```python
list(gente.keys())
```
- Ordenar:
```python
sorted(gente.keys())
```
- condicional busqueda:
```python
'joel' in gente #True
'raul' not in gente #False
```
### Constructor dict()
**Formas de definir un diccionario**
```python
precios = {'manzana': 3.5, 'banana': 4.5, 'kiwi': 6.0, 'pera': 3.75}
precios = dict(manzana=3.5, banana=4.5, kiwi=6.0, pera=3.75)
precios = dict([('manzana', 3.5), ('banana', 4.5), ('kiwi', 6.0), ('pera', 3.75)])
```
### Acceso a elementos por claves
```python
precios['manzana'] # 3.5
precios['banana'] # 4.5
precios['kiwi'] # 6.0
precios['pera'] # 3.75
#precios['melon'] # KeyError
# Agregar un elemento (clave-valor)
precios['melon'] = 7.5
# Actualizar un elemento (clave-valor)
precios['manzana'] # 3.0
# Borrar un elemento (clave-valor)
del precios['kiwi']
# Pertenencia
'banana' in precios
'sandia' not in precios
```
### Propio diccionario a partir de 2 listas:
```python
pregs = ['nombre','altura','edad']
resps = ['cilantro', '16', 'a veces']
pyr = {}
for p, r in zip(pregs, resps):
pyr[p] = r
# En una linea
pyr1 = {p: r for p, r in zip(pregs,resps)}
# Mas efectivo
pyr2 = dict(zip(pregs, resps))
```
### Metodos en los diccionarios
```python
precios = {'manzana': 3.5, 'banana': 4.5, 'kiwi': 6.0, 'pera': 3.75}
# Cantidad de elementos clave-valor
len(precios)
# Devuelve el valor de clave indicada, se puede definir una por defecto
# si no existe, si no se indica None.
precios.get('manzana') # 3.5
precios.get('melon') # None
precios.get('melon', 0.00) # 0.00
# Si existe devuelve el valor, sino, es creado con el valor default o None
# en caso de no indicarlo
precios.setdefault('banana') #4.5
precios.setdefault('sandia') #None
precios.setdefault('pepino', 6.6) #6.6
# Actualizacion de un diccionario
precios.update({'banana': 4.0, 'durazno': 5.5})
precios.update([('durazno', 5.1)])
precios.keys() # Retorna lista con claves del diccionario
precios.values() # Retorna lista con los valores del diccionario
precios.items() # Retorna lista con los items del diccionario
# Sacar elemento segun clave, se puede definir un default si no lanza KeyError
precios.pop('manzana')
precios.pop('melon', 0.00)
# precios.pop('melon')
# Sacar elemento segun LIFO (LAST IN FIRST OUT, estilo pila)
precios.popitem()
# Copia "superficial" de diccionarios
precios_cp = precios.copy()
# Borra los elementos del diccionario
precios.clear()
```
### Iterando diccionarios
Vista dinamica de las entradas del diccionario
```python
precios = {'manzana': 3.5, 'banana': 4.5, 'kiwi': 6.0, 'pera': 3.75}
# Vista de Diccionarios
claves = precios.keys()
valores = precios.values()
items = precios.items()
precios['melon'] = 5.5 # Agregado melon al diccionario
# Iteracion de diccionarios
for fruta, precio in precios.items():
print("Precio de", fruta, ": $:", precio)
# La consulta a las variables previas asignadas como
# claves, valores e items. Mantienen sus valores actualizados según
# diccionario (apuntan a la misma direccion de memoria?)
print('\n',claves,'\n'*2, valores,'\n'*2, items)
```
> Condiciones que deben cumplir las claves en diccionarios
>
> Los diccionarios están implentados internamente como una tabla hash
> por lo que las claves deben ser objetos **Hasheables**.
>
> \*\***hashable** es un obejto que tiene un valor hash
> que nunca cambia durante el ciclo de vida del programa.
>
> Debe tener definido el metodo mágico __HASH__
> Debe poder ser comparado con otros objetos, es decir
> Debe tener definido el metodo __EQ__
> Deben ser iguales cuando su hash son iguales
>
> Todos los objetos inmutables ofrecidos Built-In en Python son hasheables.
> Ej,: **strings - numeros - tuplas - fechas**
>
> Los contenedores mutables como las listas, conjuntos,
> los diccionarios **no** son hasheables.
>
> Los objetos que son definidos por instancias de clases definidas por el
> usuario **son** hasheables por defecto.
>
> Los hash al compararse, siempre son distintos a otros obejos,
> salvo a si mismos.
>
> Su valor hash se deriba de su ID que es único
### Resumen diccionarios
```python
dict([('pepe', 1234), ('joel', 2345), ('raul', 3456)])
# Creación por comprensión de diccionarios desde expresiones arbitrarias clave/valor
otro = {x: x ** 2 for x in (2, 4, 6)}
# {2: 4, 4: 16, 6: 36}
# Si las claves son cadenas simples, se pueden especificar usando argumentos por claves****
otro = dict(pepe=1234, joel=2345, raul=3456)
# {'pepe': 1234, 'joel': 2345, 'raul': 3456}
# ITERACION .items()
caballeros = {'seya': 'pegaso', 'yoga': 'cisne'}
for k, v in caballeros.items():
print(k, v)
# Al iterar una secuencia se puede obtener tanto el índice como el valor correspondiente,
# usando la finción "enumerate()"
for k, v in enumerate(caballeros):
print(k, v)
# 0 seya
# 1 yoga
# Iterar sobre 2 o mas secuencias, emparejando valores con "zip()"
pregs = ['nombre', 'objetivo', 'color favorito']
resps = ['Yorldan', 'volar', 'diciembre']
for p, r in zip(pregs, resps):
print('Cual es tu {0}? {1}.'.format(p, r))
# Iterar en orden inverso
for i in reversed(range(1, 10, 2)):
print(i)
# Iterar secuencia ordenada "sorted()"
# sorted devuelve una nueva lista ordenada
# recordatorio set() <--- conjunto
canasta = ['manzana', 'naranja', 'manzana', 'pera', 'naranaja', 'banana']
for f in sorted(set(canasta)):
print(f)
# A veces, es mas simple y seguro crear una nueva lista,
# en vez de cambiar una lista mientras se recorre.
import math
datos = [56.2, float('NaN'), 51.7, 55.3, 52.5, float('NaN'), 47.8]
datos_filtrados = []
for valor in datos:
if not math.isnan(valor):
datos_filtrados.append(valor)
# datos_filtrados ---> [56.2, 51.7, 55.3, 52.5, 47.8]
# Recopilacion de metodos de mas arriba
# gente['raul'] = 345
# otro = dict(pepe=1234, joel=2345, raul=3456)
# otro = { x: x ** 2 for x in (2, 4, 6) }
# Base para crear Diccionarios con dos listas
pyr = {}
for p, r in zip(pregs, resps):
pyr[p] = r
pyr1 = {p: r for p, r in zip(pregs, resps)}
pyr2 = dict(zip(pregs, resps))
```
----
## Generadores
Son funciones especiales, devuelven una secuencia de valores, de uno en uno,
en cada llamada. Son una forma sencilla y potente de iterador.
Un iterador tiene siempre sus elementos disponibles, un generador los va
**generando** con un llamado al metodo **__next()__**
Con esto se pueden tener generadores, que generen infitinos elementos,
*ej. nums. naturales.*
Retornan un resultado con la sentencia **yield**.
La función queda en "**pausa**" hasta que es invocada nuevamente.
### Creación de generador
Un grador. de nros. 0-9
```python
def gen_10():
for n in range(10):
yield n
```
Uso, hay que instanciar el generador. Luego usar la funcion **__next__()**
al no quedar elementos que iterar, el generador lanza una excepción **StopIteration**
```python
a_gen_10 = gen_10() # Instancia del generador
a_gen_10.__next__() # 0
a_gen_10.__next__() # 1
a_gen_10.__next__() # 2
... # 3..8
a_gen_10.__next__() # 9
a_gen_10.__next__() # Traceback (most recent call last):
# File "<stdin>", line 1, in <module>
# StopIteration
```
Al igual que los iteradores, los generadores se pueden "consumir" con un ciclo for.
**ej. Generador nros. naturales, infinitos**
```python
def naturales():
n = 1
while True:
yield n
n += 1
nats = naturales()
```
**ej. Sentencia "return" para indicar StopIteration**
```python
def n_nats(n):
i = 1
while True:
if i > n:
return
yield i
i += 1
```
### Generar los 100 primeros números naturales
Similar a las listas por comprensión, solo que estas se escriben entre parentesis
```python
# Lista por Comprensión
[p for p in range(10) if p % 2 == 0]
# [0, 2, 4, 6, 8]
# Generador
(p for p in range(10) if p % 2 == 0)
# <generator object <genexpr> at 0x7fb25c49da50>
```
> Los generadores son útiles:
> - Trabajando con estructuras infinitas.
> - Trabajando con estructuras con alta carga de memoria, se puede reducir el espacio a revisar.
> - Se puede retrasar su calculo hasta último momento para favorecer otros procesos.
> - Se puede pasar un argumento en caso de acabar el generador.
> ejemplo: `next(nats, None)`, `next(nats, 'ratas')`
### Envio de objetos a un generador con send()
Esto también produce la 'generación'
```python
def accumulator():
total = 0
value = None
while True:
# receive sent value
value = yield total
if value is None:
break
# aggregate values
total += value
generator = accumulator()
# advance until the first "yield"
next(generator) # 0
# from this point on, the generator aggregates values
generator.send(1) # 1
generator.send(10) # 11
generator.send(100) # 111
# Calling next(generator) is equivalent to calling generator.send(None)
# next(generator) # StopIteration
```
### Uso de generadores
```python
def gen1000():
"Genera los pri meros 1000 números."
for x in range(1000):
yield x
primeros1000 = gen1000()
for x in primeros1000:
print(x, end=" - ")
print('\n'*2)
```
```python
def gen_primos(cantidad=1):
"Generador de números primos."
cont = 1
ls_prim = []
# Comienzo ciclo infinito
while cantidad > cont:
es_primo = True
cont += 1
if len(ls_prim) > 0:
for primo in ls_prim:
if cont % primo == 0:
es_primo = False
break
if es_primo:
ls_prim.append(cont)
yield cont
prim1000prim = gen_primos(1000)
for x in prim1000prim:
print(x, end=" - ")
```
```python
def num_par(n):
return (x for x in range(n) if x % 2 == 0)
gen = num_par(15)
gen.__next__()
gen.__next__()
print(list(gen))
```
----
## Decoradores
Los decoradores son funciones, que toman por argumento una función,
y devuelven otra funcion.
- Ayudan a hacer codigo mas limpio
- Reducen el codigo común y repetitivo
- Favorecen la separacion de responsabilidades del codigo
- Aumentan la legibilidad y mantenibilidad del progama
Son funciones:
- Reciben una funcion y devuelven otra función.
- Sirven para extender funcionalidad de una func.
o también para agregar funcionalidad yuxtapuesta a la misma.
ej. Decorando una función, con otra que mide el tiempo de ejecución
> Notar el orden:
> explicit args, luego \*args, luego \*\*kwargs.
> ejm. `def foo(arg1, arg2, *args, **kwargs):`
```python
def smart_division(div_func):
def div(a, b):
if b == 0:
print("No se puede dividir por cero!")
return
return div_func(a, b)
return div
@smart_division
def division(a, b):
return a / b
r = division(1, 2)
print(r)
r = division(2, 0)
print(r)
```
### Decorador generico
El anterior recibe 2 parametros en este vamos a pasar cualquier cantidad de argumentos
```python
def log(f):
def wrap(*args, **kwargs):
print('Ejecutando la función', f.__name__,
'con los argumentos', ', '.join([str(arg) for arg in args]))
return f(*args, **kwargs)
return wrap
@log
def suma(a, b):
return a + b
print(suma(1,2), '\n')
@log
@smart_division
def division_2(a, b):
return a / b
r = division_2(1, 2)
print(r)
r = division_2(2, 0)
print(r)
```
Los decoradores sirven para un código mas legible y cohesionado.
### Mas decoradores
```python
import time
def time_meter(f):
def wrap(*args, **kwargs):
ti = time.time()
result = f(*args, **kwargs)
tf = time.time()
etime = tf-ti
print("La func.",f.__name__,"demoró",round(etime, 5),"segundos en ejecutarse")
return result
return wrap
@time_meter
def suma(a, b):
time.sleep(0.2)
print('retraso')
return a+b
suma(1,3)
```
### Los decoradores tb pueden recibir parametros
```python
def logger(debug=False):
def _logger(func):
def inner(*args,**kwargs):
if debug:
print("Modo Debug")
for i, arg in enumerate(args):
print("arg %d:%s" % (i,arg))
return func(*args, *kwargs)
return inner
return _logger
@logger(True)
def suma(a,b):
return a+b
suma(2, 5)
#Modo debug
#arg 0:2
#arg 1:57
#7
@logger(False)
def suma(a, b):
return a + b
suma(2, 5)
#arg 0:2
#arg 1:57
#7
# Este tieme una función anidada más, el decorador recibe parámetros propios
# La 1ra función anidada recibe la función decorada.
# La 3ra función anidada tiene la lógica del decorador.
# Los decoradores pueden anidarse, una funcion puede tener muchos decoradores
# ej.
@logger(True)
@time_meter
def suma(a,b):
return a+b
suma(2, 7)
print('')
@time_meter
@logger(True)
def suma(a,b):
return a+b
suma(2, 7)
```
----
## Ejercicios Modulo 2
### Verdureria
```python
La verdulería greengrocer nos pasó su listado de precios por kilo.
Utilizando una consola de Python, crear el diccionario con la lista de precios:
precios = {'manzana': 3.5,
'banana': 4.5,
'kiwi': 6.0,
'pera': 3.75,
'ciruela': 2.45,
'durazno': 4.55,
'melon': 7.35,
'sandia': 9.70,
'anana': 11.25}
```
Si tenemos el siguiente ticket de una compra
```
2 kg de manzana
2.5 kg de banana
1 kg de kiwi
3 kg de pera
1 kg de ciruela
2 kg de durazno
5 kg de melón
10 kg de sandía
3 kg de ananá
```
¿Cuál es el precio del ticket de compra?
```python
precios = {
'manzana' : 3.5,
'banana' : 4.5,
'kiwi' : 6.0,
'pera' : 3.75,
'ciruela' : 2.45,
'durazno' : 4.55,
'melon' : 7.35,
'sandia' : 9.70,
'anana' : 11.25,
}
pedido = {
'manzana' : 2.0,
'banana' : 2.5,
'kiwi' : 1.0,
'pera' : 3.0,
'ciruela' : 1.0,
'durazno' : 2.0,
'melon' : 5.0,
'sandia' : 10.0,
'anana' : 3.0,
}
sub_total = 0
for key in pedido.keys():
if key in precios.keys():
sub_total += pedido[key] * precios[key]
print('TOTAL =', sub_total)
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
resp = d.get('z', 7) == 7
print(resp)
resp = d.get('z') == None
print(resp)
resp = d.setdefault('z', 5) == 5
print(resp)
resp = d.setdefault('a', 7) == 7
print(resp)
resp = d['a'] == 1
print(resp)
resp = d.get('a', 3) == 3
print(resp)
d.update([('a', 10)])
print(d.get('a'))
d.update({'a': 1, 'b': 3})
print(d.get('a'), d.get('b'))
d.update((('a', 45), ('b', 54)))
print(d.get('a'), d.get('b'))
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
resp = d.pop('f', 2) == 2
print(resp)
print(d)
resp = d.pop('c') == 3
print(resp)
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
resp = d.popitem() == 5
print(resp)
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
resp = d.pop('a', 2) == 2
print(resp)
#d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
#resp = d.popitem() == (e, 5)
#print(resp)
d = {'a': 1, 'b': 2, 'c': 3, 'd': 4, 'e': 5}
resp = d.pop() == 5
print(resp)
espera = input('esperando')
print(espera)
```
----
## El Gato
```
Deberás programar el juego TA-TE-TI.
Cuando el programa comienza a correr, en la pantalla aparece el tablero de TA-TE-TI (de 3x3)
y un input que permite al usuario elegir el símbolo “X” o el símbolo “O”. Las “X” empiezan.
El usuario debe elegir la posición del tablero (esta posición debe ser correcta y no debe
estar ocupada) donde poner el símbolo en el tablero y el sistema valida si el juego termina
con un ganador o en empate.
Si no hay ganador o la partida no terminó todavía en empate, el juego continúa preguntando
al otro usuario que seleccione la posición del tablero dónde quiere poner su símbolo y así
siguiendo hasta que la partida termine con un ganador o en empate.
Notas:
Representar el tablero como una matriz de 3x3.
El juego termina en empate cuando el tablero está completo y no hay ganadores.
```
> Este archivo ha sido creado con fines didacticos y como entrega final del 2do
> modulo del Curso Aprende a Programar en Python, dictado por la Universidad Austral,
> a travez de Coursera
> @autor: devfzn@gmail.com
```python
import random
import os
from time import sleep
clear = lambda: os.system('clear') if os.name == 'posix' else os.system('cls')
limpiar_pantalla = True
""" Creación de matriz base"""
#matriz = [['a1', 'b1', 'c1'], ['a2', 'b2', 'c2'], ['a3', 'b3', 'c3']]
matriz = [['|_','|_','|_'], ['|_','|_','|_'], ['|_','|_','|_']]
# Creacción de dicionario vacío y lista de llaves.
master, llaves = {}, [x+y for x in 'abc' for y in '123']
# Preguntas para solicitar entradas de usuario
lado = '\tEscribe \'o\' para jugar con \'O\',\n\to \'x\' para jugar con \'X\',\n\t(\'X\' hace la primera jugada)\n\t> '
salir = '\tJugar Otra? (si/no)\n\t> '
jugada = '\tIngresa tu jugada (ej. a2)\n\t> '
tit_V = 'Ganaste, Felicitaciones!'
tit_P = ' Has perdido'
# Lista con tuplas de condiciones para ganar o perder el juego.
ganadoras = [('a1', 'b1', 'c1'),
('a2', 'b2', 'c2'),
('a3', 'b3', 'c3'),
('a1', 'a2', 'a3'),
('b1', 'b2', 'b3'),
('c1', 'c2', 'c3'),
('a1', 'b2', 'c3'),
('c1', 'b2', 'a3')]
def actualizar_matriz():
"""
Actualiza los valores en la matriz según los valores en diccionario maestro
:return: None
"""
x, y = 0, 0 # Variables para indices de matriz tablero
for valor in master.values(): # Recorre los valores del diccionario
if not y < 3: # Limite indice columnas
y = 0 # Reinicio contador columnas
x += 1 # Autoincremento de fila al llegar a 3ra columna
matriz[y][x] = valor # Asigna el valor del diccionario a la matriz (casilla)
y += 1 # Autoincremento de columna
def dibujar_tablero(*frase):
"""
Muestra en pantalla un string pasado como argumento,
(coordenadas y matriz de juego)
:param frase: titulo a mostrar según circunstancia
:return: None
"""
if limpiar_pantalla:
clear()
print('\n\t', *frase, '\n\n\t\t a b c')
for x in range(len(matriz)):
print('\t\t', end=str(x+1))
for y in range(len(matriz[x])):
print(matriz[x][y], end='')
print('|')
print('\n')
# ANIMACION
saludo = ['\n\tBienvenido al Clasico ', '\n\n\n\t\t\"GATO!\"']
jugadas = [[0, 0], [1, 2], [2, 0], [1, 0], [1, 1], [2, 2], [0, 2]]
def parp_text(repets, *texto):
print(len(texto))
x, sec = 0, True
while x < repets:
for txt in texto:
for tx in txt:
clear()
print('\n\t', tx)
sleep(0.7)
x += 1
sleep(1)
def animacion():
actualizar_matriz()
for x in range(len(jugadas)):
if x % 2 == 0:
matriz[jugadas[x][0]][jugadas[x][1]] = '|X'
else:
matriz[jugadas[x][0]][jugadas[x][1]] = '|O'
dibujar_tablero('\"devfzn@gmail.com\"')
sleep(0.5)
animacion()
parp_text(2, saludo)
def input_usr(pregunta, respuestas):
"""
Solicita una entrada al usuario, recive 1 string y una lista de string
:param pregunta: string Pregunta a realizar
:param respuestas: [string] Lista de posibles respuestas en mayúscula
:return: 'string' input de usuario validado
"""
resp = '' # Variable que recibe entrada de usuario
while resp not in respuestas: # Valida respuesta según 'respuestas'
resp = input(pregunta).upper() # Asignación entrada de usuario (mayusculas forzadas)
return resp
jugar = True # Variable que gobierna el ciclo principal (Juego)
while jugar: # Ciclo principal
global turno #
for llave in llaves: # Recorre las llaves y asgina '|_'(vacío) a cada item
master[llave] = '|_'
slave = master.copy() # Copia superficial del diccionario maestro
actualizar_matriz() # Poblar matríz con los valores del diccionario maestro
dibujar_tablero(' Elige el lado X - O') # Imprime matríz en pantalla
global p1
p1 = input_usr(lado, ['x', 'X', 'o', 'O']) # Asignación de entrada de usuario, P1 (Player 1)
titulo = 'P1 jugando con -> ' + p1
cambio = lambda x: 'O' if x == 'X' else 'X' # Función anónima retorna el símbolo opuesto al evaluado X,O
global pc
pc = cambio(p1) # Asignación de símbolo a jugador PC (Player Computer)
turno = 0 # Contador de turnos jugados
dibujar_tablero(titulo)
def turno_p1():
"""
Función que recibe una entrada de usuario y la valida
según la copia de las llaves del diccionario maestro.
Actualiza el valor en el diccionario maestro con el
symbolo seleccionado por el jugador PC y e xtrae la copia de 'slave'
:return: None
"""
jp1 = input_usr(jugada, list(x.upper() for x in list(slave.keys()))).lower()
slave.pop(jp1)
master[jp1] = '|' + p1
def turno_pc():
"""
Función que selecciona una llave al azar de la copia del
diccionario maestro y actualiza a este último con el simbolo
del jugador PC.
Despues del turo nro. 2 evalúa si hay algún espacio estrategico,
donde existen dos simbolos iguales, pone el tercero, priorizando
los pares de symbolos afines con el bando asignado.
:return: None
"""
if turno > 2:
falta, hay_par = (), False
for pos in ganadoras:
a, b, c = master[pos[0]], master[pos[1]], master[pos[2]]
if '|_' != a == b != c == '|_':
falta = (a, pos[2])
hay_par = True
if a == pc + '|_':
break
elif '|_' != a == c != b == '|_':
falta = (a, pos[1])
hay_par = True
if a == pc + '|_':
break
elif '|_' != c == b != a == '|_':
falta = (c, pos[0])
hay_par = True
if c == pc + '|_':
break
if hay_par:
jpc = falta[1]
else:
jpc = ''.join(random.choice(list(slave.keys())))
else:
jpc = ''.join(random.choice(list(slave.keys())))
slave.pop(jpc)
master[jpc] = '|' + pc
def eval_term():
"""
Función que evalua si se cumplen las condiciones para terminar la partida.
Compara los valores el diccionario 'maestro' según las llaves en las tuplas,
de la lista 'ganadaoras'. Asigna un titulo y un valor al booleano 'termino'
:return: None
"""
global titulo
global termino
for g in ganadoras:
test = master[g[0]]
test2 = '|' + p1
if '|_' != test == master[g[1]] == master[g[2]]:
termino = True
titulo = tit_V if test2 == '|X' == test else tit_V if test2 == '|O' == test else tit_P
if turno > 8 and not termino:
termino, titulo = True, '\t EMPATE'
X = p1 == 'X' # Booleano indica si jugador 1 es 'X'
termino = False # Booleano que gobierna ciclo de jugadas
while not termino: # Ciclo de jugadas
turno_p1() if X else turno_pc() # Alterna el turno del Player1 y PC, segun 'X'
X = not X # Alterna el booleano 'X'
turno += 1
eval_term()
actualizar_matriz()
dibujar_tablero(titulo)
resp = input_usr(salir, ['N', 'NO', 'S', 'SI'])
jugar = resp in 'SI' # Actualización del booleano que gobierna while principal
autor = ['', '\n\n\t "devfzn@gmail.com\"']
parp_text(4, autor)
```