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.
Existen 7 tipos de datos en Python
- De tipo texto:
str
- Numéricos:
int
,float
,complex
- Secuencias:
list
,tuple
,range
- Mapping:
dict
- Sets:
set
,frozenset
- Booleanos:
bool
- Binarios:
bytes
,bytearray
,memoryview
Podemos obtener el tipo de dato mediante la función type()
type(5.)
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)
a = 'MaximoFN'
type(a)
Python tipa las variables por ti, pero si las quieres tipar tu se puede hacer
b = int(5.1)
type(b), b
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
Los strings
son cadenas de caracteres, estos se pueden definir con doble comilla "
o comilla simple '
string = "MaximoFN"
string
string = 'MaximoFN'
string
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
string = '''Este es un ejemplo de
como estoy introduciendo un string
en varias lineas'''
string
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)
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='')
Podemos obtener la longitud de nuestro string mediante la función len()
len(string)
Checkear si hay algun string determinado dentro del nuestro
'ejemplo' in string
Los strings tienen ciertos atributos útiles, como poner todo en mayusculas
print(string.upper())
Todo en minúsculas
print(string.lower())
Reemplazar caracteres
print(string.replace('o', '@'))
Obtener todas las palabras
print(string.split())
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 = 'Maximo'
string2 = 'FN'
string1 + string2
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 \"MaximoFN\"")
Lo mismo con la comilla simple, añadimos \'
print('Este es el blog de \'MaximoFN\'')
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 \\MaximoFN\\')
Ya vimos antes el escape character
de nueva linea \n
print('Este es el blog de \nMaximoFN')
Si queremos escribir desde el inicio de linea añadimos \r
print('Esto no se imprimirá \rEste es el blog de MaximoFN')
Si queremos añadir añadir un espacio grande (sangría) usamos \t
print('Este es el blog de \tMaximoFN')
Podemos borrar un caracter con \b
print('Este es el blog de \bMaximoFN')
Podemos añadir el codigo ASCII en octal mediante \ooo
print('\115\141\170\151\155\157\106\116')
O añadir el codigo ASCII en hexadecimal mediante \xhh
print('\x4d\x61\x78\x69\x6d\x6f\x46\x4e')
Por último, podemos convertir otro tipo de dato a string
n = 5
print(type (n))
string = str(n)
print(type(string))
Numeros de tipo entero
n = 5
n, type(n)
Números de tipo de coma flotante
n = 5.1
n, type(n)
Números complejos
n = 3 + 5j
n, type(n)
Se puede convertir entre tipos de números
n = 5
n = float(n)
n, type(n)
n = 5.1
n = complex(n)
n, type(n)
n = 5.1
n = int(n)
n, type(n)
No se puede convertir un numero complex
a tipo int
o tipo float
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
Podemos obtener la longitud de una lista mediante la función len()
len(lista)
Las listas pueden tener items de distintos tipos
lista = ['item0', 1, True, 5.3, "item4", 5, 6.6]
lista
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]
Pero una de las cosas potentes de Python es que si queremos acceder a la última posición podemos usar índices negativos
lista[-1]
Si en vez de la última posición de la lista queremos la penúltima
lista[-2]
Si solo queremos un rango de valores, por ejemplo, del segundo al quinto item accedemos mediante [2:5]
lista[2:5]
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]
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:]
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]
Se puede comprobar si un item está en la lista
'item4' in lista
Las listas en Python son dinámicas, es decir, se pueden modificar. Por ejemplo se puede modificar el tercer item
lista[2] = False
lista
También se puede modificat un rango de valores
lista[1:4] = [1.1, True, 3]
lista
Se pueden añadir valores al final de la lista mediante el método append()
lista.append('item7')
lista
O podemos insertar un valor en una posición determinada mediante el método insert()
lista.insert(2, 'insert')
lista
Se pueden unir listas mediante el método extend()
lista2 = ['item8', 'item9']
lista.extend(lista2)
lista
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
Podemos eliminar una posición determinada mediante el método pop()
lista.pop(2)
lista
Si no se especifica el indice se elimina el último item
lista.pop()
lista
O se puede eliminar un item sabiendo su valor mediante el método remove()
lista.remove('item7')
lista
Con la función del()
se puede eliminar también un item de la posición indicada
del lista[3]
lista
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)
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)
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
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
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
Para ordenar listas usamos el método sort()
lista = [5, 8, 3, 4, 9, 5, 6]
lista.sort()
lista
También nos las ordena allfabéticamente
lista = ["orange", "mango", "kiwi", "pineapple", "banana"]
lista.sort()
lista
A la hora de ordenar alfabéticamente distingue entre mayúsculas y minúsculas
lista = ["orange", "mango", "kiwi", "Pineapple", "banana"]
lista.sort()
lista
Se pueden ordenar en orden descendente mediante el atributo reverse = True
lista = [5, 8, 3, 4, 9, 5, 6]
lista.sort(reverse = True)
lista
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
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
Se puede voltear la lista mediante el método reverse
lista = [5, 8, 3, 4, 9, 5, 6]
lista.reverse()
lista
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
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
O hay que usar el constructor de listas list()
lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = list(lista1)
lista1[0] = True
lista2
Se pueden concatenar listas mediante el operador +
lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = ['a', 'b', 'c']
lista = lista1 + lista2
lista
O mediante el método extend
lista1 = [5, 8, 3, 4, 9, 5, 6]
lista2 = ['a', 'b', 'c']
lista1.extend(lista2)
lista1
Otra forma de concatenar es repetir la tupla X veces mediante el operador *
lista1 = ['a', 'b', 'c']
lista2 = lista1 * 3
lista2
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
Se puede obtener su longitud mediante la función len()
len (tupla)
Para crear tuplas con un único elemento es necesario añadir una coma
tupla = ('item0',)
tupla, type(tupla)
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])
Podemos comprobar si hay un item en la tupla
'item4' in tupla
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
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")
Cuando creamos tuplas, en realidad estamos empaquetando datos
tupla = ('item0', 1, True, 3.3, 'item4', True)
tupla
pero podemos desempaquetarlos
item0, item1, item2, item3, item4, item5 = tupla
item0, item1, item2, item3, item4, item5
Si queremos sacar menos datos que la longitud de la tupla añadimos un *
item0, item1, item2, *item3 = tupla
item0, item1, item2, item3
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
Se pueden concatenar tuplas mediante el operador +
tupla1 = ("a", "b" , "c")
tupla2 = (1, 2, 3)
tupla3 = tupla1 + tupla2
tupla3
Otra forma de concatenar es repetir la tupla X veces mediante el operador *
tupla1 = ("a", "b" , "c")
tupla2 = tupla1 * 3
tupla2
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)
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)
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='')
Si por ejemplo no queremos que empiece en 0
for i in range(2, 5):
print(f'{i} ', end='')
for i in range(-2, 5):
print(f'{i} ', end='')
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='')
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
Como se ha dicho no permiten duplicidades
diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964,
"year": 2000,
"colors": ["red", "white", "blue"]
}
diccionario["year"]
Se puede obtener su longitud mediante la función len()
len(diccionario)
Como se puede ver la longitud es 4 y no 5, ya que year
lo cuenta solo una vez
Para acceder a un item lo podemos hacer a través de su key
diccionario["model"]
También se puede acceder mediante el método get()
diccionario.get("model")
Para saber todas las key
s de los diccionarios se puede usar el método keys()
diccionario.keys()
Se puede usar una variable para apuntar a las key
s 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)
Para obtener los valores del diccionario se puede usar el método ‘values()’
diccionario.values()
Se puede usar una variable para apuntar a los values
s 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)
Si lo que se quiere son los item
s enteros, es decir key
s y value
s hay que usar el método items()
diccionario.items()
Se puede usar una variable para apuntar a los item
s 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)
Se puede checkear si una key
existe en el diccionario
"model" in diccionario
Se puede modificar un item
accediendo a el directamente
diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}
# Se modifica un item
diccionario["year"] = 2020
diccionario
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
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
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
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
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
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")
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
Si se quiere limpiar el diccionario hay que usar el método clear()
diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}
diccionario.clear()
diccionario
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"]
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"]
O hay que usar el constructor de diccionarios dict()
diccionario1 = {
"brand": "Ford",
"model": "Mustang",
"year": 1964
}
diccionario2 = dict(diccionario1)
diccionario1["year"] = 2000
diccionario2["year"]
Los diccionarios pueden tener items
s 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
}
diccionario_nested = {
"child1" : child1,
"child2" : child2,
"child3" : child3
}
diccionario_nested
Estos son los métodos que se pueden usar en los diccionarios
Igual que podíamos hacer lists comprehensions
mediante la sintaxis
list_comprehension = [expression for item in iterable if condition == True]
Podemos hacer disctionarys comprehensions
mediante la siguiente sintaxis
dictionary_comprehension = [key expresion: value expresion for item in iterable if condition == True]
Veamos un ejemplo
dictionary_comprehension = {x: x**2 for x in (2, 4, 6) if x > 2}
dictionary_comprehension
Los sets
s 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_
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_
Se puede obtener la longitud del set
mediante la función len()
len(set_)
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_
Se puede añadir un item al set mediante el método add()
set_.add(8.8)
set_
Se puede añadir otro set mediante el método update()
set2 = {"item5", "item6", 7}
set_.update(set2)
set_
También se pueden añadir items de tipos de datos iterables de Python
lista = ["item9", 10, 11.2]
set_.update(lista)
set_
Se puede eliminar un item determinado mediante el método remove()
set_.remove('item9')
set_
O mediante el método discard()
set_.discard('item6')
set_
Mediante el método pop()
se puede eliminar el último item, pero como los set
s 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}")
Mediante el método clear()
se puede vaciar el set
set_.clear()
set_
Por úlitmo, con del
se puede eliminar el set
del set_
if 'set_' not in locals():
print("set eliminado")
Una forma de unir sets es mediante el método union()
set1 = {"a", "b" , "c"}
set2 = {1, 2, 3}
set3 = set1.union(set2)
set3
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
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
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
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
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
Estos son los métodos que se pueden usar en los sets
Los frozenset
s son como los set
s pero con la salvedad de que son inmutables, al igual que las tupla
s son como las list
s pero inmutables. Por lo que no podremos añadir o eliminar items
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))
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(""))
print(bool(3))
print(bool(0))
lista = [1, 2, 3]
print(bool(lista))
lista = []
print(bool(lista))
tupla = (1, 2, 3)
print(bool(tupla))
tupla = ()
print(bool(tupla))
diccionario = {
"brand": "Ford",
"model": "Mustang",
"year": 1964,
"colors": ["red", "white", "blue"]
}
print(bool(diccionario))
diccionario.clear()
print(bool(diccionario))
set_ = {'item0', 1, 5.3, "item4", 5, 6.6}
print(bool(set_))
set_.clear()
print(bool(set_))
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"MaximoFN"
byte
También se pueden crear mediante su contructor bytes()
byte = bytes(10)
byte
byte = bytes(range(10))
byte
Se pueden concatenar bytes mediante el operador +
byte1 = b'DeepMax'
byte2 = b'FN'
byte3 = byte1 + byte2
byte3
O medainte la repetición con el operador *
byte1 = b'MaximoFN '
byte2 = byte1 * 3
byte2
Podemos comprobar si un caracter está dentro de la cadena
b'D' in byte1
Estos son los métodos que se pueden usar en los bytes
Los bytearray
s son igual que los bytes
solo que son mutables
byte_array = bytearray(b'MaximoFN')
byte_array
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}')
Operador suma +
3 + 5
Oeprador resta -
3 - 5
Operador multiplicación *
3 * 5
Operador división /
3 / 5
Operador módulo %
. Devuelve el resto de una división
25 % 2
Operador exponente **
5 ** 2
Operador división entera //
25 // 2
Operador es igual ==
1 == 1
Operador es distinto !=
1 != 2
Operador es mayor que >
3 > 2
Operador es menor que <
2 < 3
Operador es mayor o igual que >=
3 >= 3
Operador es menor o igual que <=
3 <= 3
Operador and
True and True
Operador or
True or False
Operador not
not False
Operador is
5.3 is 5.3
Operador is not
5.3 is not 5
Operador in
x = ["apple", "banana"]
"banana" in x
Operador not in
x = ["apple", "banana"]
"orange" not in x
Operador AND &
a = 60 # 60 = 0011 1100
b = 13 # 13 = 0000 1101
c = a & b; # 12 = 0000 1100
c
Operador OR |
a = 60 # 60 = 0011 1100
b = 13 # 13 = 0000 1101
c = a | b; # 61 = 0011 1101
c
Operador XOR ^
a = 60 # 60 = 0011 1100
b = 13 # 13 = 0000 1101
c = a ^ b; # 49 = 0011 0001
c
Operador NOT ~
a = 60 # 60 = 0011 1100
c = ~a; # -61 = 1100 0011
c
Operador desplazamiento hacia la izquierda <<
a = 60 # 60 = 0011 1100
c = a << 2; # 240 = 1111 0000
c
Operador desplazamiento hacia la derecha >>
a = 60 # 60 = 0011 1100
c = a >> 2; # 15 = 0000 1111
c
Operador =
a = 5
a
Operador +=
. x += y
es equivalente a x = x + y
a += 5
a
Operador -=
. x -= y
es equivalente a `x = x – y
a -= 5
a
Operador =
. x = y
es equivalente a `x = x * y
a *= 3
a
Operador /=
. x /= y
es equivalente a `x = x / y
a /= 3
a
Operador %=
. x %= y
es equivalente a `x = x % y
a = 25
a %= 2
a
Operador //=
. x //= y
es equivalente a `x = x // y
a = 25
a //= 2
a
Operador =
. x = y
es equivalente a `x = x ** y
a = 5
a **= 2
a
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
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
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
Operador >>=
. x >>= y
es equivalente a `x = x >> y
a = 60 # 60 = 0011 1100
a <<= 2; # 240 = 1111 0000
a
Operador <<=
. x <<= y
es equivalente a `x = x << y
a = 60 # 60 = 0011 1100
a >>= 2; # 15 = 0000 1111
a
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
Mediante if
podemos crear condiciones
if len('MaximoFN') == 8:
print('MaximoFN tiene 8 caracteres')
Si queremos crear más de una condición podemos usar elif
if len('MaximoFN') < 8:
print('MaximoFN tiene menos de 8 caracteres')
elif len('MaximoFN') == 8:
print('MaximoFN tiene 8 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('MaximoFN') < 8:
print('MaximoFN tiene menos de 8 caracteres')
elif len('MaximoFN') > 8:
print('MaximoFN tiene más de 8 caracteres')
else:
print('MaximoFN tiene 8 caracteres')
Si queremos escribir todo en una sola línea
if len('MaximoFN') == 8: print('MaximoFN tiene 8 caracteres')
Igual, si queremos escribir todo en una línea, pero con varias condiciones
print('MaximoFN tiene menos de 8 caracteres') if len('MaximoFN') < 8 else print('MaximoFN tiene más de 8 caracteres') if len('MaximoFN') > 8 else print('MaximoFN tiene 8 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('MaximoFN') < 8:
print('MaximoFN tiene menos de 8 caracteres')
elif len('MaximoFN') > 8:
pass
else:
print('MaximoFN tiene 8 caracteres')
El bucle while
se ejecuta mientras la condición sea True
i = 0
string = 'MaximoFN'
while len(string) > i:
print(string[i], end='')
i += 1
Si queremos que el bucle pare por alguna condición usamos break
i = 0
string = 'MaximoFN'
while len(string) > i:
if string[i] == 'F':
break
print(string[i], end='')
i += 1
Si queremos que una de las iteracciones no se ejecute por alguna razón usamos continue
i = 0
string = 'Maximo FN'
while len(string) > i:
if string[i] == ' ':
i += 1
continue
print(string[i], end='')
i += 1
Mediante else
se puede ejecutar un bloque de código si la condición del while
no es True
i = 0
string = 'MaximoFN'
while len(string) > i:
print(string[i], end='')
i += 1
else:
print("\nSe ha terminado el while")
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 = 'MaximoFN'
for x in string:
print(x, end='')
lista = ['M', 'a', 'x', 'i', 'm', 'o', 'F', 'N']
for x in lista:
print(x, end='')
tupla = ('M', 'a', 'x', 'i', 'm', 'o', 'F', 'N')
for x in tupla:
print(x, end='')
string = 'MaximoFN'
for i in range(len(string)):
print(string[i], end='')
diccionario = {
"letra1": "M",
"letra2": "a",
"letra3": "x",
"letra4": "i",
"letra5": "m",
"letra6": "o",
"letra7": "F",
"letra8": "N",
}
for x in diccionario.values():
print(x, end='')
También se puede iterar por los set
s, pero como son elementos no ordenados, no tendremos control del orden de ejecución
set_ = {'M', 'a', 'x', 'i', 'm', 'o', 'F', 'N'}
for x in set_:
print(x, end='')
Si queremos que el bucle pare por alguna condición usamos break
string = 'MaximoFN'
for x in string:
if x == 'F':
break
print(x, end='')
Si queremos que una de las iteracciones no se ejecute por alguna razón usamos continue
string = 'Maximo FN'
for x in string:
if x == ' ':
continue
print(x, end='')
Mediante else
se puede ejecutar un bloque de código si la condición del while
no es True
string = 'MaximoFN'
for x in string:
print(x, end='')
else:
print("\nSe 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 = 'MaximoFN'
for x in string:
pass
print('Interior del for no codificado')
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('MaximoFN')
Para llamar a la función solo es necesario escribir su nombre
funcion()
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", "MaximoFN")
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")
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 = "MaximoFN", argumento1 = "Blog", argumento2 = "de")
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 = "MaximoFN")
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 = "MaximoFN"):
print(argumento1 + ' '+ argumento2 + ' ' + argumento3)
funcion("Blog", "de")
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", "MaximoFN"])
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", "MaximoFN"]))
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", "MaximoFN"])
print(dato0 + ' ' + dato1 + ' ' + dato2)
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", "MaximoFN"])
print(dato_de_interes)
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)
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)
A continuación se muestra una lista de estas funciones
import builtins
dir(builtins)
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)
Otra opción para ver la explicación de la función es usar el método __doc__
de la función
funcion.__doc__
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("MaximoFN")
funcion_envoltorio = decorador(funcion)
print('Función sin decoradores: ')
funcion()
print('\nFunción con decoradores: ')
funcion_envoltorio()
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("MaximoFN")
print('Función con decoradores: ')
funcion2()
args
y *kwargs
son argumentos opcionales que se pueden usar al definir una función en Python. La sintaxis es la siguiente:
def mi_funcion(arg1, arg2, args, *kwargs):
# código de la función aquí
args
se usa para enviar un número variable de argumentos a una función. Al usar args
, puedes enviar una cantidad variable de argumentos a la función sin tener que especificar el número exacto de argumentos que necesita la función. Los argumentos se reciben en la función como una tupla.
def saludo(saludo, *nombres):
for nombre in nombres:
print(f"{saludo}, {nombre}")
saludo("Hola", "Alicia", "Roberto", "Carlos")
kwargs
se usa de la misma manera, pero para enviar un número variable de argumentos con palabras clave (keyword arguments
) a una función. Al usar kwargs
, puedes enviar una cantidad variable de argumentos a la función, y especificar el valor de cada argumento usando su nombre. Los argumentos se reciben en la función como un diccionario.
def saludo(saludo, **personas):
for key, value in personas.items():
print(f"{saludo} {key}, tu edad es {value} años")
saludo("Hola", Juan=22, Maria=32, Pedro=25)
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))
x = lambda a, b, c : a + b + c
print(x(5, 6, 2))
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)}")
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
Esto es equivalente a usar list comprehension
lista_modificada = [funcion_mas_1(x) for x in lista]
lista_modificada
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
Esto es equivalente a usar list comprehension
lista_filtrada = [x for x in lista if esPar(x)]
lista_filtrada
La función reduce
permite realizar tareas acumulativas en estructuras iterables
from functools import reduce
lista = [1, 22, 33]
def acumular(valor, acumulador):
print(f'valor = {valor}, acumulador = {acumulador}, acumulacion = {valor + acumulador}')
return valor + acumulador
acumulacion = reduce(acumular, lista)
print(f'\nacumulacion = {acumulacion}')
Con la función zip
se puede comprimir 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
Supongamos que queremos iterar sobre una secuencia de números, pero de una manera especial que no nos ofrece ningún tipo de bucle, pues esto lo podemos solucionar con los generadores. Para poder hacer esto, la función generadora no tiene que devolver el valor con return
, sino con yield
para que sepa que tiene que seguir iterando
def iterador_custom(N):
for i in range (N):
if i % 3 == 0:
yield i
generador = iterador_custom(20)
for i in generador:
print(i)
Acabamos de hacer un iterador por números múltiplos de 3
Podemos crear funciones que reciben otras funciones como parámetros, de manera que la función que recibe otra función como parámetro se llama función de orden superior (high order function). Veamos un ejemplo
def increment(x):
return x + 1
def hof(f, x):
return 2*f(x)
print(hof(increment, 3))
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 = 'MaximoFN'
Una vez creada la clase se puede crear un objeto de dicha clase
objeto = Clase()
Clase.variable
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)
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()
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()
Se pueden modificar las variables de los objetos
objeto_persona.nombre = 'Marta'
objeto_persona.saludar()
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()
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()
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()
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()
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()
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()
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(f"Soy un estudiante del curso número {self.curso}")
objeto_hijo = Estudiante("Mariano", "Sanz", 4)
objeto_hijo.imprimir_nombre()
objeto_hijo.imprimir_estudiante()
Podemos definir operaciones básicas, como la suma, entre varios objetos de una clase. Por ejemplo, si tenemos una clase que representa un vector, podemos definir la suma y la multiplicación entre objetos de dicha clase
class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
return Vector(self.x + other.x, self.y + other.y)
def __mul__(self, other):
return Vector(self.x * other.x, self.y * other.y)
def __str__(self):
return f"Vector ({self.x}, {self.y})"
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # Vector (4, 6)
print(v1 * v2) # Vector (3, 8)
Todas las posibles sobre carga de operaciones son:
__add__(self, other)
: sobrecarga el operador de suma (+
).__sub__(self, other)
: sobrecarga el operador de resta (-
).
__mul__(self, other)
: sobrecarga el operador de multiplicación ().
__truediv__(self, other)
: sobrecarga el operador de división (/
).__floordiv__(self, other)
: sobrecarga el operador de división de redondeo (//
).__mod__(self, other)
: sobrecarga el operador de módulo (%
).__divmod__(self, other)
: sobrecarga la funcióndivmod()
.
__pow__(self, other)
: sobrecarga el operador de potencia (*).
__lshift__(self, other)
: sobrecarga el operador de desplazamiento a la izquierda (<<
).__rshift__(self, other)
: sobrecarga el operador de desplazamiento a la derecha (>>
).__and__(self, other)
: sobrecarga el operador de and (&
).__or__(self, other)
: sobrecarga el operador de or (|
).__xor__(self, other)
: sobrecarga el operador de xor (^
).__lt__(self, other)
: sobrecarga el operador de comparación menor que (<
).__le__(self, other)
: sobrecarga el operador de comparación menor o igual que (<=
).__eq__(self, other)
: sobrecarga el operador de comparación igual a (==
).__ne__(self, other)
: sobrecarga el operador de comparación diferente a (!=
).__gt__(self, other)
: sobrecarga el operador de comparación mayor que (>
).__ge__(self, other)
: sobrecarga el operador de comparación mayor o igual que (>=
).__neg__(self)
: sobrecarga el operador de negación (-
).__pos__(self)
: sobrecarga el operador de posición (+
).__abs__(self)
: sobrecarga la funciónabs()
.__invert__(self)
: sobrecarga el operador de inversión (~
).__complex__(self)
: sobrecarga la funcióncomplex()
.__int__(self)
: sobrecarga la funciónint()
.__float__(self)
: sobrecarga la funciónfloat()
.
Como hemos visto en el apartado 2 (Tipos de datos de Python), existen algunos tipos de datos sobre los que se puede iterar. Pero podemos hacernos nuestro propia clase iterable, siempre que tenga las funciones __len__
y __getitem__
class custonIterator:
def __init__(self, n):
self.items = [i for i in range(n)]
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
iterator = custonIterator(10)
print(len(iterator)) # 10
print(iterator[0]) # 0
print(iterator[1]) # 1
Ahora podemo iterar con el objeto de nuestra clase con bucles for
por ejemplo
for i in iterator:
print(i, end=" ") # 0 1 2 3 4 5 6 7 8 9
Nos puede interesar llamar a un objeto de una función como una clase, esto se puede conseguir añadiendo la función __call__
a la clase
class potencia:
def __init__(self, base):
self.base = base
def __call__(self, potencia):
return self.base ** potencia
potencia_cuadrado = potencia(2)
print(potencia_cuadrado(3)) # 8
Cuando creamos una clase, podemos hacer que algunos atributos o funciones sean privados y no se pueda acceder desde fuera de la clase, para ello hay qye añadir __
antres del atributo a clase
class Privados:
def __init__(self):
self.publico = "Soy público"
self.__privado = "Soy privado"
def getPrivado(self):
return self.__privado
def setPrivado(self, valor):
self.__privado = valor
def __funcion_privada(self):
return "Soy una función privada"
def funcion_publica(self):
return self.__funcion_privada()
privados = Privados()
print("Acceso al atributo publico: ", end="")
try:
print(f"{privados.publico}")
except:
print("\tNo se puede acceder al atributo privado")
print("Acceso al atributo privado: ", end="")
try:
print(f"{privados.__privado}")
except:
print("\tNo se puede acceder al atributo privado")
print("Acceso al atributo privado mediante el accesor: ", end="")
try:
print(f"{privados.getPrivado()}")
except:
print("\tNo se puede acceder al atributo privado mediante el accesor")
print("Llamada a la función privada: ", end="")
try:
print(f"{privados.__funcion_privada()}")
except:
print("\tNo se puede llamar a la función privada")
print("Llamada a la función pública: ", end="")
try:
print(f"{privados.funcion_publica()}")
except:
print("\tNo se puede llamar a la función pública")
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))
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=' ')
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)
string = "plátano"
for x in string:
print(x, end=' ')
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=' ')
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=' ')
Podemos iterar por un objeto iterable obteniendo en cada iteracción su indice y su valor mediante el método enumerate()
string = "MaximoFN"
for index, valor in enumerate(string):
print(f"En la posición {index}, está el caracter {valor}")
Si tenemos dos objetos iterables, cuya longitud sea la misma, podemos iterar por los dos a la vez mediante el método zip()
string1 = 'MaximoFN__'
string2 = 'PythonPost'
if len(string1) == len(string2):
for valor1, valor2 in zip(string1, string2):
print(f"En el primer string hay {valor1}, en el segundo string hay {valor2}")
Una variable solo está disponible dentro de la región en la que se crea. A esto se le llama alcance
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()
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()
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}')
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}')
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}')
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}')
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
Vamos a crear una carpeta para crear nuestros módulos con el comando mkdir
(make directory)
!mkdir introduccion_python
A continuación veamos qué archivos hay es nuestra carpeta. Esto lo haremos mediante el comando ls
(list)
!ls introduccion_python
Vemos que está vacía, creamos un nuevo archivo .py
en el que vamos a crear nuestro módulo
%%writefile introduccion_python/modulo1.py
def funcion_del_modulo(nombre):
print("Hola, " + nombre)
Volvemos a ver qué archivos hay en nuestra carpeta
!ls introduccion_python
Vemos que se ha creado un archivo modulo1.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 introduccion_python.modulo1
introduccion_python.modulo1.funcion_del_modulo('MaximoFN')
Si queremos que dentro de nuestro código, el módulo tenga un nombre determinado podemos usar la palabra as
import introduccion_python.modulo1 as mod1
mod1.funcion_del_modulo('MaximoFN')
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
En este caso no hace falta indicar el nombre del módulo al llamar a la función
%%writefile introduccion_python/modulo2.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")
from introduccion_python.modulo2 import funcion2_del_modulo
funcion2_del_modulo('MaximoFN')
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
Vamos ahora a crear un archivo llamado modulo3.py
%%writefile introduccion_python/modulo3.py
print("Hola desde modulo3")
def funcion_del_modulo():
return "Hola desde la función del modulo3"
Si ahora importamos modulo3.py
para usar la función funcion_del_modulo
veamos qué ocurre
import introduccion_python.modulo3 as mod3
print(mod3.funcion_del_modulo())
Vemos que se ha ejecutado el print
de modulo3.py
, pero no es lo que nosotros queríamos, esto es debido a que al llamarse el archivo modulo3.py
python lo ejecuta como un script
Pero ¿qué ocurre si queremos ejecutar introduccion_python/main.py
como un scrip?
!python introduccion_python/modulo3.py
Vemos que solo se ejecuta el print
, pero no la función funcion_del_modulo
. Si queremos tener la dualidad de funcionalidad del archivo modulo3.py
, es decir, que podamos importarlo desde otro módulo sin que se ejecute como un script y ejecutarlo solo y que se ejecute la función que nosotros queremos se usa un entry point
. Esto es, usar la condición if __name__ == '__main__':
y a continuación indicar qué queremos que se ejecute. Veámoslo con un ejemplo, voy a reescribir el archivo modulo3.py
%%writefile introduccion_python/modulo3.py
print("Hola desde modulo3")
def funcion_del_modulo():
return "Hola desde la función del modulo3"
if __name__ == "__main__":
funcion_del_modulo()
Si ahora llamo a main.py
desde otro módulo, ya no se ejecutará el print
import introduccion_python.modulo3 as mod3
print(mod3.funcion_del_modulo())
Y si lo ejecuto como un script independiente, se ejecutará la función funcion_del_modulo
!python introduccion_python/modulo3.py
En python podemos crearnos nuestros propios paquetes, para ello creamos una carpeta con el nombre del paquete
!mkdir mi_paquete_de_python
Creamos ahora dos archivos dentro
!touch mi_paquete_de_python/modulo1.py mi_paquete_de_python/modulo2.py
Y escribimos en ellos
%%writefile mi_paquete_de_python/modulo1.py
def funcion1():
print("Hola desde la función 1 del módulo 1")
def funcion2():
print("Hola desde la función 2 del módulo 1")
%%writefile mi_paquete_de_python/modulo2.py
def funcion1():
print("Hola desde la función 1 del módulo 2")
def funcion2():
print("Hola desde la función 2 del módulo 2")
Ahora podemos llamar a las funciones de nuestro paquete
from mi_paquete_de_python import modulo1 as mod1
from mi_paquete_de_python import modulo2 as mod2
mod1.funcion1()
mod1.funcion2()
mod2.funcion1()
mod2.funcion2()
Pero qué ocurre si nuestro paquete tiene decenas de archivos con funciones que queremos usar, tendríamos que importar todos los archivos uno a uno. Para evitar esto, se puede crear un archivo __init__.py
dentro del paquete donde se haga toda esta importación de archivos
!touch mi_paquete_de_python/__init__.py
%%writefile mi_paquete_de_python/__init__.py
import modulo1
import modulo2
Ahora podemos solamente importar nuestro paquete, que ya internamente se han importado todos los módulos
import mi_paquete_de_python as mi_paquete
mi_paquete.modulo1.funcion1()
mi_paquete.modulo1.funcion2()
mi_paquete.modulo2.funcion1()
mi_paquete.modulo2.funcion2()
De esta manera solo tenemos que hacer un import
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")
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(variable_no_declarada)
except NameError:
print("La variable \'variable_no_declarada\' no está definida")
except:
print("Algo inesperado ha ocurrido")
Se puede usar la palabra else
para indicar el caso en el que no haya ocurrido un error
try:
print('MaximoFN')
except NameError:
print("Ha ocurrido una excepción")
else:
print('Todo OK')
con la palabra finally
se ejecutará un codigo al final haya ocurrido una excepción o no
try:
print(variable_no_declarada)
except:
print("Ha ocurrido una excepción")
finally:
print("'try except' finallizado")
Esto puede resultar útil para cerrar objetos y limpiar recursos
class Clase:
variable = 'MaximoFN'
objeto = Clase()
try:
print(Clase.mi_variable)
except:
print("Ha ocurrido una excepción")
finally:
del objeto
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))
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))
Durante este post en varias ocasiones han aparecido palabras reservadas de Python o keyword
s, estas son una serie de palabras reservadas por Python
A continuación se muestra una lista de las keyword
s
import keyword
keyword.kwlist
Importando el módulo this
podemos leer el zen
de Python, es decir, su filosofía o principios
import this
Continúa leyendo
- DoLa – Decoding by Contrasting Layers Improves Factuality in Large Language Models¿Alguna vez has hablado con un LLM y te ha respondido algo que suena como si hubiera estado bebiendo café de máquina durante toda la noche? 😂 ¡Eso es lo que llamamos una «alucinación» en el mundo de los LLMs! Pero no te preocupes, porque no es que tu modelo de lenguaje esté loco (aunque a veces puede parecerlo 🤪). La verdad es que los LLMs pueden ser un poco… creativos cuando se trata de generar texto. Pero gracias a DoLa, un método que utiliza capas de contraste para mejorar la factibilidad de los LLMs, podemos evitar que nuestros modelos de lenguaje se conviertan en escritores de ciencia ficción 😂. En este post, te explicaré cómo funciona DoLa y te mostraré un ejemplo de código para que puedas entender mejor cómo hacer que tus LLMs sean más fiables y menos propensos a inventar historias. ¡Vamos a salvar a nuestros LLMs de la locura y hacer que sean más útiles! 🚀
- QLoRA: Efficient Finetuning of Quantized LLMs¡Hola a todos! 🤗 Hoy vamos a hablar de QLoRA, la técnica que te permitirá hacer que tus modelos de lenguaje sean más eficientes y rápidos ⏱️. Pero, ¿cómo lo hace? 🤔 Bueno, primero utiliza la cuantización para reducir el tamaño de los pesos del modelo, lo que ahorra memoria y velocidad 📈. Luego, aplica LoRA (Low-Rank Adaptation), que es como un superpoder que permite al modelo adaptarse a nuevos datos sin necesidad de volver a entrenar desde cero 💪. Y, para que veas cómo funciona en la práctica, te dejo un ejemplo de código que te hará decir ‘¡Eureka!’ 🎉. ¡Vamos a sumergirnos en el mundo de QLoRA y descubrir cómo podemos hacer que nuestros modelos sean más inteligentes y eficientes! 🤓
- GPTQ: Accurate Post-Training Quantization for Generative Pre-trained Transformers¡Atención, desarrolladores! 🚨 ¿Tienes un modelo de lenguaje que es demasiado grande y pesado para tu aplicación? 🤯 ¡No te preocupes, GPTQ está aquí para ayudarte! 🤖 Este algoritmo de cuantización es como un mago que hace desaparecer los bits y bytes innecesarios, reduciendo el tamaño de tu modelo sin perder demasiada precisión. 🎩 Es como comprimir un archivo sin perder calidad. ¡Es una forma de hacer que tus modelos sean más eficientes y rápidos! 🚀
- llm.int8() – 8-bit Matrix Multiplication for Transformers at Scale¡Prepárate para ahorrar espacio y acelerar tus modelos! 💥 En este post, voy a explorar el método llm.int8(), una técnica de cuantización que te permite reducir el tamaño de tus modelos de aprendizaje automático sin sacrificar demasiada precisión. 📊 ¡Eso significa que podrás entrenar y desplegar modelos más grandes y complejos en menos espacio y con menor consumo de recursos! 💻 Vamos a ver cómo utilizar llm.int8() con transformers para cuantizar un modelo y hacer que sea más eficiente, sin perder la esencia de su inteligencia artificial. 🤖
- LLMs quantization¡Imagina que tienes un modelo de lenguaje gigante que puede responder a cualquier pregunta, desde la capital de Francia hasta la receta perfecta para hacer brownies! 🍞️🇫🇷 Pero, ¿qué pasa cuando ese modelo tiene que caber en un dispositivo móvil? 📱 ¡Eso es donde entra en juego la cuantización! 🎉 Esta técnica nos permite reducir el tamaño de los modelos sin sacrificar su precisión, lo que significa que podemos disfrutar de inteligencia artificial en nuestros dispositivos móviles sin necesidad de un supercomputador. 💻 ¡Es como comprimir un elefante en una caja de zapatos, pero sin aplastar al elefante! 🐘😂