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

59 KiB

Ir a: Repositorio, Modulo 1, Modulo 3

Modulo 2 - Python basico

Indice


Strings

Cadenas(secuencia) de caracteres

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

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

    ' ',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

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)

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

hoy = datetime.date.today()
ayer = hoy - datetime.timedelta(days=1)

delta = hoy - ayer      # 1 day, 0:00:00

Datetime ( hora y fechas )


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

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

while True :
    try:
        x = int(input('Excepcion si no se ingresa un ENTERO ->'))
        break
    except ValueError:
        print('Se puede salir con Ctrl-C')
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
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('')
for cls in [B, C, D]:
    try:
        raise cls()
    except B:
 (       print("B")
    except D:
        print("D")
    except C:
        print("C")
    print('')
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

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

try:
    raise NameError('y bueno un error común')
except NameError:
    print('ee.. salto un except!')
    raise

Excepción Personalizada

class Error(Exception):
    """Clase base para excepciones en el módulo"""
    pass

Acciones de limpieza

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

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

   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

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

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

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

    print('12'.zfill(5), end='\n' * 2)
    # 00012
    
  • format()

    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

    # !a    apply()
    # !s    str()
    # !r    repr()
    
    var = 'ratas'
    print('mi yate esta lleno de {!r}.'.format(var))
    # mi yate esta lleno de ratas.
    
    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

    mi_file = open('nombre_archivo', 'modo')
    -r  read (default)
    -r+ read/write
    -w  write
    -a  append
    -x  ?
  • read()

    # 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.

    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

    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

# 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

with open('./python_subprocess_bash', 'r') as archivo:
    print(archivo.read())

Lee por linea

with open('./python_subprocess_bash', 'r') as archivo:
    print(archivo.readline())

Genera una lista con las lineas del archivo

with open('./python_subprocess_bash', 'r') as archivo:
    print(archivo.readlines())

Genera una lista con las lineas del archivo

with open('./python_subprocess_bash', 'r') as archivo:
    print(list(archivo))

For recorre linea a linea

with open('./python_subprocess_bash', 'r') as archivo:
    for linea in archivo:
        print(linea)

Escribiendo en archivos

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.

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'

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)

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'])

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.

import json

Serializar un objeto

# Transforma el mismo a una representacion de caracteres.
json.dumps([1, 2, 3])   
# '[1, 2, 3]'

Deserializar una cadena

# Transforma la cadena a un objeto json
json.loads('[1, 2, 3]')
# [1, 2, 3]

Escribir como json a un archivo

with open('archivo.json', 'w') as arch:
    json.dump([1, 2, 3, 4], arch)

Leer un json

with open('archivo.json', 'r') as arch:
    print( json.load(arch) )

Otro ejemplo

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.

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.

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

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

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

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

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


# 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()
# 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)

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)

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"

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

# 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

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

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

# 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)]
# 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.

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.

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

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

# 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.

       gente = {} vacia
       gente = {'pepe': 123, 'joel': 234 }

Las operaciones principales son

  • Guardar con una clave
        gente['raul'] = 345
    
  • Extraer de un dict. con una clave
        gente['joel']  # 234
    
  • Borrar con del clave
        del gente['pepe']
    
  • Listar claves
        list(gente.keys())
    
  • Ordenar:
        sorted(gente.keys())
    
  • condicional busqueda:
        'joel' in gente #True
        'raul' not in gente #False
    

Constructor dict()

Formas de definir un diccionario

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

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:

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

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

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

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

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

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

def naturales():
    n = 1
    while True:
        yield n
        n += 1

nats = naturales()

ej. Sentencia "return" para indicar StopIteration

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

# 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'

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

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)
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=" - ")
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):

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

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

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

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

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?

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

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)