1. Resumen

Vamos a hacer una breve introducción a Python, explicando los tipos de datos que tenemos, los operadores, el uso de funciones y de clases. Además veremos cómo usar los objetos iterables, cómo usar módulos, etc.

python

2. Tipos de datos de Python

Existen 7 tipos de datos en Python

  1. De tipo texto:str
  2. Numéricos:int, float, complex
  3. Secuencias: list, tuple, range
  4. Mapping: dict
  5. Sets: set, frozenset
  6. Booleanos: bool
  7. Binarios: bytes, bytearray, memoryview

Podemos obtener el tipo de dato mediante la función type()

type(5.)
float

Python es un lenguaje de tipado dinámico, es decir puedes tener una variable de un tipo y luego asignarle otro tipo

a = 5
type(a)
int
a = 'DeepMaxFn'
type(a)
str

Python tipa las variables por ti, pero si las quieres tipar tu se puede hacer

b = int(5.1)
type(b), b
(int, 5)

Aunque b se ha inicializado como 5.1, es decir, debería ser de tipo float, al tiparlo nosotros a tipo int, vemos que es de tipo int y además su valor es 5

2.1. Strings

Los strings son cadenas de caracteres, estos se pueden definir con doble comilla " o comilla simple '

string = "DeepMaxFn"
string
'DeepMaxFn'
string = 'DeepMaxFn'
string
'DeepMaxFn'

Para escribir un string muy largo y no tener una fila que ocupe mucho espacio se puede introducir en varias lineas

string = """Este es un ejemplo de
como estoy introduciendo un string
en varias lineas"""
string
'Este es un ejemplo de\ncomo estoy introduciendo un string\nen varias lineas'
string = '''Este es un ejemplo de
como estoy introduciendo un string
en varias lineas'''
string
'Este es un ejemplo de\ncomo estoy introduciendo un string\nen varias lineas'

Sin embargo vemos que en medio ha metido el caracter \n, este caracter indica el salto de linea. Si usamos la función print() veremos como ya no aparece

print(string)
Este es un ejemplo de
como estoy introduciendo un string
en varias lineas

Como hemos dicho los strings son cadenas de caracteres, por lo que podemos navegar e iterar a traves de ellos

for i in range(10):
  # Se indica a la función print que cuando imprima no termine con un salto de 
  # linea para escribir todo en la misma linea
  print(string[i], end='')
Este es un

Podemos obtener la longitud de nuestro string mediante la función len()

len(string)
73

Checkear si hay ulgun string determinado dentro del nuestro

'ejemplo' in string
True

Los strings tienen ciertos atributos útiles, como poner todo en mayusculas

print(string.upper())
ESTE ES UN EJEMPLO DE
COMO ESTOY INTRODUCIENDO UN STRING
EN VARIAS LINEAS

Todo en minúsculas

print(string.lower())
este es un ejemplo de
como estoy introduciendo un string
en varias lineas

Reemplazar caracteres

print(string.replace('o', '@'))
Este es un ejempl@ de
c@m@ est@y intr@duciend@ un string
en varias lineas

Obtener todas las palabras

print(string.split())
['Este', 'es', 'un', 'ejemplo', 'de', 'como', 'estoy', 'introduciendo', 'un', 'string', 'en', 'varias', 'lineas']

Puedes ver todos los métodos de los strings en este enlace

Otra cosa util que se puede hacer con los strings es concatenarlos

string1 = 'DeepMax'
string2 = 'Fn'
string1 + string2
'DeepMaxFn'

Antes explicamos que el caracter \n correspondía a una salto de linea, este caracter especial corresponde a una serie de caracteres especiales llamados Escape Characters. Veamos otros

Si declaramos un string con doble comilla y queremos añadir una doble comilla dentro del string usamos el escape character \"

print("Este es el blog de \"DeepMaxFn\"")
Este es el blog de "DeepMaxFn"

Lo mismo con la comilla simple, añadimos \'

print('Este es el blog de \'DeepMaxFn\'')
Este es el blog de 'DeepMaxFn'

Ahora tenemos el problema de si queremos añadir el caracter \ ya que como hemos visto es un escape character, así que lo solucionamos poniendo doble barra (backslash) \\

print('Este es el blog de \\DeepMaxFn\\')
Este es el blog de \DeepMaxFn\

Ya vimos antes el escape character de nueva linea \n

print('Este es el blog de \nDeepMaxFn')
Este es el blog de 
DeepMaxFn

Si queremos escribir desde el inicio de linea añadimos \r

print('Esto no se imprimirá \rEste es el blog de DeepMaxFn')
Este es el blog de DeepMaxFn

Si queremos añadir añadir un espacio grande (sangría) usamos \t

print('Este es el blog de \tDeepMaxFn')
Este es el blog de 	DeepMaxFn

Podemos borrar un caracter con \b

print('Este es el blog de \bDeepMaxFn')
Este es el blog de DeepMaxFn

Podemos añadir el codigo ASCII en octal mediante \ooo

print('\104\145\145\160\115\141\170\106\156')
DeepMaxFn

O añadir el codigo ASCII en hexadecimal mediante \xhh

print('\x44\x65\x65\x70\x4d\x61\x78\x46\x6e')
DeepMaxFn

Por último, podemos convertir otro tipo de dato a string

n = 5
print(type (n))
string = str(n)
print(type(string))
<class 'int'>
<class 'str'>

2.2. Números

2.2.1. Enteros

Numeros de tipo entero

n = 5
n, type(n)
(5, int)

2.2.2. Float

Números de tipo de coma flotante

n = 5.1
n, type(n)
(5.1, float)

2.2.3. Complejos

Números complejos

n = 3 + 5j
n, type(n)
((3+5j), complex)

2.2.4. Conversión

Se puede convertir entre tipos de números

n = 5
n = float(n)
n, type(n)
(5.0, float)
n = 5.1
n = complex(n)
n, type(n)
((5.1+0j), complex)
n = 5.1
n = int(n)
n, type(n)
(5, int)

No se puede convertir un numero complex a tipo int o tipo float

2.3. Secuencias

2.3.1. Listas

Las listas guardan múltiples items en una variable. Se declaran mediante los símbolos [], con los items separados por comas

lista = ['item0', 'item1', 'item2', 'item3', 'item4', 'item5']
lista
['item0', 'item1', 'item2', 'item3', 'item4', 'item5']

Podemos obtener la longitud de una lista mediante la función len()

len(lista)
6

Las listas pueden tener items de distintos tipos

lista = ['item0', 1, True, 5.3, "item4", 5, 6.6]
lista
['item0', 1, True, 5.3, 'item4', 5, 6.6]

En Python se empieza a contar desde la posición 0, es decir, si queremos obtener la primera posición de la lista

lista[0]
'item0'

Pero una de las cosas potentes de Python es que si queremos acceder a la última posición podemos usar índices negativos

lista[-1]
6.6

Si en vez de la última posición de la lista queremos la penúltima

lista[-2]
5

Si solo queremos un rango de valores, por ejemplo, del segundo al quinto item accedemos mediante [2:5]

lista[2:5]
[True, 5.3, 'item4']

Si se omite el primer número del rango singnifica que queremos desde el primer item de la lista hasta el item indicado, es decir, si queremos desde el primer item hasta el quinto usamos [:5]

lista[:5]
['item0', 1, True, 5.3, 'item4']

Si se omite el último número del rango significa que queremos desde el item indicado hasta el último, es decir, si queremos desde el tercer item hasta el último usamos [3:]

lista[3:]
[5.3, 'item4', 5, 6.6]

Podemos escoger el rango de items también con números negativos, es decir, si queremos desde el antepenúltimo hasta el penúltimo usamos [-3:-1]. Esto es útil cuando se tiene listas que no se sabe su longitud, pero se sabe que se quiere un rango de valores del final, porque por ejemplo, la lista se ha creado con medidas que se van tomando y se quiere saber las últimas medias

lista[-3:-1]
['item4', 5]

Se puede comprobar si un item está en la lista

'item4' in lista
True
2.3.1.1. Editar listas

Las listas en Python son dinámicas, es decir, se pueden modificar. Por ejemplo se puede modificar el tercer item

lista[2] = False
lista
['item0', 1, False, 5.3, 'item4', 5, 6.6]

También se puede modificat un rango de valores

lista[1:4] = [1.1, True, 3]
lista
['item0', 1.1, True, 3, 'item4', 5, 6.6]

Se pueden añadir valores al final de la lista mediante el método append()

lista.append('item7')
lista
['item0', 1.1, True, 3, 'item4', 5, 6.6, 'item7']

O podemos insertar un valor en una posición determinada mediante el método insert()

lista.insert(2, 'insert')
lista
['item0', 1.1, 'insert', True, 3, 'item4', 5, 6.6, 'item7']

Se pueden unir listas mediante el método extend()

