Python

Un poco de Historia

Python fue creado a finales de los años 80 por un programador holandés llamado Guido van Rossum, quien sigue siendo aún hoy el líder del desarrollo del lenguaje. (Edit julio 2018: ya no más)

El nombre del lenguaje proviene de los humoristas británicos Monty Python.

"I chose Python as a working title for the project, being in a slightly irreverent mood (and a big fan of Monty Python's Flying Circus)."

Diferencias entre C y Python

C Python
Compilado Interpretado
Tipado estatico Tipado dinamico
Procedural Multiparadigma
Bajo nivel Alto nivel
Acceso a memoria de bajo nivel mediante punteros Recolector de basura

¿Cómo empezar?

  • Al ser un lenguaje interpretado, se puede ir escribiendo a medida que se ejecuta, sin necesidad de compilar de antemano! Solamente hace falta escribir python o python3 en una terminal para empezar

  • También, permite escribir archivos y correrlos. Crear un archivo con extensión .py y luego correr python miarchivo.py en laterminal

La filosofía de Python

In [1]:
import this
The Zen of Python, by Tim Peters

Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Readability counts.
Special cases aren't special enough to break the rules.
Although practicality beats purity.
Errors should never pass silently.
Unless explicitly silenced.
In the face of ambiguity, refuse the temptation to guess.
There should be one-- and preferably only one --obvious way to do it.
Although that way may not be obvious at first unless you're Dutch.
Now is better than never.
Although never is often better than *right* now.
If the implementation is hard to explain, it's a bad idea.
If the implementation is easy to explain, it may be a good idea.
Namespaces are one honking great idea -- let's do more of those!

Conocimientos Básicos de Python: Variables y Tipos

In [4]:
'''Este es un comentario''' # Triple comillas para comentarios. Numeral para comentarios en linea

print("Hello World!")
Hello World!
In [5]:
# Declaración de variables

string = 'Hola'
print(string)
entero = 1
print(entero)
flotante = 1.0
print(flotante)
tupla = (entero,flotante)
print(tupla)
nupla = (entero,flotante,string)
print(nupla)
Hola
1
1.0
(1, 1.0)
(1, 1.0, 'Hola')
In [6]:
# Declaración de más variables

lista = [entero,flotante,string]
print(lista)
diccionario = {'1':tupla,50:nupla,'3':entero}
print(diccionario)
conjunto = set([1,2])
print(conjunto)
booleano = True
print(booleano)
[1, 1.0, 'Hola']
{'1': (1, 1.0), 50: (1, 1.0, 'Hola'), '3': 1}
{1, 2}
True
In [7]:
# Pueden cambiar de tipo

elemento = 1
print(elemento) 
print(type(elemento))
elemento = str(1)
print(elemento)
print(type(elemento))

# Pueden redefinirse
elemento = ['dos']

print(elemento)
print(type(elemento))
1
<class 'int'>
1
<class 'str'>
['dos']
<class 'list'>

Funciones en Python

In [8]:
# Definir una función
def suma(a,b):
    return a+b
In [9]:
print(suma(1,2))
print(suma(1.0,2.0))
print(suma(1.0,2))
print(suma("hola ","como te va"))
print(suma([1,2,3],[4,5]))
print(suma("1",3)) # Falla
3
3.0
3.0
hola como te va
[1, 2, 3, 4, 5]
-------------------------------------------------------
TypeError             Traceback (most recent call last)
<ipython-input-9-3adc1bd67839> in <module>
      4 print(suma("hola ","como te va"))
      5 print(suma([1,2,3],[4,5]))
----> 6 print(suma("1",3)) # Falla

<ipython-input-8-e26832e5268e> in suma(a, b)
      1 # Definir una función
      2 def suma(a,b):
----> 3     return a+b

TypeError: can only concatenate str (not "int") to str
In [11]:
# El valor por default de divisor es 1

def division(dividendo,divisor=1):
    return dividendo/divisor

print(division(4)) # Usa el valor por default
print(division(1,2)) # Parámetros por orden
print(division(dividendo=1,divisor=2)) # Parámetros por nombre
print(division(divisor=2,dividendo=1))
4.0
0.5
0.5
0.5
In [14]:
# Unpacking