lista2 = ['item8', 'item9']
lista.extend(lista2)
lista
['item0', 1.1, 'insert', True, 3, 'item4', 5, 6.6, 'item7', 'item8', 'item9']

No es necesario extender la lista mediante otra lista, se puede hacer mediante otro tipo de dato iterable de Python (tuplas, sets, diccionarios, etc)

tupla = ('item10', 'item11')
lista.extend(tupla)
lista
['item0',
 1.1,
 'insert',
 True,
 3,
 'item4',
 5,
 6.6,
 'item7',
 'item8',
 'item9',
 'item10',
 'item11']

Podemos eliminar una posición determinada mediante el método pop()

lista.pop(2)
lista
['item0',
 1.1,
 True,
 3,
 'item4',
 5,
 6.6,
 'item7',
 'item8',
 'item9',
 'item10',
 'item11']

Si no se especifica el indice se elimina el último item

lista.pop()
lista
['item0', 1.1, True, 3, 'item4', 5, 6.6, 'item7', 'item8', 'item9', 'item10']

O se puede eliminar un item sabiendo su valor mediante el método remove()

lista.remove('item7')
lista
['item0', 1.1, True, 3, 'item4', 5, 6.6, 'item8', 'item9', 'item10']

Con la función del() se puede eliminar también un item de la posición indicada

del lista[3]
lista
['item0', 1.1, True, 'item4', 5, 6.6, 'item8', 'item9', 'item10']

Si no se indica el índice se elimina la lista entera

Con el método clear() deja la lista vacía

lista.clear()
lista
[]

Se puede obtener la cantidad de items con un valor determinado mediante el método count()

lista = [5, 4, 6, 5, 7, 8, 5, 3, 1, 5]
lista.count(5)
4

También se puede obtener el primer índice de un item con un valor determinado mediante el método index()

lista = [5, 4, 6, 5, 7, 8, 5, 3, 1, 5]
lista.index(5)
0
2.3.1.2. List comprehension

Podemos operar a través de la lista

fruits = ["apple", "banana", "cherry", "kiwi", "mango"]
newlist = []

# Iteramos por todos los items de la lista
for x in fruits:
  # Si el item contiene el caracter "a" lo añadimos a newlist
  if "a" in x:
    newlist.append(x)

newlist
['apple', 'banana', 'mango']

Otras de las cosas potentes de Python son las list comprehension, que permiten hacer todo en una sola linea y que el código quede más compacto

fruits = ["apple", "banana", "cherry", "kiwi", "mango"]

newlist = [x for x in fruits if "a" in x]

newlist
['apple', 'banana', 'mango']

La sintaxis es la siguiente

newlist = [expression for item in iterable if condition == True]

Se puede aprovechar para realizar operaciones en la lista original

newlist = [x.upper() for x in fruits if "a" in x]
newlist
['APPLE', 'BANANA', 'MANGO']
2.3.1.3. Ordenar listas

Para ordenar listas usamos el método sort()

lista = [5, 8, 3, 4, 9, 5, 6]
lista.sort()
lista
[3, 4, 5, 5, 6, 8, 9]

También nos las ordena allfabéticamente

lista = ["orange", "mango", "kiwi", "pineapple", "banana"]
lista.sort()
lista
['banana', 'kiwi', 'mango', 'orange', 'pineapple']

A la hora de ordenar alfabéticamente distingue entre mayúsculas y minúsculas

lista = ["orange", "mango", "kiwi", "Pineapple", "banana"]
lista.sort()
lista
['Pineapple', 'banana', 'kiwi', 'mango', 'orange']

Se pueden ordenar en orden descendente mediante el atributo reverse = True

lista = [5, 8, 3, 4, 9, 5, 6]
lista.sort(reverse = True)
lista
[9, 8, 6, 5, 5, 4, 3]

Se pueden ordenar de la manera que queramos mediante el atributo key

def myfunc(n):
  # devuelve el valor absoluto de n - 50
  return abs(n - 50)

lista = [100, 50, 65, 82, 23]
lista.sort(key = myfunc)
lista
[50, 65, 23, 82, 100]

Se puede aprovechar esto para que por ejemplo, a la hora de ordenar no distinga entre mayúsculas y minúsculas

lista = ["orange", "mango", "kiwi", "Pineapple", "banana"]
lista.sort(key = str.lower)
lista
['banana', 'kiwi', 'mango', 'orange', 'Pineapple']

Se puede voltear la lista mediante el método reverse

lista = [5, 8, 3, 4, 9, 5, 6]
lista.reverse()
lista
[6, 5, 9, 4, 3, 8, 5]
2.3.1.4. Copiar listas

No se pueden copiar listas mediante lista1 = lista2, ya que si se modifica lista1 también se modifica lista2

lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = lista1
lista1[0] = True
lista2
[True, 8, 3, 4, 9, 5, 6]

Por lo que hay que usar el método copy()

lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = lista1.copy()
lista1[0] = True
lista2
[5, 8, 3, 4, 9, 5, 6]

O hay que usar el constructor de listas list()

lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = list(lista1)
lista1[0] = True
lista2
[5, 8, 3, 4, 9, 5, 6]
2.3.1.5. Concatenar listas

Se pueden concatenar listas mediante el operador +

lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = ['a', 'b', 'c']
lista = lista1 + lista2
lista
[5, 8, 3, 4, 9, 5, 6, 'a', 'b', 'c']

O mediante el método extend

lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = ['a', 'b', 'c']
lista1.extend(lista2)
lista1
[5, 8, 3, 4, 9, 5, 6, 'a', 'b', 'c']

Otra forma de concatenar es repetir la tupla X veces mediante el operador *

lista1 = ['a', 'b', 'c']
lista2 = lista1 * 3
lista2
['a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c']

2.3.2. Tuplas

Las tuplas son similares a las listas, guardan múltiples items en una variable, pueden contener items de distintos tipos, pero no s epueden modificar, ni reordenar. Se definen mediante (), con los items separados por comas

Al no poderse modificar hace que las tuplas se ejecuten un poco más rápido que las listas, por lo que si no necesitas modificar los datos es mejor utilizar tuplas en vez de listas

tupla = ('item0', 1, True, 3.3, 'item4', True)
tupla
('item0', 1, True, 3.3, 'item4', True)

Se puede obtener su longitud mediante la función len()

len (tupla)
6

Para crear tuplas con un único elemento es necesario añadir una coma

tupla = ('item0',)
tupla, type(tupla)
(('item0',), tuple)

Para acceder a un elemento de la tupla se procede igual que con las listas

tupla = ('item0', 1, True, 3.3, 'item4', True)
print(tupla[0])
print(tupla[-1])
print(tupla[2:4])
print(tupla[-4:-2])
item0
True
(True, 3.3)
(True, 3.3)

Podemos comprobar si hay un item en la tupla

'item4' in tupla
True
2.3.2.1. Modificar tuplas

Aunque las tuplas no son modificables, se pueden modificar conviertiéndolas a listas, modificando la lista y después volviéndola a convertir a tupla

lista = list(tupla)
lista[4] = 'ITEM4'
tupla = tuple(lista)
tupla
('item0', 1, True, 3.3, 'ITEM4', True)

Al convertirla a lista podemos hacer todas las modificaciones vistas en las listas

Lo que sí se puede es eliminar la tupla entera

del tupla

if 'tupla' not in locals():
  print("tupla eliminada")
tupla eliminada
2.3.2.2. Desempaquetar tuplas

Cuando creamos tuplas, en realidad estamos empaquetando datos

tupla = ('item0', 1, True, 3.3, 'item4', True)
tupla
('item0', 1, True, 3.3, 'item4', True)

pero podemos desempaquetarlos

item0, item1, item2, item3, item4, item5 = tupla
item0, item1, item2, item3, item4, item5
('item0', 1, True, 3.3, 'item4', True)

Si queremos sacar menos datos que la longitud de la tupla añadimos un *

item0, item1, item2, *item3 = tupla
item0, item1, item2, item3
('item0', 1, True, [3.3, 'item4', True])

Se puede poner el asterisco * en otra parte si por ejemplo lo que queremos es el último item

item0, item1, *item2, item5 = tupla
item0, item1, item2, item5
('item0', 1, [True, 3.3, 'item4'], True)
2.3.2.3. Concatenar tuplas

Se pueden concatenar tuplas mediante el operador +

tupla1 = ("a", "b" , "c")
tupla2 = (1, 2, 3)

tupla3 = tupla1 + tupla2
tupla3
('a', 'b', 'c', 1, 2, 3)

Otra forma de concatenar es repetir la tupla X veces mediante el operador *

tupla1 = ("a", "b" , "c")

tupla2 = tupla1 * 3
tupla2
('a', 'b', 'c', 'a', 'b', 'c', 'a', 'b', 'c')
2.3.2.4. Métodos de las tuplas

Las tuplas tienen dos métodos, el primero es el método count() que devuelve el número de veces que existe un item dentro de la tupla

tupla = (5, 4, 6, 5, 7, 8, 5, 3, 1, 5)
tupla.count(5)
4

Otro método es index() que devuelve la primera posición de un item dentro de la tupla

tupla = (5, 4, 6, 5, 7, 8, 5, 3, 1, 5)
tupla.index(5)
0

2.3.3. Range

Con range() podemos crear una secuencia de números, comenzando desde 0 (de forma predeterminada), se incrementa en 1 (de forma predeterminada) y se detiene antes de un número especificado

range(start, stop, step)

Por ejemplo si queremos una secuencia de 0 a 5 (sin incluir el 5)

for i in range(5):
  print(f'{i} ', end='')
0 1 2 3 4 

Si por ejemplo no queremos que empiece en 0

for i in range(2, 5):
  print(f'{i} ', end='')
2 3 4 
for i in range(-2, 5):
  print(f'{i} ', end='')
-2 -1 0 1 2 3 4 

Por último, si no queremos que se incremente en 1. Si por ejemplo queremos una secuencia de número pares

for i in range(0, 10, 2):
  print(f'{i} ', end='')
0 2 4 6 8 

2.4. Diccionarios

Los diccionarios se usan para guardar datos en pares key:data. Son modificables, no ordenados y no permiten duplicidades. Se definen mediante los símbolos {}. Admiten items de distintos tipos de datos

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964,
  "colors": ["red", "white", "blue"]
}
diccionario
{'brand': 'Ford',
 'colors': ['red', 'white', 'blue'],
 'model': 'Mustang',
 'year': 1964}

Como se ha dicho no permiten duplicidades

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964,
  "year": 2000,
  "colors": ["red", "white", "blue"]
}
diccionario["year"]
2000

Se puede obtener su longitud mediante la función len()

len(diccionario)
4

Como se puede ver la longitud es 4 y no 5, ya que year lo cuenta solo una vez

2.4.1. Acceder a los items

Para acceder a un item lo podemos hacer a través de su key

diccionario["model"]
'Mustang'

También se puede acceder mediante el método get()

diccionario.get("model")
'Mustang'

Para saber todas las keys de los diccionarios se puede usar el método keys()

diccionario.keys()
dict_keys(['brand', 'model', 'year', 'colors'])

Se puede usar una variable para apuntar a las keys del diccionario, con lo que llamándola una vez es necesario

diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}

# Se declara una vez la variable que apunta a las keys
x = diccionario.keys()
print(x)

# Se añade una nueva key
diccionario["color"] = "white"

# Se consulta la variable que apunta a las key
print(x)
dict_keys(['brand', 'model', 'year'])
dict_keys(['brand', 'model', 'year', 'color'])

Para obtener los valores del diccionario se puede usar el método 'values()'

diccionario.values()
dict_values(['Ford', 'Mustang', 2000, ['red', 'white', 'blue']])

Se puede usar una variable para apuntar a los valuess del diccionario, con lo que llamándola una vez es necesario

diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}

# Se declara una vez la variable que apunta a los values
x = diccionario.values()
print(x)

# Se modifica un value
diccionario["year"] = 2020

# Se consulta la variable que apunta a los values
print(x)
dict_values(['Ford', 'Mustang', 1964])
dict_values(['Ford', 'Mustang', 2020])

Si lo que se quiere son los items enteros, es decir keys y values hay que usar el método items()

diccionario.items()
dict_items([('brand', 'Ford'), ('model', 'Mustang'), ('year', 2020)])

Se puede usar una variable para apuntar a los items del diccionario, con lo que llamándola una vez es necesario

diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}

# Se declara una vez la variable que apunta a los items
x = diccionario.items()
print(x)

# Se modifica un value
diccionario["year"] = 2020

# Se consulta la variable que apunta a los items
print(x)
dict_items([('brand', 'Ford'), ('model', 'Mustang'), ('year', 1964)])
dict_items([('brand', 'Ford'), ('model', 'Mustang'), ('year', 2020)])

Se puede checkear si una key existe en el diccionario

"model" in diccionario
True

2.4.2. Modificar los items

Se puede modificar un item accediendo a el directamente

diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}

# Se modifica un item
diccionario["year"] = 2020

diccionario
{'brand': 'Ford', 'model': 'Mustang', 'year': 2020}

O se puede modificar mediante el método update()

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# Se modifica un item
diccionario.update({"year": 2020})

diccionario
{'brand': 'Ford', 'model': 'Mustang', 'year': 2020}

2.4.3. Añadir items

Se puede añadir un item añadiéndolo sin más

diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}

# Se modifica un item
diccionario["colour"] = "blue"

diccionario
{'brand': 'Ford', 'colour': 'blue', 'model': 'Mustang', 'year': 1964}

O se puede añadir mediante el método update()

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# Se modifica un item
diccionario.update({"colour": "blue"})

diccionario
{'brand': 'Ford', 'colour': 'blue', 'model': 'Mustang', 'year': 1964}

2.4.4. Eliminar items

Se puede eliminar un item con una key específica mediante el método pop()

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# Se elimina un item
diccionario.pop("model")

diccionario
{'brand': 'Ford', 'year': 1964}

O se puede eliminar un item con una key específica mediante del indicando el nombre de la key entre los símbolos []

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# Se elimina un item
del diccionario["model"]

diccionario
{'brand': 'Ford', 'year': 1964}

Se elimina el diccionario entero si se usa del y no se especifica la key de un item

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# Se elimina un item
del diccionario

if 'diccionario' not in locals():
  print("diccionario eliminado")
diccionario eliminado

Si lo que se quiere es eliminar el último item introducido se puede usar el método popitem()

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}

# Se elimina el último item introducido
diccionario.popitem()

diccionario
{'brand': 'Ford', 'model': 'Mustang'}

Si se quiere limpiar el diccionario hay que usar el método clear()

diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
diccionario.clear()
diccionario
{}

2.4.5. Copiar diccionarios

No se pueden copiar diccionarios mediante diccionario1 = diccionario2, ya que si se modifica diccionario1 también se modifica diccionario2

diccionario1 = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
diccionario2 = diccionario1
diccionario1["year"] = 2000
diccionario2["year"]
2000

Por lo que hay que usar el método copy()

diccionario1 = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
diccionario2 = diccionario1.copy()
diccionario1["year"] = 2000
diccionario2["year"]
1964

O hay que usar el constructor de diccionarios dict()

diccionario1 = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964
}
diccionario2 = dict(diccionario1)
diccionario1["year"] = 2000
diccionario2["year"]
1964

2.4.6. Diccionarios nested

Los diccionarios pueden tener itemss de cualquier tipo de dato, incluso otros diccionarios. A este tipo de diccionarios se les denomina diccionarios nested

diccionario_nested = {
  "child1" : {
    "name" : "Emil",
    "year" : 2004
  },
  "child2" : {
    "name" : "Tobias",
    "year" : 2007
  },
  "child3" : {
    "name" : "Linus",
    "year" : 2011
  }
}
diccionario_nested
{'child1': {'name': 'Emil', 'year': 2004},
 'child2': {'name': 'Tobias', 'year': 2007},
 'child3': {'name': 'Linus', 'year': 2011}}
child1 = {
  "name" : "Emil",
  "year" : 2004
}
child2 = {
  "name" : "Tobias",
  "year" : 2007
}
child3 = {
  "name" : "Linus",
  "year" : 2011
}

diccionario_nested = {
  "child1" : child1,
  "child2" : child2,
  "child3" : child3
}

diccionario_nested
{'child1': {'name': 'Emil', 'year': 2004},
 'child2': {'name': 'Tobias', 'year': 2007},
 'child3': {'name': 'Linus', 'year': 2011}}

2.4.7. Métodos de los diccionarios

Estos son los métodos que se pueden usar en los diccionarios

2.5. Sets

2.5.1. Set

Los setss se utilizan en python para guardar un conjunto de items en una sola variable. Se puede guardar items de distinto tipo. Son no ordenados y no tienen indice.

Se diferencian de las listas en que no tienen ni orden ni índice.

Se declaran mediante los símbolos {}

Como set es una palabra reservada en Python creamos un set con el nombre set_

set_ = {'item0', 1, 5.3, "item4", 5, 6.6}
set_
{1, 5, 5.3, 6.6, 'item0', 'item4'}

No puede haber items duplicados, si encuentra algún item duplicado se queda solo con uno

set_ = {'item0', 1, 5.3, "item4", 5, 6.6, 'item0'}
set_
{1, 5, 5.3, 6.6, 'item0', 'item4'}

Se puede obtener la longitud del set mediante la función len()

len(set_)
6

Como se puede ver la longitud del set es 6 y no 7, ya que se queda con un solo 'item0'

Se puede checkear si un item se encuentra en el set

'item4' in set_
True
2.5.1.1. Añadir items

Se puede añadir un item al set mediante el método add()