dic = {
    "divisor": 2,
    "dividendo": 1
}

lista = [1,2]

print(division(**dic))
print(division(*lista))
0.5
0.5
In [15]:
# Funciones básicas ya en el lenguaje
# Hechas para funcionar para distintos tipos

string_ordenado = sorted('bca')
print(string_ordenado)

lista_ordenada = sorted([1,3,2])
print(lista_ordenada)

separadas = "hola, don, pepito".split(",")
print(separadas)

unidas = "".join(separadas)
print(unidas)

lista_numeros = [3,2,4]
print(lista_numeros)
lista_numeros.sort()
print(lista_numeros)
['a', 'b', 'c']
[1, 2, 3]
['hola', ' don', ' pepito']
hola don pepito
[3, 2, 4]
[2, 3, 4]

Diferencia entre lista y tupla

Las listas se caracterizan por ser mutables, es decir, se puede cambiar su contenido en tiempo de ejecución, mientras que las tuplas son inmutables ya que no es posible modificar el contenido una vez creada.

Listas de Python

In [36]:
# Como hacer una lista
lista = [] # A modo de ejemplo llamamos a la lista "lista", pero no deben llamar a las variables por su tipo
print(id(lista))
# Como agregar cosas a la lista
print(lista)
lista.append(1) # Inserto un 1 al final
print(id(lista))
lista.append("dos") # Inserto un "dos" al final
lista.append(3.0) # Inserto un 3.0 al final
lista.insert(-2,10) # Inserto en posicion 2 un 10
print(lista)

tupla = ()
print(id(tupla))
tupla = (1,2)
print(id(tupla))
4418280648
[]
4418280648
[1, 10, 'dos', 3.0]
4384608328
4416599432
In [24]:
# Como iterar una lista elemento por elemento
print("Primera iteración")
for elemento in lista:
    print("Elemento ", elemento)

print("Segunda iteración")
for i, elemento in enumerate(lista):
    print("Indice: ", i)
    print("Valor: ", elemento)
Primera iteración
Elemento  1
Elemento  10
Elemento  dos
Elemento  3.0
Segunda iteración
Indice:  0
Valor:  1
Indice:  1
Valor:  10
Indice:  2
Valor:  dos
Indice:  3
Valor:  3.0
In [34]:
# Como hacer un ciclo for que recorra la lista
print("Tercera iteración")
for i in range(0,4,-1):
    print("Elemento",i)
    
# Como hacer un ciclo while que recorra la lista
print("Cuarta iteración")
i = 0
while i < len(lista):
    print("Elemento",lista[i])
    i += 1 # No se puede hacer i++ o ++i
Tercera iteración
Cuarta iteración
Elemento 1
Elemento 10
Elemento dos
Elemento 3.0
In [37]:
# Como remover por elemento
elemento = lista.remove(1) # Elimina la primer aparición de 1
print(elemento)
print(lista)

# Como remover por posicion
elemento = lista.pop(1) # Elimina el elemento en la posición pasada por parámetro
                        # si no se le pasa nada elimina el último
print(elemento)
print(lista)
None
[10, 'dos', 3.0]
dos
[10, 3.0]

Tuplas de Python

In [38]:
# Como hacer una tupla
tupla = (1,2)  # Las tuplas son inmutables. No se pueden crear e ir agregando cosas

print(tupla)
print(tupla[0])
print(tupla[1])

tupla[1] = 3 # Falla. No se puede mutar
(1, 2)
1
2
-------------------------------------------------------
TypeError             Traceback (most recent call last)
<ipython-input-38-5adb78ed4132> in <module>
      6 print(tupla[1])
      7 
----> 8 tupla[1] = 3 # Falla. No se puede mutar

TypeError: 'tuple' object does not support item assignment

Slices

Valen para listas, tuplas o strings (segmentos)

In [40]:
numeros = [0,1,2,3,4,5,6,7,8,9,10]