set_.add(8.8)
set_
{1, 5, 5.3, 6.6, 8.8, 'item0', 'item4'}

Se puede añadir otro set mediante el método update()

set2 = {"item5", "item6", 7}
set_.update(set2)
set_
{1, 5, 5.3, 6.6, 7, 8.8, 'item0', 'item4', 'item5', 'item6'}

También se pueden añadir items de tipos de datos iterables de Python

lista = ["item9", 10, 11.2]
set_.update(lista)
set_
{1, 10, 11.2, 5, 5.3, 6.6, 7, 8.8, 'item0', 'item4', 'item5', 'item6', 'item9'}
2.5.1.2. Eliminar items

Se puede eliminar un item determinado mediante el método remove()

set_.remove('item9')
set_
{1, 10, 11.2, 5, 5.3, 6.6, 7, 8.8, 'item0', 'item4', 'item5', 'item6'}

O mediante el método discard()

set_.discard('item6')
set_
{1, 10, 11.2, 5, 5.3, 6.6, 7, 8.8, 'item0', 'item4', 'item5'}

Mediante el método pop() se puede eliminar el último item, pero como los sets no son ordenados no hay manera de saber cúal es el último item. El método pop() devuelve el item eliminado

print(f"set antes de pop(): {set_}")
eliminado = set_.pop()
print(f"Se ha eliminado {eliminado}")
set antes de pop(): {1, 5, 5.3, 6.6, 8.8, 7, 10, 11.2, 'item4', 'item0', 'item5'}
Se ha eliminado 1

Mediante el método clear() se puede vaciar el set

set_.clear()
set_
set()

Por úlitmo, con del se puede eliminar el set

del set_

if 'set_' not in locals():
  print("set eliminado")
set eliminado
2.5.1.3. Unir items

Una forma de unir sets es mediante el método union()

set1 = {"a", "b" , "c"}
set2 = {1, 2, 3}
set3 = set1.union(set2)
set3
{1, 2, 3, 'a', 'b', 'c'}

Otra forma es mediante el método update(), pero de esta manera se añade un set en otro, no se crea uno nuevo

set1 = {"a", "b" , "c"}
set2 = {1, 2, 3}
set1.update(set2)
set1
{1, 2, 3, 'a', 'b', 'c'}

Estos métodos de union elimina los duplicados, pero si queremos obtener los items duplicados en dos sets usamos el método intersection()

set1 = {"apple", "banana", "cherry"}
set2 = {"google", "microsoft", "apple"}

set3 = set1.intersection(set2)
set3
{'apple'}

Si queremos obtener los items duplicados en dos sets, pero sin crear un set nuevo, usamos el método intersection_update()

set1 = {"apple", "banana", "cherry"}
set2 = {"google", "microsoft", "apple"}

set1.intersection_update(set2)
set1
{'apple'}

Ahora al revés, si queremos quedarnos con los no duplicados usamos el método symmetric_difference().

La diferencia entre eso y la unión entre dos sets es que en la unión se queda con todos los items, pero los que están duplicados solo los coge una vez. Ahora nos quedamos con los que no están duplicados

set1 = {"apple", "banana", "cherry"}
set2 = {"google", "microsoft", "apple"}

set3 = set1.symmetric_difference(set2)
set3
{'banana', 'cherry', 'google', 'microsoft'}

Si queremos quedarnos con los no duplicados sin crear un set nuevo usamos el método symmetric_difference_update()

set1 = {"apple", "banana", "cherry"}
set2 = {"google", "microsoft", "apple"}

set1.symmetric_difference_update(set2)
set1
{'banana', 'cherry', 'google', 'microsoft'}
2.5.1.4. Métodos de los sets

Estos son los métodos que se pueden usar en los sets

2.5.2. Frozenset

Los frozensets son como los sets pero con la salvedad de que son inmutables, al igual que las tuplas son como las lists pero inmutables. Por lo que no podremos añadir o eliminar items

2.6. Booleanos

Hay solo dos booleanos en Python: True y False

Mediante la función bool() se puede evaluar si cualquier cosa es True o False

print(bool("Hello"))
print(bool(15))
print(bool(0))
True
True
False

2.6.1. Otros tipos de datos True o False

Los siguientes datos son True:

  • Cualquier string que no esté vacío
  • Cualquier número escepto el 0
  • Cualquier lista, tupla, diccionario o set que no esté vacío
print(bool("Hola"))
print(bool(""))
True
False
print(bool(3))
print(bool(0))
True
False
lista = [1, 2, 3]
print(bool(lista))

lista = []
print(bool(lista))
True
False
tupla = (1, 2, 3)
print(bool(tupla))

tupla = ()
print(bool(tupla))
True
False
diccionario = {
  "brand": "Ford",
  "model": "Mustang",
  "year": 1964,
  "colors": ["red", "white", "blue"]
}
print(bool(diccionario))

diccionario.clear()
print(bool(diccionario))
True
False
set_ = {'item0', 1, 5.3, "item4", 5, 6.6}
print(bool(set_))

set_.clear()
print(bool(set_))
True
False

2.7. Binarios

2.7.1. Bytes

El tipo bytes es una secuencia inmutable de bytes. Solo admiten caracteres ASCII. También se pueden representar los bytes mediante números enteros cuyo valores deben cumplir 0 <= x < 256

Para crear un tipo byte debemos introducir antes el caracter b

byte = b"DeepMaxFN"
byte
b'DeepMaxFN'

También se pueden crear mediante su contructor bytes()

byte = bytes(10)
byte
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
byte = bytes(range(10))
byte
b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t'

Se pueden concatenar bytes mediante el operador +

byte1 = b'DeepMax'
byte2 = b'FN'
byte3 = byte1 + byte2
byte3
b'DeepMaxFN'

O medainte la repetición con el operador *

byte1 = b'DeepMaxFN '
byte2 = byte1 * 3
byte2
b'DeepMaxFN DeepMaxFN DeepMaxFN '

Podemos comprobar si un caracter está dentro de la cadena

b'D' in byte1
True

Estos son los métodos que se pueden usar en los bytes

2.7.2. Bytearray

Los bytearrays son igual que los bytes solo que son mutables

byte_array = bytearray(b'DeepMaxFN')
byte_array
bytearray(b'DeepMaxFN')

2.7.3. Memoryview

Los objetos memoryview permiten que el código Python acceda a los datos internos de un objeto que admite el protocolo de búfer sin realizar copias.

La función memoryview() permite el acceso directo de lectura y escritura a los datos orientados a bytes de un objeto sin necesidad de copiarlos primero. Eso puede generar grandes ganancias de rendimiento cuando se opera con objetos grandes, ya que no crea una copia al cortar.

Protocolo de búfer, puede crear otro objeto de acceso para modificar los datos grandes sin copiarlos. Esto hace que el programa utilice menos memoria y aumenta la velocidad de ejecución.

byte_array = bytearray('XYZ', 'utf-8')
print(f'Antes de acceder a la memoria: {byte_array}')

mem_view = memoryview(byte_array)

mem_view[2]= 74
print(f'Después de acceder a la memoria: {byte_array}')
Antes de acceder a la memoria: bytearray(b'XYZ')
Después de acceder a la memoria: bytearray(b'XYJ')

3. Operadores

3.1. Operadores aritméticos

Operador suma +

3 + 5
8

Oeprador resta -

3 - 5
-2

Operador multiplicación *

3 * 5
15

Operador división /

3 / 5
0.6

Operador módulo %. Devuelve el resto de una división

25 % 2
1

Operador exponente **

5 ** 2
25

Operador división entera //

25 // 2
12

3.2. Operadores de comparación

Operador es igual ==

1 == 1
True

Operador es distinto !=

1 != 2
True

Operador es mayor que >

3 > 2
True

Operador es menor que <

2 < 3
True

Operador es mayor o igual que >=

3 >= 3
True

Operador es menor o igual que <=

3 <= 3
True

3.3. Operadores lógicos

Operador and

True and True
True

Operador or

True or False
True

Operador not

not False
True

3.4. Operadores de identidad

Operador is

5.3 is 5.3
True

Operador is not

5.3 is not 5
True

3.5. Operadores de pertenencia

Operador in

x = ["apple", "banana"]

"banana" in x
True

Operador not in

x = ["apple", "banana"]

"orange" not in x
True

3.6. Operadores bit a bit

Operador AND &

a = 60            # 60 = 0011 1100 
b = 13            # 13 = 0000 1101 

c = a & b;        # 12 = 0000 1100
c
12

Operador OR |

a = 60            # 60 = 0011 1100 
b = 13            # 13 = 0000 1101 

c = a | b;        # 61 = 0011 1101
c
61

Operador XOR ^

a = 60            # 60 = 0011 1100 
b = 13            # 13 = 0000 1101 

c = a ^ b;        # 49 = 0011 0001
c
49

Operador NOT ~

a = 60            # 60 = 0011 1100 

c = ~a;           # -61 = 1100 0011
c
-61

Operador desplazamiento hacia la izquierda <<

a = 60            # 60 = 0011 1100 

c = a << 2;       # 240 = 1111 0000
c
240

Operador desplazamiento hacia la derecha >>

a = 60            # 60 = 0011 1100 

c = a >> 2;       # 15 = 0000 1111
c
15

3.7. Operadores de asignación

Operador =

a = 5
a
5

Operador +=. x += y es equivalente a x = x + y

a += 5
a
10

Operador -=. x -= y es equivalente a `x = x - y

a -= 5
a
5

Operador *=. x *= y es equivalente a `x = x * y

a *= 3
a
15

Operador /=. x /= y es equivalente a `x = x / y

a /= 3
a
5.0

Operador %=. x %= y es equivalente a `x = x % y

a = 25
a %= 2
a
1

Operador //=. x //= y es equivalente a `x = x // y

a = 25
a //= 2
a
12

Operador **=. x **= y es equivalente a `x = x ** y

a = 5
a **= 2
a
25

Operador &=. x &= y es equivalente a `x = x & y

a = 60            # 60 = 0011 1100 
b = 13            # 13 = 0000 1101 

a &= b;           # 12 = 0000 1100
a
12

Operador |=. x |= y es equivalente a `x = x | y

a = 60            # 60 = 0011 1100 
b = 13            # 13 = 0000 1101 

a |= b;           # 61 = 0011 1101
a
61

Operador ^=. x ^= y es equivalente a `x = x ^ y

a = 60            # 60 = 0011 1100 
b = 13            # 13 = 0000 1101 

a ^= b;           # 49 = 0011 0001
a
49

Operador >>=. x >>= y es equivalente a `x = x >> y

a = 60            # 60 = 0011 1100 

a <<= 2;          # 240 = 1111 0000
a
240

Operador <<=. x <<= y es equivalente a `x = x << y

a = 60            # 60 = 0011 1100 

a >>= 2;          # 15 = 0000 1111
a
15

4. Control de flujo

Para poder utilizar las herramientas de control de flujo es necesario añadir la sentencia, dos puntos : y en una nueva línea escribir el códgo con indentación

A diferencia de otros lenguajes, Python necesita la indentación (añadir un espacio en blanco) para definir el código de dentro de una herramienta de control de flujo

4.1. If

Mediante if podemos crear condiciones

if len('DeepMaxFN') == 9:
  print('DeepMaxFN tiene 9 caracteres')
DeepMaxFN tiene 9 caracteres

Si queremos crear más de una condición podemos usar elif

if len('DeepMaxFN') < 9:
  print('DeepMaxFN tiene menos de 9 caracteres')
elif len('DeepMaxFN') == 9:
  print('DeepMaxFN tiene 9 caracteres')
DeepMaxFN tiene 9 caracteres

Si lo que queremos es que se ejecute algo en caso de que no se cumpla ninguna de las condiciones indicadas podemos usar else

if len('DeepMaxFN') < 9:
  print('DeepMaxFN tiene menos de 9 caracteres')
elif len('DeepMaxFN') > 9:
  print('DeepMaxFN tiene más de 9 caracteres')
else:
  print('DeepMaxFN tiene 9 caracteres')
DeepMaxFN tiene 9 caracteres

Si queremos escribir todo en una sola línea

if len('DeepMaxFN') == 9: print('DeepMaxFN tiene 9 caracteres')
DeepMaxFN tiene 9 caracteres

Igual, si queremos escribir todo en una línea, pero con varias condiciones

print('DeepMaxFN tiene menos de 9 caracteres') if len('DeepMaxFN') < 9 else print('DeepMaxFN tiene más de 9 caracteres') if len('DeepMaxFN') > 9 else print('DeepMaxFN tiene 9 caracteres')
DeepMaxFN tiene 9 caracteres

Si por ejemplo queremos hacer la estructura del if pero no queremos, de momento, codificar una de las condiciones podemos usar pass

if len('DeepMaxFN') < 9:
  print('DeepMaxFN tiene menos de 9 caracteres')
elif len('DeepMaxFN') > 9:
  pass
else:
  print('DeepMaxFN tiene 9 caracteres')
DeepMaxFN tiene 9 caracteres

4.2. While

El bucle while se ejecuta mientras la condición sea True

i = 0
string = 'DeepMaxFN'
while len(string) > i:
  print(string[i], end='')
  i += 1
DeepMaxFN

Si queremos que el bucle pare por alguna condición usamos break

i = 0
string = 'DeepMaxFN'
while len(string) > i:
  if string[i] == 'F':
    break
  print(string[i], end='')
  i += 1
DeepMax

Si queremos que una de las iteracciones no se ejecute por alguna razón usamos continue

i = 0
string = 'Deep Max FN'
while len(string) > i:
  if string[i] == ' ':
    i += 1
    continue
  print(string[i], end='')
  i += 1
DeepMaxFN

Mediante else se puede ejecutar un bloque de código si la condición del while no es True

i = 0
string = 'DeepMaxFN'

while len(string) > i:
  print(string[i], end='')
  i += 1
else:
  print("\nSe ha terminado el while")
DeepMaxFN
Se ha terminado el while

4.3. For

El bucle for se usa para ejecutar código mientras se itera por una secuencia, esta secuencia puede ser un cualquir elemento iterable de Python (string, lista, tupla, range, diccionario, set)

string = 'DeepMaxFN'

for x in string:
  print(x, end='')
DeepMaxFN
lista = ['D', 'e', 'e', 'p', 'M', 'a', 'x', 'F', 'N']

for x in lista:
  print(x, end='')
DeepMaxFN
tupla = ('D', 'e', 'e', 'p', 'M', 'a', 'x', 'F', 'N')

for x in tupla:
  print(x, end='')
DeepMaxFN
string = 'DeepMaxFN'

for i in range(len(string)):
  print(string[i], end='')
DeepMaxFN
diccionario = {
  "letra1": "D",
  "letra2": "e",
  "letra3": "e",
  "letra4": "p",
  "letra5": "M",
  "letra6": "a",
  "letra7": "x",
  "letra8": "F",
  "letra9": "N"
}

for x in diccionario.values():
  print(x, end='')
DeepMaxFN

También se puede iterar por los sets, pero como son elementos no ordenados, no tendremos control del orden de ejecución

set_ = {'D', 'e', 'e', 'p', 'M', 'a', 'x', 'F', 'N'}

for x in set_:
  print(x, end='')
epaFDxNM

Si queremos que el bucle pare por alguna condición usamos break

string = 'DeepMaxFN'

for x in string:
  if x == 'F':
    break
  print(x, end='') 
DeepMax

Si queremos que una de las iteracciones no se ejecute por alguna razón usamos continue

string = 'Deep Max FN'

for x in string:
  if x == ' ':
    continue
  print(x, end='') 
DeepMaxFN

Mediante else se puede ejecutar un bloque de código si la condición del while no es True

string = 'DeepMaxFN'

for x in string:
  print(x, end='') 
else:
  print("\nSe ha terminado el for")
DeepMaxFN
Se ha terminado el for

Si por ejemplo queremos hacer la estructura del for pero no queremos, de momento, codificar el interior podemos usar pass

string = 'DeepMaxFN'
for x in string:
  pass
print('Interior del for no codificado')
Interior del for no codificado

5. Funciones

Una función es una porción de código que se puede ejecutar tantas veces como quieras. Se le puede pasar argumentos y puede devolver datos como resultado

Para definir una función se comienza con la palabra reservada def seguido del nombre de la función, paréntesis (), dos puntos : y a continuación en una nueva línea indentado el código de la función

def funcion():
  print('DeepmaxFN')

Para llamar a la función solo es necesario escribir su nombre

funcion()
DeepmaxFN

A las funciones se le pueden pasar todos los argumentos que se quiera, dentro de los paréntesis y separados por comas

def funcion(string1, string2):
  print(string1 + ' ' + string2)

funcion("Hola", "DeepMaxFN")
Hola DeepMaxFN

Cuando se llama a la función hay que pasarle el mismo número de argumentos que se han declarado, si se pasan más o menos obtendremos un error.

Si no sabemos los argumentos que va a recibir la función se puede usar *args, es decir, poniendo un * antes de los argumentos se indica que el número de argumentos es libre.

Al hacer esto se le pasa una tupla (recordemos que es inmutable) con los argumentos

def funcion(*argumentos):
  numero_argumentos = len(argumentos)

  for i in range(numero_argumentos):
    print(argumentos[i], end=' ')

funcion("funcion", "con", "varios", "argumentos", "sin", "especificar", "cuantos")
funcion con varios argumentos sin especificar cuantos 

En caso de no saber el orden de los argumentos de una función, podemos indicar el argumento que le queremos pasar indicando su nombre