print(numeros)
print(numeros[2]) # Imprimo elemento en la posición 2
print(numeros[-1]) # Imprimo elemento en la última posición
print(numeros[0:2]) # Imprimo de la pos 0 a la pos 2
print(numeros[-4:-2])
print(numeros[-2:-4:-1])
print(numeros[:3])
print(numeros[3:])
print(numeros[::2])
print(numeros[:])
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
2
10
[0, 1]
[7, 8]
[9, 8]
[0, 1, 2]
[3, 4, 5, 6, 7, 8, 9, 10]
[0, 2, 4, 6, 8, 10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
In [16]:
numeros[7] = 'siete' # Las listas se pueden mutar
print(numeros)
numeros = numeros[::-1]
print(numeros)
numeros_2 = numeros[:]
print(numeros_2)
[0, 1, 2, 3, 4, 5, 6, 'siete', 8, 9, 10]
[10, 9, 8, 'siete', 6, 5, 4, 3, 2, 1, 0]
[10, 9, 8, 'siete', 6, 5, 4, 3, 2, 1, 0]
In [2]:
lista_de_listas = [[1],[2]]
print("Lista externa:", id(lista_de_listas))
for lista in lista_de_listas:
    print("Lista interna:", id(lista))

print('-'*25)
lista_de_listas_2 = lista_de_listas[:]
print("Lista externa:", id(lista_de_listas_2))
for lista in lista_de_listas_2:
    print("Lista interna:", id(lista))
    
print('-'*25)    
lista_de_listas[0].append("prueba") # [[1,"prueba"], [2]]
print(lista_de_listas)
print(lista_de_listas_2)
Lista externa: 4373289032
Lista interna: 4375061704
Lista interna: 4373336840
-------------------------
Lista externa: 4373289288
Lista interna: 4375061704
Lista interna: 4373336840
-------------------------
[[1, 'prueba'], [2]]
[[1, 'prueba'], [2]]
In [42]:
print(numeros[15]) # Falla. No se puede acceder a una posición inexistente
-------------------------------------------------------
IndexError            Traceback (most recent call last)
<ipython-input-42-b1872d0ba857> in <module>
----> 1 print(numeros[15]) # Falla. No se puede acceder a una posición inexistente

IndexError: list index out of range
In [43]:
palabra = 'palabra'
print(palabra)
print(palabra[3])
print(palabra[:3])
print(palabra[3:])
palabra
a
pal
abra
In [44]:
tupla = (0,1)

print(tupla)
print(tupla[0])
print(tupla[1])

tupla[3] = 2 # Falla. No se puede cambiar algo en una tupla. Es inmutable!
(0, 1)
0
1
-------------------------------------------------------
TypeError             Traceback (most recent call last)
<ipython-input-44-d4b254182ca5> in <module>
      5 print(tupla[1])
      6 
----> 7 tupla[3] = 2 # Falla. No se puede cambiar algo en una tupla. Es inmutable!

TypeError: 'tuple' object does not support item assignment

Condicionales (if...elif...else)

if <condición_1>:
    <hacer algo_1 si se da la condición_1>
elif <condición_2>:
    <hacer algo_2 si se da la condición_2>
...
elif <condición_n>:
    <hacer algo_n si se da la condición_n>
else:
    <hacer otra cosa si no dan las anteriores>
In [24]:
def busqueda_binaria(lista, elemento):
    # not equivale a ! en C
    # True y False empiezan con mayúscula
    if not lista: return False
    elif len(lista) == 1: # len(lista) devuelve el largo de la lista
        return lista[0] == elemento
    mitad = len(lista)//2 # // es la operación división entera
    if lista[mitad] == elemento: return True
    if lista[mitad] > elemento:
        return busqueda_binaria(lista[:mitad],elemento)
    if lista[mitad] < elemento:
        return busqueda_binaria(lista[mitad:],elemento)
    
print(busqueda_binaria([1,2,3,4,5],4))
print(busqueda_binaria([1,4,6,7,9,10],2))
True
False

Diccionarios de Python

Son como hashmaps, las claves deben ser inmutables para que no pierda sentido el diccionario. Si se pudieran modificar, se podrían cambiar las claves y generaría conflictos.

Tipos mutables Tipos inmutables
Listas Int
Diccionarios Float
Sets String
. Tublas
In [4]:
# Cómo hacer un diccionario
diccionario = {}

# Cómo agregar cosas al diccionario
diccionario['clave1'] = 'valor1'
diccionario[2] = 'valor2'
diccionario['clave3'] = 3
print(diccionario)

# {('clave1','valor1'),('clave2','valor2'),('clave3',3)}
print(diccionario.get('clave3',2))  # Equivalente a diccionario['clave3']
                                    # pero en caso de no tener la clave, devuelve
                                    # un elemento por default (en este caso 2)

print ('clave1' in diccionario) # Verifico si la clave está en el diccionario
{'clave1': 'valor1', 2: 'valor2', 'clave3': 3}
3
True
In [5]:
# Cómo iterar un diccionario elemento por elemento
for clave,valor in diccionario.items(): # diccionario.items() va devolviendo tuplas con el formato (clave,valor)
                                        # con esta sintaxis se desempaquetan en clave y valor (similar a enumerate)
    print("Clave:", str(clave), "\tValor", str(valor))

print('-'*25)
# Cómo iterar las claves
for clave in diccionario.keys():
    print("Clave", str(clave))
    
print('-'*25)
# Cómo iterar los valores 
for valor in diccionario.values():
    print("Valor", str(valor))
Clave: clave1 	Valor valor1
Clave: 2 	Valor valor2
Clave: clave3 	Valor 3
-------------------------
Clave clave1
Clave 2
Clave clave3
-------------------------
Valor valor1
Valor valor2
Valor 3

Sets

Son similares a los diccionarios (en eficiencia) pero se almacenan solo claves, y tienen algunas operaciones particulares.

En particular, no pueden tener elementos iguales (pensar que son conjuntos)

In [45]:
# Se definen como los diccionarios pero sin hacerlos 'clave:valor', solamente una seguidilla de elementos
{1,2,2,3}
Out[45]:
{1, 2, 3}
In [2]:
help(set)
Help on class set in module builtins:

class set(object)
 |  set() -> new empty set object
 |  set(iterable) -> new set object
 |  
 |  Build an unordered collection of unique elements.
 |  
 |  Methods defined here:
 |  
 |  __and__(self, value, /)
 |      Return self&value.
 |  
 |  __contains__(...)
 |      x.__contains__(y) <==> y in x.
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __iand__(self, value, /)
 |      Return self&=value.
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __ior__(self, value, /)
 |      Return self|=value.
 |  
 |  __isub__(self, value, /)
 |      Return self-=value.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __ixor__(self, value, /)
 |      Return self^=value.
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __or__(self, value, /)
 |      Return self|value.
 |  
 |  __rand__(self, value, /)
 |      Return value&self.
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __ror__(self, value, /)
 |      Return value|self.
 |  
 |  __rsub__(self, value, /)
 |      Return value-self.
 |  
 |  __rxor__(self, value, /)
 |      Return value^self.
 |  
 |  __sizeof__(...)
 |      S.__sizeof__() -> size of S in memory, in bytes
 |  
 |  __sub__(self, value, /)
 |      Return self-value.
 |  
 |  __xor__(self, value, /)
 |      Return self^value.
 |  
 |  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_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.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __hash__ = None

Módulos

Para incluir alguna biblioteca de funciones se usa import. Pueden ser cosas ya predefinidas en Python (math, random, etc), nombres de archivos en nuestro directorio (por ejemplo, para mimodulo.py ponemos import mimodulo) o bibliotecas instaladas por el usuario

In [3]:
import math
print(math.pi)

from math import pi
print(pi)

from math import pi as PI
print(PI)
3.141592653589793
3.141592653589793
3.141592653589793

Manejo de excepciones

Se pueden encapsular errores esperados en un bloque 'try/except' para evitar cortar el flujo del programa

In [48]:
division(1,0) # No se puede dividir por cero
-------------------------------------------------------
ZeroDivisionError     Traceback (most recent call last)
<ipython-input-48-2515e6bf8d70> in <module>
----> 1 division(1,0) # No se puede dividir por cero

<ipython-input-11-fd41877ed25b> in division(dividendo, divisor)
      2 
      3 def division(dividendo,divisor=1):
----> 4     return dividendo/divisor
      5 
      6 print(division(4)) # Usa el valor por default

ZeroDivisionError: division by zero
In [52]:
try:
    division(1,0)
except ZeroDivisionError:
    print('No se puede dividir por cero, ojo!')
except:
    print('Error de cualquier tipo')
No se puede dividir por cero, ojo!

Lectura y escritura de archivos

In [5]:
import random
with open('archivo.csv','w') as archivo: # Al usar esta sintaxis no es necesario hacer close
    archivo.write("Alumno, nota\n")
    # Tambien de forma similar al fprintf se puede hacer:
    # print("Alumno, nota\n", file=archivo)
    for i in range(0,10):
        archivo.write(str(i) + "," + str(random.randrange(0,10))+"\n")

print(archivo)  # Comentario aclaratorio:
                # Las variables definidas en un determinado scope siguen existiendo por fuera del mismo.
                # Se debe tener cuidado con esto, ya que nada garantiza que por fuera el valor sea el esperado.
<_io.TextIOWrapper name='archivo.csv' mode='w' encoding='UTF-8'>
In [8]:
with open('archivo.csv','r') as f:
    for linea in f:
        print(linea, end="") # Reemplaza "\n" por ""
Alumno, nota
0,2
1,8
2,2
3,7
4,4
5,5
6,5
7,5
8,3
9,5

Archivos CSV

In [9]:
import csv
with open('archivo.csv','r') as f:
    f_reader = csv.DictReader(f,delimiter=',')
    #f_reader = csv.reader(f,delimiter = ',') # Devuelve lista de elementos
    for row in f_reader:
        print(row)
OrderedDict([('Alumno', '0'), (' nota', '2')])
OrderedDict([('Alumno', '1'), (' nota', '8')])
OrderedDict([('Alumno', '2'), (' nota', '2')])
OrderedDict([('Alumno', '3'), (' nota', '7')])
OrderedDict([('Alumno', '4'), (' nota', '4')])
OrderedDict([('Alumno', '5'), (' nota', '5')])
OrderedDict([('Alumno', '6'), (' nota', '5')])
OrderedDict([('Alumno', '7'), (' nota', '5')])
OrderedDict([('Alumno', '8'), (' nota', '3')])
OrderedDict([('Alumno', '9'), (' nota', '5')])
In [10]:
from csv import writer

with open('archivo.csv','w') as f:
    f_writer = writer(f,delimiter=',')
    f_writer.writerow([1,2])
    f_writer.writerow([2,3])
    f_writer.writerow([4,5])

Objetos

Los objetos tiene metodos y atributos:

  • Atributos: equivalentes a variables.
  • Métodos: equivalentes a las primitivas.

Se puede trazar una equivalencia entre los objetos y los structs de C, "ambas son estructuras en las que se le pueden guardar cosas".

La clase de un objeto es el tipo.

Por ejemplo:

En C, para definir un nodo haciamos:

typedef struct nodo {
    void* dato;
    struct nodo* siguiente;
} nodo_t;

Cómo creo una clase

In [11]:
class Nodo():
    
    def __init__(self,dato,siguiente = None):
        self.dato = dato
        self.siguiente = siguiente
            
    def obtener_dato(self):
        return self.dato;
    
    def proximo(self):
        return self.siguiente

    def __repr__(self):
        return str(self.dato)
    
    def __eq__(self, other):
        return self.dato == other.dato
    
    def __str__(self):
        return str(self.dato)
In [12]:
nodo = Nodo("hola")
print(nodo)
nodo2 = Nodo("lala")
print([nodo,nodo2])
nodo3 = nodo.obtener_dato()
print(nodo3)
hola
[hola, lala]
hola
In [13]:
help(Nodo)
Help on class Nodo in module __main__:

class Nodo(builtins.object)
 |  Nodo(dato, siguiente=None)
 |  
 |  Methods defined here:
 |  
 |  __eq__(self, other)
 |      Return self==value.
 |  
 |  __init__(self, dato, siguiente=None)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __repr__(self)
 |      Return repr(self).
 |  
 |  __str__(self)
 |      Return str(self).
 |  
 |  obtener_dato(self)
 |  
 |  proximo(self)
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  __dict__
 |      dictionary for instance variables (if defined)
 |  
 |  __weakref__
 |      list of weak references to the object (if defined)
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __hash__ = None

Ejemplo: Lista Enlazada

In [19]:
class Lista_Enlazada():
    
    def __init__(self):
        self.primero = None
        self.ultimo = None
        self.largo = 0
    
    def insertar_al_principio(self,dato):
        nodo = Nodo(dato, self.primero)
        self.primero = nodo
        self.largo += 1
        if self.largo == 1: self.ultimo = nodo
            
    def insertar_al_final(self,dato):
        if self.largo == 0:
            return self.insertar_al_principio(dato)
        nodo = Nodo(dato)
        nodo_anterior = self.ultimo
        nodo_anterior.siguiente = nodo
        self.ultimo = nodo
        self.largo += 1            

    def ver_primero(self):
        return self.primero.obtener_dato();
    
    def borrar_primero(self):
        dato = self.primero.obtener_dato()
        self.primero = self.primero.siguiente
        self.largo -= 1
        if self.largo == 0:
            self.ultimo = None
        return dato
            
    def __str__(self):
        cadena = []
        nodo_actual = self.primero
        while nodo_actual is not None:
            cadena.append(str(nodo_actual.obtener_dato()))
            nodo_actual = nodo_actual.siguiente
        return " | ".join(cadena)
In [20]:
lista = Lista_Enlazada()
lista.insertar_al_principio("Dato 1")
lista.insertar_al_principio("Dato 2")
elemento = lista.ver_primero()
print(elemento)
print(str(lista))
Dato 2
Dato 2 | Dato 1

Librería para Heaps

In [21]:
import heapq

heap = [5,2,3,7,2,20,1]
heapq.heapify(heap)
print(heap)

heapq.heappush(heap,1.5)
print(heap)

menor = heapq.heappop(heap)
print(menor)
print(heap)

n_menores = heapq.nsmallest(3,heap)
print(n_menores)
[1, 2, 3, 7, 2, 20, 5]
[1, 1.5, 3, 2, 2, 20, 5, 7]
1
[1.5, 2, 3, 2, 7, 20, 5]
[1.5, 2, 2]

Y como hacer un Max-Heap

In [22]:
class Nodo_heap(object):
    def __init__(self,dato):
        self.dato = dato
    
    def obtener_valor():
        return dato
    
    def __lt__(self, other):
        return self.dato>other.dato
    
    def __gt__(self, other):
        return self.dato<other.dato
    
    def __eq__(self, other):
        return self.dato==other.dato

    def __str__(self):
        return str(self.dato)
    
    def __repr__(self):
        return str(self.dato)
In [25]:
heap = [Nodo_heap(5), Nodo_heap(2), Nodo_heap(3), Nodo_heap(7), Nodo_heap(2), Nodo_heap(20), Nodo_heap(1)]
heapq.heapify(heap)
print(heap)
[20, 7, 5, 2, 2, 3, 1]

Concepto de Cola

El comportamiento de una cola se puede describir con la frase "Lo primero que se encoló es lo primero que se usa". Es decir, su estructura es FIFO (First in, First out)

Suponiendo que implementamos una cola usando una lista. ¿Cómo se podría implementar? ¿Cuál sería el costo?

Opción 1 Opción 2
enqueue encola al principio de la lista enqueue encola al final de la lista
dequeue desencola del final de la lista dequeue desencola del principio de la lista

Problema: En el primer caso encolar y en el segundo caso desencolar del principio implica desplazar todo el contenido de la lista (en un sentido u otro). Esta operación es costosa, imaginense una lista muy grande!

Deque como Cola

Deque: diseñado para appends y pops eficientes en ambos extremos

  • Operación encolar: se usa la función append()
  • Operacion dequeue (desencolar): se usa la función popleft()
In [26]:
from collections import deque

queue = deque(["a", "b", "c"])
print(queue)
queue.append("d")
print(queue)
queue.popleft()
print(queue)

for elemento in queue:
    print(elemento)
deque(['a', 'b', 'c'])
deque(['a', 'b', 'c', 'd'])
deque(['b', 'c', 'd'])
b
c
d
In [27]:
help(deque)
Help on class deque in module collections:

class deque(builtins.object)
 |  deque([iterable[, maxlen]]) --> deque object
 |  
 |  A list-like sequence optimized for data accesses near its endpoints.
 |  
 |  Methods defined here:
 |  
 |  __add__(self, value, /)
 |      Return self+value.
 |  
 |  __bool__(self, /)
 |      self != 0
 |  
 |  __contains__(self, key, /)
 |      Return key in self.
 |  
 |  __copy__(...)
 |      Return a shallow copy of a deque.
 |  
 |  __delitem__(self, key, /)
 |      Delete self[key].
 |  
 |  __eq__(self, value, /)
 |      Return self==value.
 |  
 |  __ge__(self, value, /)
 |      Return self>=value.
 |  
 |  __getattribute__(self, name, /)
 |      Return getattr(self, name).
 |  
 |  __getitem__(self, key, /)
 |      Return self[key].
 |  
 |  __gt__(self, value, /)
 |      Return self>value.
 |  
 |  __iadd__(self, value, /)
 |      Implement self+=value.
 |  
 |  __imul__(self, value, /)
 |      Implement self*=value.
 |  
 |  __init__(self, /, *args, **kwargs)
 |      Initialize self.  See help(type(self)) for accurate signature.
 |  
 |  __iter__(self, /)
 |      Implement iter(self).
 |  
 |  __le__(self, value, /)
 |      Return self<=value.
 |  
 |  __len__(self, /)
 |      Return len(self).
 |  
 |  __lt__(self, value, /)
 |      Return self<value.
 |  
 |  __mul__(self, value, /)
 |      Return self*value.
 |  
 |  __ne__(self, value, /)
 |      Return self!=value.
 |  
 |  __reduce__(...)
 |      Return state information for pickling.
 |  
 |  __repr__(self, /)
 |      Return repr(self).
 |  
 |  __reversed__(...)
 |      D.__reversed__() -- return a reverse iterator over the deque
 |  
 |  __rmul__(self, value, /)
 |      Return value*self.
 |  
 |  __setitem__(self, key, value, /)
 |      Set self[key] to value.
 |  
 |  __sizeof__(...)
 |      D.__sizeof__() -- size of D in memory, in bytes
 |  
 |  append(...)
 |      Add an element to the right side of the deque.
 |  
 |  appendleft(...)
 |      Add an element to the left side of the deque.
 |  
 |  clear(...)
 |      Remove all elements from the deque.
 |  
 |  copy(...)
 |      Return a shallow copy of a deque.
 |  
 |  count(...)
 |      D.count(value) -> integer -- return number of occurrences of value
 |  
 |  extend(...)
 |      Extend the right side of the deque with elements from the iterable
 |  
 |  extendleft(...)
 |      Extend the left side of the deque with elements from the iterable
 |  
 |  index(...)
 |      D.index(value, [start, [stop]]) -> integer -- return first index of value.
 |      Raises ValueError if the value is not present.
 |  
 |  insert(...)
 |      D.insert(index, object) -- insert object before index
 |  
 |  pop(...)
 |      Remove and return the rightmost element.
 |  
 |  popleft(...)
 |      Remove and return the leftmost element.
 |  
 |  remove(...)
 |      D.remove(value) -- remove first occurrence of value.
 |  
 |  reverse(...)
 |      D.reverse() -- reverse *IN PLACE*
 |  
 |  rotate(...)
 |      Rotate the deque n steps to the right (default n=1).  If n is negative, rotates left.
 |  
 |  ----------------------------------------------------------------------
 |  Static methods defined here:
 |  
 |  __new__(*args, **kwargs) from builtins.type
 |      Create and return a new object.  See help(type) for accurate signature.
 |  
 |  ----------------------------------------------------------------------
 |  Data descriptors defined here:
 |  
 |  maxlen
 |      maximum size of a deque or None if unbounded
 |  
 |  ----------------------------------------------------------------------
 |  Data and other attributes defined here:
 |  
 |  __hash__ = None