def funcion(argumento1, argumento2, argumento3):
  print(argumento1 + ' '+ argumento2 + ' ' + argumento3)

funcion(argumento3 = "DeepmaxFN", argumento1 = "Blog", argumento2 = "de")
Blog de DeepmaxFN

En caso de querer pasar los argumentos con su nombre, pero en caso de no saber cuantos argumentos se van a pasar se puede usar **kargs. En este caso se le pasará un diccionario con los argumentos

def funcion(**kargumentos):
  print("Autor del blog: " + kargumentos["autor"])

funcion(blog = "Blog", pertenencia = "de", autor = "DeepMaxFN")
Autor del blog: DeepMaxFN

Si queremos que algún argumento tenga un valor por defecto lo podemos indicar entre los paréntesis de la función. De esta manera si a la hora de llamar a la función no se pasa dicho argumento, este en la función tendrá el valor por defecto

def funcion(argumento1, argumento2, argumento3 = "DeepMaxFN"):
  print(argumento1 + ' '+ argumento2 + ' ' + argumento3)

funcion("Blog", "de")
Blog de DeepMaxFN

Se puede pasar cualquier tipo de dato como argumento, por ejemplo si se pasa un lista como argumento, dentro de la función, dicho argumento será tratado como una lista

def funcion(argumento):
  longitud_lista = len(argumento)

  for i in range(longitud_lista):
    print(argumento[i], end=' ')

funcion(["Blog", "de", "DeepMaxFN"])
Blog de DeepMaxFN 

Las funciones pueden devolver datos, esto se hace mediante la palabra reservada return

def funcion(argumento):
  longitud_lista = len(argumento)
  string = ""

  for i in range(longitud_lista):
    string = string + argumento[i] + ' '

  return string

print(funcion(["Blog", "de", "DeepMaxFN"]))
Blog de DeepMaxFN 

Pueden devolver más de un dato

def funcion(argumento):
  longitud_lista = len(argumento)
  string0 = argumento[0]
  string1 = argumento[1]
  string2 = argumento[2]

  return string0, string1, string2

dato0, dato1, dato2 = funcion(["Blog", "de", "DeepMaxFN"])

print(dato0 + ' ' + dato1 + ' ' + dato2)
Blog de DeepMaxFN

Si uno de los datos devueltos no nos interesa podemos pasar de el mediante _

def funcion(argumento):
  longitud_lista = len(argumento)
  string0 = argumento[0]
  string1 = argumento[1]
  string2 = argumento[2]

  return string0, string1, string2

_, _, dato_de_interes = funcion(["Blog", "de", "DeepMaxFN"])

print(dato_de_interes)
DeepMaxFN

Si por ejemplo queremos hacer la estructura de la función pero no queremos, de momento, codificar el interior podemos usar pass

def funcion():
  pass

funcion()

Una función puede llamarse a si misma, a esto se le llama recursión o recursividad de la función.

Por ejemplo podemos usar esta cualidad para calcular el factorial de un número

def factorial(n):
  if n == 0 or n == 1:
    return 1
  else:
    return n * factorial(n-1)

factorial(5)
120

5.1. Built in functions

Hay una serie de funciones ya definidas en Python que se pueden usar, como por ejemplo la función abs(), que devuelve el valor absoluto

abs(-5)
5

A continuación se muestra una lista de estas funciones

import builtins

dir(builtins)
['ArithmeticError',
 'AssertionError',
 'AttributeError',
 'BaseException',
 'BlockingIOError',
 'BrokenPipeError',
 'BufferError',
 'BytesWarning',
 'ChildProcessError',
 'ConnectionAbortedError',
 'ConnectionError',
 'ConnectionRefusedError',
 'ConnectionResetError',
 'DeprecationWarning',
 'EOFError',
 'Ellipsis',
 'EnvironmentError',
 'Exception',
 'False',
 'FileExistsError',
 'FileNotFoundError',
 'FloatingPointError',
 'FutureWarning',
 'GeneratorExit',
 'IOError',
 'ImportError',
 'ImportWarning',
 'IndentationError',
 'IndexError',
 'InterruptedError',
 'IsADirectoryError',
 'KeyError',
 'KeyboardInterrupt',
 'LookupError',
 'MemoryError',
 'ModuleNotFoundError',
 'NameError',
 'None',
 'NotADirectoryError',
 'NotImplemented',
 'NotImplementedError',
 'OSError',
 'OverflowError',
 'PendingDeprecationWarning',
 'PermissionError',
 'ProcessLookupError',
 'RecursionError',
 'ReferenceError',
 'ResourceWarning',
 'RuntimeError',
 'RuntimeWarning',
 'StopAsyncIteration',
 'StopIteration',
 'SyntaxError',
 'SyntaxWarning',
 'SystemError',
 'SystemExit',
 'TabError',
 'TimeoutError',
 'True',
 'TypeError',
 'UnboundLocalError',
 'UnicodeDecodeError',
 'UnicodeEncodeError',
 'UnicodeError',
 'UnicodeTranslateError',
 'UnicodeWarning',
 'UserWarning',
 'ValueError',
 'Warning',
 'ZeroDivisionError',
 '__IPYTHON__',
 '__build_class__',
 '__debug__',
 '__doc__',
 '__import__',
 '__loader__',
 '__name__',
 '__package__',
 '__spec__',
 'abs',
 'all',
 'any',
 'ascii',
 'bin',
 'bool',
 'bytearray',
 'bytes',
 'callable',
 'chr',
 'classmethod',
 'compile',
 'complex',
 'copyright',
 'credits',
 'delattr',
 'dict',
 'dir',
 'display',
 'divmod',
 'dreload',
 'enumerate',
 'eval',
 'exec',
 'filter',
 'float',
 'format',
 'frozenset',
 'get_ipython',
 'getattr',
 'globals',
 'hasattr',
 'hash',
 'help',
 'hex',
 'id',
 'input',
 'int',
 'isinstance',
 'issubclass',
 'iter',
 'len',
 'license',
 'list',
 'locals',
 'map',
 'max',
 'memoryview',
 'min',
 'next',
 'object',
 'oct',
 'open',
 'ord',
 'pow',
 'print',
 'property',
 'range',
 'repr',
 'reversed',
 'round',
 'set',
 'setattr',
 'slice',
 'sorted',
 'staticmethod',
 'str',
 'sum',
 'super',
 'tuple',
 'type',
 'vars',
 'zip']

5.2. Documentación de una función

Se puede añadir una explicación de una fucnión que creemos mediante un comentario al inicio de la función, de esta manera cuando llamemos a la built in function help() nos mostrará dicha explicación.

def funcion():
  "Esta es la explicación de la función"

  None

help(funcion)
Help on function funcion in module __main__:

funcion()
    Esta es la explicación de la función

Otra opción para ver la explicación de la función es usar el método __doc__ de la función

funcion.__doc__
'Esta es la explicación de la función'

5.3. Decoradores

Los decoradores son una funcionalidad de Python que premiten añadir características nuevas a una función

Se crea una función decorador que tiene como parámetro otra función. Entonces la función decorador añade la característica nueva a la función que recibe

def decorador(parametro_funcion):
  """Agrega barritas arriba y abajo de la funcion"""

  def envoltorio():
    """Aplica las barritas al texto"""

    print("==================")
    parametro_funcion()
    print("==================")
      
  return envoltorio

def funcion():
  print("DeepMaxFN")

funcion_envoltorio = decorador(funcion)

print('Función sin decoradores: ')
funcion()

print('\nFunción con decoradores: ')
funcion_envoltorio()
Función sin decoradores: 
DeepMaxFN

Función con decoradores: 
==================
DeepMaxFN
==================

Pero otra manera más potente de usar los decoradores es mediante el uso de @ y el nombre del decorador antes de la función.

Es decir, primero se define el decorador y a continuación se llama a una función con el decorador definido

def decorador2(parametro_funcion2):
  """Agrega barritas arriba y abajo de la funcion"""

  def envoltorio2():
    """Aplica las barritas al texto"""

    print("==================")
    parametro_funcion2()
    print("==================")
      
  return envoltorio2

@decorador2
def funcion2():
  print("DeepMaxFN")

print('Función con decoradores: ')
funcion2()
Función con decoradores: 
==================
DeepMaxFN
==================

6. Funciones adicionales

6.1. Funciones lambda

Una función lambda es una pequeña función anónima.

Una función lambda puede tomar cualquier número de argumentos, pero solo puede tener una expresión.

Las funciones lambda se definen de la siguiente manera:

    lambda arguments : expression
x = lambda a : a + 10
print(x(5))
15
x = lambda a, b, c : a + b + c
print(x(5, 6, 2))
13

El poder de lambda se muestra mejor cuando los usa como una función anónima dentro de otra función.

def myfunc(n):
  return lambda a : a * n

mydoubler = myfunc(2)
mytripler = myfunc(3)

print(f"mydoubler: {mydoubler(11)}")
print(f"mytripler: {mytripler(11)}")
mydoubler: 22
mytripler: 33

6.2. Función map

La función map permite aplicar a cada elemento de una estructura iterable una función

lista = [1, 2, 3]

def funcion_mas_1(valor):
  return valor + 1

lista_modificada = list(map(funcion_mas_1, lista))
lista_modificada
[2, 3, 4]

Esto es equivalente a usar list comprehension

lista_modificada = [funcion_mas_1(x) for x in lista]
lista_modificada
[2, 3, 4]

6.3. Función filter

La función filter permite seleccionar los elementos de una estructura iterable que cumplan con una característica

lista = [1, 2, 3, 4, 5, 6, 7]

def esPar(valor):
  return valor % 2 == 0

lista_filtrada = list(filter(esPar, lista))
lista_filtrada
[2, 4, 6]

Esto es equivalente a usar list comprehension

lista_filtrada = [x for x in lista if esPar(x)]
lista_filtrada
[2, 4, 6]

6.4. Función reduce

La función reduce permite realizar tareas acumulativas en estructuras iterables

from functools import reduce

lista = [1, 2, 3]

def acumular(valor, acumulador):
  print(f'valor = {valor}, acumulador = {acumulador}, acumulacion = {valor + acumulador}')
  return valor + acumulador

acumulacion = reduce(acumular, lista)
print(f'\nacumulacion = {acumulacion}')
valor = 1, acumulador = 2, acumulacion = 3
valor = 3, acumulador = 3, acumulacion = 6

acumulacion = 6
2+3+4+5+6+7
27

6.5. Función zip

Con la función zip se puede compibar varias estructuras iterables en una sola, es decir permite agrupar varias estructuras Ax en una sola estructura B. La estructura B está formada por tuplas de los elementos de las estructuras Ax

nombres = ["Manolo", "Andres", "Fernando"]
altura = [181, 178, 180]

my_zip = list(zip(nombres, altura))
my_zip
[('Manolo', 181), ('Andres', 178), ('Fernando', 180)]

7. Clases y objetos

Python es un lenguaje de programación orientado a objetos. Casi todo en Python es un objeto, con sus propiedades y métodos.

Una clase es como un constructor de objetos o un "plano" para crear objetos.

Para crear una clase se usa la palabra reservada class

class Clase:
  variable = 'DeepMaxFN'

Una vez creada la clase se puede crear un objeto de dicha clase

objeto = Clase()
Clase.variable
'DeepMaxFN'

Normalmente las clases tienen una función inicial, que se ejecuta cuando se crea un objeto de la clase. Esta función se denomina dunder init y se escribe __init__(). A la función dunder init se le tiene que pasar siempre la variable self, que indica la propia clase, y a continuación, las variables que se quiera

Con esta función se suelen inicializar las variables de las clases, o se ejecuta el código que se necesite cuando se crea un objeto de la clase

class Persona:
  def __init__(self, nombre, edad):
    self.nombre = nombre
    self.edad = edad

objeto_persona = Persona("Miguel", 36)

print(objeto_persona.nombre)
print(objeto_persona.edad)
Miguel
36

Además de la función inicial dunder init, se pueden crear más funciones. A estas funciones se les llama métodos de la clase. A estos métodos siempre hay que pasarles la variable self

class Persona:
  def __init__(self, nombre, edad):
    self.nombre = nombre
    self.edad = edad

  def saludar(self):
    print(f'Hola mi nombre es {self.nombre} y tengo {self.edad} años')

objeto_persona = Persona("Miguel", 36)

objeto_persona.saludar()
Hola mi nombre es Miguel y tengo 36 años

La variable self no tiene por qué ser llamada self, puede tener cualquier nombre, pero dentro de cada clase tiene que ser siempre el mismo. Pero por convenio se suele usar self

class Persona:
  def __init__(yo_mismo, nombre, edad):
    yo_mismo.nombre = nombre
    yo_mismo.edad = edad

  def saludar(yo_mismo):
    print(f'Hola mi nombre es {yo_mismo.nombre} y tengo {yo_mismo.edad} años')

objeto_persona = Persona("Miguel", 36)

objeto_persona.saludar()
Hola mi nombre es Miguel y tengo 36 años

Se pueden modificar las variables de los objetos

objeto_persona.nombre = 'Marta'
objeto_persona.saludar()
Hola mi nombre es Marta y tengo 36 años

Incluso eliminarlas

del objeto_persona.nombre

También se puede eliminar el objeto entero

del objeto_persona

Si por ejemplo queremos hacer la estructura de la clase pero no queremos, de momento, codificar el interior podemos usar pass

class Persona:
  pass

objeto_persona = Persona()

7.1. Herencia

La herencia nos permite definir una clase que hereda todos los métodos y propiedades de otra clase.

La clase padre es la clase de la que se hereda, también llamada clase base.

La clase hija es la clase que hereda de otra clase, también llamada clase derivada.

Creamos una clase padre

class Persona:
  def __init__(self, nombre, apellido):
    self.nombre = nombre
    self.apellido = apellido

  def imprimir_nombre(self):
    print(f'Me llamo {self.nombre} {self.apellido}')

objeto_padre = Persona("Laura", "Perez")
objeto_padre.imprimir_nombre()
Me llamo Laura Perez

Para crear la clase hija hay que indicar entre paréntesis, a la hora de declarar la clase, de qué clase hereda

class Estudiante(Persona):
  pass

Y a la hora de crear el objeto de la clase hija, se le pasan los parámetros que la clase padre necesita

objeto_hijo = Estudiante("Mariano", "Sanz")
objeto_hijo.imprimir_nombre()
Me llamo Mariano Sanz

Hasta ahora la clase hija ha heredado las funciones de la clase padre, pero podemos modificarlas reescribiéndolas. Por ejemplo reescribiendo la función duder init.

Si se reescribe la función dunder init, si queremos que se llame a la función dunder init de la clase padre hay que llamarla.

Para esto hay dos maneras, una es mediante el nombre de la clase padre, en este caso hay que pasarle la variable self

class Estudiante(Persona):
  def __init__(self, nombre, apellido):
    Persona.__init__(self, nombre, apellido)

objeto_hijo = Estudiante("Mariano", "Sanz")
objeto_hijo.imprimir_nombre()
Me llamo Mariano Sanz

Otra forma es mediante super(), en este caso no hace falta pasarle la variable self

class Estudiante(Persona):
  def __init__(self, nombre, apellido):
    super().__init__(nombre, apellido)

objeto_hijo = Estudiante("Mariano", "Sanz")
objeto_hijo.imprimir_nombre()
Me llamo Mariano Sanz

Al modificar las funciones se puede añadir código nuevo

class Estudiante(Persona):
  def __init__(self, nombre, apellido, curso):
    Persona.__init__(self, nombre, apellido)
    self.curso = curso

  def imprimir_nombre(self):
    Persona.imprimir_nombre(self)
    print(f'Estoy en el curso número {self.curso}')

objeto_hijo = Estudiante("Mariano", "Sanz", 4)
objeto_hijo.imprimir_nombre()
Me llamo Mariano Sanz
Estoy en el curso número 4

Por último se pueden añadir nuevos métodos

class Estudiante(Persona):
  def __init__(self, nombre, apellido, curso):
    Persona.__init__(self, nombre, apellido)
    self.curso = curso

  def imprimir_nombre(self):
    Persona.imprimir_nombre(self)
    print(f'Estoy en el curso número {self.curso}')

  def imprimir_estudiante(self):
    print("Soy un estudiante del curso número {self.curso}")

objeto_hijo = Estudiante("Mariano", "Sanz", 4)
objeto_hijo.imprimir_nombre()
objeto_hijo.imprimir_estudiante()
Me llamo Mariano Sanz
Estoy en el curso número 4
Soy un estudiante del curso número {self.curso}

8. Iteradores

Un iterador es un objeto que contiene un número contable de valores.

Un iterador es un objeto sobre el que se puede iterar, lo que significa que puede atravesar todos los valores.

Técnicamente, en Python, un iterador es un objeto que implementa el protocolo del iterador, que consta de los métodos __iter__() y __next__().

Las listas, tuplas, diccionarios y conjuntos son todos objetos iterables. Son contenedores iterables de los que puede obtener un iterador.

Todos estos objetos tienen un método iter() que se usa para obtener un iterador:

tupla = ("manzana", "plátano", "cereza")
iterable = iter(tupla)

print(next(iterable))
print(next(iterable))
print(next(iterable))
manzana
plátano
cereza
string = "plátano"
iterable = iter(string)

print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
print(next(iterable), end=' ')
p l á t a n o 

El bucle for en realidad crea un objeto iterador y ejecuta el método next() para cada bucle.

tupla = ("manzana", "plátano", "cereza")

for x in tupla:
  print(x)
manzana
plátano
cereza
string = "plátano"

for x in string:
  print(x, end=' ')
p l á t a n o 

8.1. Crear un objeto iterador

Para crear un objeto/clase como iterador, hay que implementar los métodos __iter__() y __next__().

class Numeros:
  def __iter__(self):
    self.a = 1
    return self

  def __next__(self):
    x = self.a
    self.a += 1
    return x

objeto_iterador = Numeros()
iterador = iter(objeto_iterador)

print(next(iterador), end=' ')
print(next(iterador), end=' ')
print(next(iterador), end=' ')
print(next(iterador), end=' ')
print(next(iterador), end=' ')
1 2 3 4 5 

El ejemplo anterior continuaría para siempre si tuviera suficientes llámadas a next(), o si se usara en un bucle for.

Para evitar que la iteración continúe para siempre, podemos usar la declaración StopIteration.

En el método __next__(), podemos agregar una condición de terminación para generar un error si la iteración se realiza un número específico de veces:

class Numeros:
  def __iter__(self):
    self.a = 1
    return self

  def __next__(self):
    if self.a <= 20:
      x = self.a
      self.a += 1
      return x
    else:
      raise StopIteration

objeto_iterador = Numeros()
iterador = iter(objeto_iterador)

for x in iterador:
  print(x, end=' ')
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 

9. Alcance de variables

Una variable solo está disponible dentro de la región en la que se crea. A esto se le llama alcance

9.1. Alcance local

Una variable creada dentro de una función pertenece al ámbito local de esa función y solo se puede usar dentro de esa función.

def funcion():
  x = 300
  print(x)

funcion()
300

La variable x no está disponible fuera de la función, pero está disponible para cualquier función dentro de la función

def funcion():
  x = 300
  def funcion_interna():
    print(x)
  funcion_interna()

funcion()
300

9.2. Alcance global

Una variable creada en el cuerpo principal del código Python es una variable global y pertenece al ámbito global.

Las variables globales están disponibles desde cualquier ámbito, global y local.

x = 300

def funcion():
  print(f'Ámbito local: {x}')

funcion()

print(f'Ámbito global: {x}')
Ámbito local: 300
Ámbito global: 300

Si se crean dos variables, una global y otra local, las dos con el mismo nombre, Python las creará como dos variables distintas

x = 300

def funcion():
  x = 200
  print(f'Variable local: {x}')

funcion()

print(f'Variable global: {x}')
Variable local: 200
Variable global: 300

Si se necesita crear una variable global, pero está declarada en el ámbito local, se puede usar la palabra clave global.

La palabra clave global hace que la variable sea global.

def funcion():
  global x
  x = 300

funcion()

print(f'Variable global: {x}')
Variable global: 300

Además, el uso de la palabra clave global realizar un cambio en una variable global dentro de una función.

x = 300

def funcion():
  global x
  x = 200

funcion()

print(f'Variable global: {x}')
Variable global: 200

10. Módulos

Un módulo es un archivo que contiene un conjunto de funciones que desea incluir en su aplicación.

Para crear un módulo, simplemente guarde el código que desea en un archivo con la extensión de archivo .py

Tip: En los cuadernos Jupyter (Colab es un cuaderno Jupyter en linea) si escribimos el caracter ! antes de un comando podremos ejecutar comandos de consola

Primero vamos a ver en qué directorio estamos, para eso usamos el comando pwd (print working directory)

!pwd
/content

A continuación veamos qué archivos hay es nuestro directorio. Esto lo haremos mediante el comando ls (list)

!ls /content
sample_data

Vemos que solo está la carpeta sample_data

%%writefile modulo.py

def funcion_del_modulo(nombre):
  print("Hola, " + nombre)
Overwriting modulo.py

Volvemos a ver qué archivos hay en nuestro directorio

!ls /content
modulo.py  sample_data

Vemos que se ha creado un archivo modulo.py. Ya podemos usarlo

Para usar un módulo externo hay que usar la palabra import. Para usar las funciones del módulo hay que poner primero el nombre del módulo, un . y a continuación el nombre de la función que se quiere usar

import modulo

modulo.funcion_del_modulo('DeepMaxFN')
Hola, DeepMaxFN

Si queremos que dentro de nuestro código, el módulo tenga un nombre determinado podemos usar la palabra as

import modulo as mod

mod.funcion_del_modulo('DeepMaxFN')
Hola, DeepMaxFN

Si el módulo tiene varias funciones, pero solo queremos importar una podemos mediante el uso de las palabras from e import. La forma sería

from <modulo> import <funcion>

En este caso no hace falta indicar el nombre del módulo al llamar a la función

%%writefile modulo.py

def funcion1_del_modulo(nombre):
  print("Hola, " + nombre + ", funcion 1")

def funcion2_del_modulo(nombre):
  print("Hola, " + nombre + ", funcion 2")

def funcion3_del_modulo(nombre):
  print("Hola, " + nombre + ", funcion 3")
Writing modulo.py
from modulo import funcion2_del_modulo

funcion2_del_modulo('DeepMaxFN')
Hola, DeepMaxFN, funcion 2

No solo podemos usar módulos creados por nosotros, sino módulos ya instalados (built-in modules)

Por ejemplo podemos usar el módulo platform

import platform

x = platform.system()
x
'Linux'

11. Try... Except

Cuando ocurre un error, o una excepción como se llama realmente, Python normalmente se detendrá y generará un mensaje de error.

Estas excepciones se pueden manejar usando las declaraciones try y except

try:
  print(variable_no_declarada)
except:
  print("Ha ocurrido una excepción")
Ha ocurrido una excepción

Dado que el bloque try genera un error, entonces se ejecutará el bloque except

Sin el bloque try, el programa se bloquearía y generaría un error

Se pueden definir tantos bloques de excepción como se desee, por ejemplo, si se quiere ejecutar un bloque de código especial para un tipo de error especial

try:
  print(x)
except NameError:
  print("La variable \'x\' no está definida")
except:
  print("Algo inesperado ha ocurrido")
La variable 'x' no está definida

Se puede usar la palabra else para indicar el caso en el que no haya ocurrido un error

try:
  print('DeepMaxFN')
except NameError:
  print("Ha ocurrido una excepción")
else:
  print('Todo OK')
DeepMaxFN
Todo OK

con la palabra finally se ejecutará un codigo al final haya ocurrido una excepción o no

try:
  print(x)
except:
  print("Ha ocurrido una excepción")
finally:
  print("'try except' finallizado")
Ha ocurrido una excepción
'try except' finallizado

Esto puede resultar útil para cerrar objetos y limpiar recursos

class Clase:
  variable = 'DeepMaxFN'

objeto = Clase()

try:
  print(Clase.mi_variable)
except:
  print("Ha ocurrido una excepción")
finally:
  del objeto
Ha ocurrido una excepción

11.1. Crear una excepción

Como desarrollador de Python, se puede elegir lanzar una excepción si ocurre una condición.

Para lanzar (o generar) una excepción, hay que usar la palabra clave raise

def division(numerador, denominador):
  if denominador == 0:
    raise Exception("El denominador no puede ser 0")
  
  return numerador/denominador

print(division(10, 0))
---------------------------------------------------------------------------
Exception                                 Traceback (most recent call last)
<ipython-input-9-33fb6066fa78> in <module>()
      5   return numerador/denominador
      6 
----> 7 print(division(10, 0))

<ipython-input-9-33fb6066fa78> in division(numerador, denominador)
      1 def division(numerador, denominador):
      2   if denominador == 0:
----> 3     raise Exception("El denominador no puede ser 0")
      4 
      5   return numerador/denominador

Exception: El denominador no puede ser 0

Se puede definir qué tipo de error generar y el texto que se imprimirá al usuario

def division(numerador, denominador):
  if denominador == 0:
    raise TypeError("El denominador no puede ser 0")
  
  return numerador/denominador

print(division(10, 0))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-10-26bfa63ae44c> in <module>()
      5   return numerador/denominador
      6 
----> 7 print(division(10, 0))

<ipython-input-10-26bfa63ae44c> in division(numerador, denominador)
      1 def division(numerador, denominador):
      2   if denominador == 0:
----> 3     raise TypeError("El denominador no puede ser 0")
      4 
      5   return numerador/denominador

TypeError: El denominador no puede ser 0

12. Keywords o palabras reservadas

Durante este post en varias ocasiones han aparecido palabras reservadas de Python o keywords, estas son una serie de palabras reservadas por Python

A continuación se muestra una lista de las keywords

import keyword

keyword.kwlist
['False',
 'None',
 'True',
 'and',
 'as',
 'assert',
 'break',
 'class',
 'continue',
 'def',
 'del',
 'elif',
 'else',
 'except',
 'finally',
 'for',
 'from',
 'global',
 'if',
 'import',
 'in',
 'is',
 'lambda',
 'nonlocal',
 'not',
 'or',
 'pass',
 'raise',
 'return',
 'try',
 'while',
 'with',
 'yield']