Clases y Objectos¶
Definición¶
En informática hay mínimo 4 paradigmas o formas de programación:
- Imperativa: Indicar en cada momento a la máquina que debe hacer
- Procedural: Crear funciones para reducir dimensiónes de código, mejorar la escalabilidad y permitir un código más reusable.
- Funcional: Las funciones se definen como si fueran funciones matemáticas. Lo que lleva a una programación generalista más potente.
- Orientación a Objectos: Se define el concepto de clase como una representación general de algo específico permitiendo diseñar un código más expresivo.
Programación imperativa¶
Todo lo que necesitamos lo programamos nosotros y todo lo que ocurre lo hemos programado nosotros.
numero : int = 5
suma : int = 0
i : int = 1
while i <= numero:
suma = suma + i
i = i + 1
suma
15
numero : int = 7
suma : int = 0
i : int = 1
while i <= numero:
suma = suma + i
i = i + 1
suma
28
Programación procedural¶
Cuando sea necesario, creamos funciones para no tener que estar a cada rato escribiendo lo mismo.
Si lo necesitamos, haremos uso de funciones ya creadas por otras personas como las funciones built-in de python.
def calcular_suma(n : int) -> int:
numeros : list = []
for i in range(1, n + 1):
numeros = numeros + [i]
return sum(numeros)
calcular_suma(5), calcular_suma(7)
(15, 28)
Programación funcional¶
En la programación funcional, las funciones se llevan a otro nivel partiendo del concepto matemático de composición de funciones.
Además, las funciones pueden recibir otras funciones como parámetros.
Ejemplo:
- g(x) = x + 1
- f(g(x)) = x - 1
- g(1) = 1 + 1 = 2
- f(g(1)) = g(1) - 1 = 2 - 1 = 1
En python el ejemplo que hemos estado viendo (dado n, calcula suma 1, n + 1) podríamos calcularla combinado funciones.
n : int = 5
suma : int = sum(range(1, n + 1))
n : int = 7
suma1 : int = sum(range(1, n + 1))
def map(function, iterable) -> list:
return [function(x) for x in iterable]
def power_of_two(x : int) -> int:
return x ** 2
suma, suma1, map(power_of_two, [1, 2, 3])
(15, 28, [1, 4, 9])
Programación Orientada a Objectos¶
Hasta ahora, los tipos de datos que hemos visto son básicamente números, letras, listas, ...; Lo básico.
Sin embargo, hay problemas en donde necesitamos tipos de datos más complejos.
Necesitamos un código con más fuerza que nos permita hacer más cosas en pos de resolver el problema al que nos enfrentamos.
La Orientación a Objectos en resumen define 2 conceptos nuevos. Clase e Instancia u objeto.
El concepto Clase es una nueva estructura que tiene como objetivo representar datos y conceptos más complejos.
A veces más cercanos a nuestro mundo y otros más cercanos a un formalismo matématico. Depende del problema.
La representación se plantea de forma genérica. Es decir, si queremos representar un arbol, según el problema necesitaremos más o menos detalle.
Una posible representación sería:
- Arbol
- tiene_hojas : bool
- color_hojas : Tuple[int, int, int]
- tipo : perenne | caduco
- altura_tronco : float
- color_tronco : Tuple[int, int, int]
- ...
Si queremos representar una persona en una web. Es decir, un usuario de Facebook por ejemplo, una posible representación sería:
- Usuario
- nombre : string
- alias : string
- edad : int
- amigos : List[Usuario]
- posts : List[Post]
- ...
Estas clases, además de servir para representar algo y de tener datos (atributos) que definen ese concepto. Tambien pueden ser elementos activos, que hagan cosas (métodos).
Ejemplos:
- Un usuario de Facebook puede aceptar amigos, lo que añade un Usuario a la lista de amigos.
- Eliminar amigos, lo que elimina un Usuario de la lista de amigos.
- Un usuario de Facebook puede publicar un Post, lo que añade un Post a la lista de posts.
- Puede reaccionar a Post, lo que tiene unas implicaciones ...
El concepto Objeto representa la clase llevada a lo específico.
Por ejemplo, en el caso del Usuario. Un objeto es un Usuario con valores concretos para cada carácterística espeficada.
Según el tipo de clase puede haber 1 o varios objetos para dicha clase.
Siguiendo con la clase Usuario, un objeto sería un Usuario con nombre Maria, ... y otro objeto sería un Usuario con nombre Pepe, ...
¿Por qué esta chapa?¶
Porque todo, absolutamente todo en python son objectos.
Lo que hasta ahora hemos visto como los ints, floats, funciones, etc... Son objetos.
Para demostrarlo, introducimos una nueva función built-in dir(object = None). Esta lista (muestra) los métodos de un objeto.
Int no es solo un número entero
print(dir( int ))
numero : int = 8
numero.numerator, numero.bit_length()
['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'as_integer_ratio', 'bit_count', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes']
(8, 4)
List no es solo un contenedor ordenado de datos
print(dir( list ))
lista : list = []
lista.append(1)
lista.extend([1, 3, 1])
lista
['__add__', '__class__', '__class_getitem__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
[1, 1, 3, 1]
Las funciones también son objetos
def suma(a : int, b : int = 3) -> int:
return a + b
print(dir(suma))
print('Nombre: ', suma.__name__, '\nValores por defecto: ', suma.__defaults__, '\nClase de origen: ', suma.__class__, '\nTipos de datos anotados por parámetro y retorno: ', suma.__annotations__)
['__annotations__', '__builtins__', '__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__globals__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__kwdefaults__', '__le__', '__lt__', '__module__', '__name__', '__ne__', '__new__', '__qualname__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__']
Nombre: suma
Valores por defecto: (3,)
Clase de origen: <class 'function'>
Tipos de datos anotados por parámetro y retorno: {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
Resumen¶
Clase: Definición general de un concepto. Modela algo que no existe por defecto en nuestro programa y que lo necesitamos para resolver el problema en cuestión.
- Atributos: Datos representan caracteristicas del concepto a modelar
- Operaciones: Indica qué puede hacer una clase.
- Ejemplo: Un Animal se puede mover.
- Métodos: Es la implementación de una operación. La operación define textualmente qué puede hacer una clase y el método es el código que hace realidad esa operación.
Objeto o Instancia: Es una clase con valores concretos para los atributos definidos por la misma.
Si tenemos un Objeto y queremos acceder a un atributo o método. La forma es a través de la notación punto.
- Ejemplos:
- Si tenemos un objeto que se llama gaviota1 y tiene un método que se llama volar(), la forma de hacer volar a la gaviota es gaviota1.volar()
- Si tenemos un objeto que se llama satélite2 y tiene un atributo llama tipo_sensor, la forma de saber qué sensor tiene el sensor es satélite2.sensor
- Aunque de normal se debe crear un método que devuelve el atributo a consultar. La forma correcta sería satélite2.get_sensor() Pero eso no es parte del curso.
Si alguien quiere profundizar en cómo se programan clases en python aquí dejo una web buena para empezar y en español.
https://j2logo.com/python/tutorial/programacion-orientada-a-objetos/
La Clase String¶
Como dijimos en la última sección, todo en python en un objeto.
En esta sección enumeramos algunos de los métodos estándar de la clase String junto con ejemplos de uso.
Para más información es buena idea saber que existe la documentación oficial de python.
Pero normalmente está mejor usar alguna web para aprender porque simplifica mucho los conceptos de varios lenguajes:
Métodos más comunes¶
Listado de métodos
for method in dir(str):
print(method)
__add__ __class__ __contains__ __delattr__ __dir__ __doc__ __eq__ __format__ __ge__ __getattribute__ __getitem__ __getnewargs__ __gt__ __hash__ __init__ __init_subclass__ __iter__ __le__ __len__ __lt__ __mod__ __mul__ __ne__ __new__ __reduce__ __reduce_ex__ __repr__ __rmod__ __rmul__ __setattr__ __sizeof__ __str__ __subclasshook__ capitalize casefold center count encode endswith expandtabs find format format_map index isalnum isalpha isascii isdecimal isdigit isidentifier islower isnumeric isprintable isspace istitle isupper join ljust lower lstrip maketrans partition removeprefix removesuffix replace rfind rindex rjust rpartition rsplit rstrip split splitlines startswith strip swapcase title translate upper zfill
Uso de algunos a métodos
capitalize()
string_1 : str = 'hola'
string_1.capitalize()
'Hola'
count(sub[, start[, end]])
string_1 : str = 'hola'
string_1.count('a')
1
string_1.count('a', 0, 2)
0
endswith(suffix[, start[, end]])
string_1 : str = 'hola'
string_1.endswith('')
True
string_1.endswith('b')
False
startswith(suffix[, start[, end]])
string_1 : str = 'hola'
string_1.startswith('-')
False
string_1.startswith('h')
True
find(sub[, start[, end]])
string_1 : str = 'hola'
string_1.find('h')
0
string_1.find('a', 1, 2)
-1
format(args, *kwargs)
string_4 : str = 'Mi nombre es {} y mi edad es {}'
string_4.format('Sergio', 25)
'Mi nombre es Sergio y mi edad es 25'
format_map(mapping)
string_5 : str = 'Mi nombre es {nombre} y mi edad es {edad}'
string_5.format_map({'nombre' : 'Sergio', 'edad' : 25})
'Mi nombre es Sergio y mi edad es 25'
lower()
string_2 : str = 'AAA'
string_2.lower()
'aaa'
upper()
string_1 : str = 'hola'
string_1.upper()
'HOLA'
split(sub)
string_6 : str = 'A,B,C,D,E,F,G,H,I'
string_6.split(',')
['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I']
join(iterable)
string_7 : str = ','
string_7.join(['A', 'B', 'C', 'D', 'E'])
'A,B,C,D,E'
Estos métodos tienen una peculiaridad interesante además de útil. No son inplace. Es decir, cuando hacemos str.método(), ese método devuelve un string con la modicación.
Lo que permite concatenar varias llamadas a funciones en una misma línea sin necesidad de crear variables intermedias o cosas de ese estilo.
string : str = 'aaaaA_bCsasddede'
string.lower().capitalize().replace('_', '').replace('a', '').replace('s', '')
'Abcddede'
Consultar la documentación¶
No olvidemos que existe la función help(obj) que nos devuelve el docstring (documentación) de un obj, función, ...
# Help __add__
help(str.__add__)
print('-' * 50 + '\n')
# Help capitalize
help(str.capitalize)
print('-' * 50 + '\n')
# Help __getitem__
help(str.__getitem__)
print('-' * 50 + '\n')
# Help format
help(str.format)
Help on wrapper_descriptor:
__add__(self, value, /)
Return self+value.
--------------------------------------------------
Help on method_descriptor:
capitalize(self, /)
Return a capitalized version of the string.
More specifically, make the first character have upper case and the rest lower
case.
--------------------------------------------------
Help on wrapper_descriptor:
__getitem__(self, key, /)
Return self[key].
--------------------------------------------------
Help on method_descriptor:
format(...)
S.format(*args, **kwargs) -> str
Return a formatted version of S, using substitutions from args and kwargs.
The substitutions are identified by braces ('{' and '}').
Prefijos¶
f-strings
Son una alternativa a la función format(arg, *kwargs).
Actualmente se recomienda más usar f-strings porque se crearon para hacer un código más potente y a su vez más corto
variable : int = 1
print( f'{variable}' ) # Mostrar valor de una variable
print( f'{variable=}' ) # Mostrar valor de una variable y el nombre de la variable
1 variable=1
r raw-strings
Raw (crudo), significa que no que cada caracter se interpreta como lo que es, un caracter.
Es decir, la expresión \n genera una nueva línea dentro del string, \t genera el resultado de pulsar la tecla tabulador.
Sin embargo, si ponemos una r delante del string, lo mencionado arriba no ocurre, cada caracter se interpreta como lo que es.
print( '\ta' )
a
print( r'\ta' )
\ta
print( '\na' )
a
print( r'\na' )
\na
Ejercicios¶
- Dada una cadena de strings, calcula la frecuencia de aparación de todos los caracteres.
- Dada una cadena de strings escrita en snake_case
- Transformarla a una cadena escrita en PascalCase
- snake_case: hola_mundo, palabra1_palabra2_palabra3_palabra4
- PascalCase: HolaMundo, Palabra1Palabra2Palabra3Palabra4
- Dada una cadena de strings, reemplaza las minúsculas por el símbolo *
La Clase List¶
Poco a poco vamos afianzando la idea de que en Python todo son objetos.
Lo importante es saber que un objeto tiene métodos y atributos.
Los atributos son variables y los métodos son funciones que actuan sobre el objeto.
Podemos acceder a los atributos y métodos a través de la notación punto. objeto.método()
Bien, tras recordar esto un poco, vamos a explicar cómo se trabajan con las listas y vamos a dar algunos nuevos conceptos sobre el tipo de dato Secuencia.
Recordemos que los strings, las listas y las tuplas son Secuencias.
Métodos de las listas¶
append(obj)
lista : list = []
lista.append(1)
lista
[1]
count(obj)
lista2 : list = [1, 2, 3, 4, 1, 1, 1, 2]
lista2.count(1)
4
extend(iterable)
lista : list = []
lista.extend([1, 2, 3])
lista
[1, 2, 3]
copy()
lista : list = []
lista_copia : list = lista
print('ID lista_copia:', id(lista_copia), 'ID lista:', id(lista), '¿Idénticos?', id(lista_copia) == id(lista))
ID lista_copia: 2365957555648 ID lista: 2365957555648 ¿Idénticos? True
lista_copia : list = lista.copy()
print('ID lista_copia:', id(lista_copia), 'ID lista:', id(lista), '¿Idénticos?', id(lista_copia) == id(lista))
ID lista_copia: 2365958326016 ID lista: 2365957555648 ¿Idénticos? False
index(obj)
lista2 : list = [1, 2, 3, 4, 1, 1, 1, 2]
lista2.index(1)
0
try:
print(f'{lista2=}.index(obj):', lista2.index(-1))
except ValueError as exception:
print('Si busco algo que no está en la lista:', f'"{exception}"')
Si busco algo que no está en la lista: "-1 is not in list"
insert(index, object)
lista : list = []
lista.insert(0, -991)
lista.insert(0, -2)
lista
[-2, -991]
pop(index)
lista : list = [1, 2, 3]
lista.pop(0)
1
lista
[2, 3]
lista : list = [2, 3]
try:
lista.pop(0)
print(lista)
lista.pop(0)
print(lista)
lista.pop(0)
except Exception as exception:
print('Si intento eliminar datos de una lista vacía:', f'"{exception}"')
[3] [] Si intento eliminar datos de una lista vacía: "pop from empty list"
remove(obj)
lista : list = [1, 2, 3]
lista.remove(1)
lista
[2, 3]
lista : list = [1, 2, 3]
try:
lista.remove(0)
except Exception as exception:
print('Si elimino algo que no está en la lista:', f'"{exception}"')
Si elimino algo que no está en la lista: "list.remove(x): x not in list"
reverse()
lista : list = [1, 2, 3]
lista.reverse()
lista
[3, 2, 1]
sort()
lista : list = [3, 4, 1, 2, -3]
lista.sort()
lista
[-3, 1, 2, 3, 4]
clear()
lista : list = [1, 2, 3]
lista.clear()
lista
[]
Los métodos de las listas, a diferencia de los strings, de normal son inplace. Es decir, objeto.método(params) modifica el objeto dentro del código del método y devuelve None.
Esto hace que no podamos concatenar varias funciones. Si queremos hacer varias operaciones debemos escribir una nueva linea por operacion.
lista : list = []
lista.append(1)
lista.append(0)
lista.remove(0)
# No podemos hacer [].append(1).append(0).remove(0).
Acceso a los elementos de las secuencias¶
Acceso a un elemento concreto por índice
lista : list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
print(lista[0]) # El primer elemento empieza en el cero
print(lista[1])
print(lista[-1]) # Si queremos acceder desde el último elemento pero no queremo usar el índice exacto (o no lo conocemos), usamos -1, -2, ...
print(lista[-2]) # Si queremos acceder desde el último elemento pero no queremo usar el índice exacto (o no lo conocemos), usamos -1, -2, ...
1 2 9 8
Slicing¶
Si tenemos una secuencia, podemos obtener una subsecuencia a usando la nomenclatura.
secuencia[start : end : step]
Con esto, obtenemos una subsecuencia a partir de una patrón de índices.
Ejemplo:
[0 : 10 : 1] -> 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 || El número end, en este caso 10, nunca se incluye en los índices generados
[0 : 10 : 2] -> 0, 2, 4, 6, 8 || El número end, en este caso 10, nunca se incluye en los índices generados
[0 : None] -> Devuelve todos los índices, la secuencia completa. end = None equivale a len(secuencia).
[:] -> Devuelve todos los índices. Es una copia de la lista. Lo mismo que hacer lista.copy()
[::] -> Devuelve todos los índices. Es una copia de la lista. Lo mismo que hacer lista.copy()
[::-1] -> Devuelve la lista invertida. Ej [0, 1, 2][::-1] -> [2, 1, 0]
lista : list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(lista[0 : 10 : 1])
print(lista[0 : 10 : 2])
print(lista[0 : None], '------', lista[0 : len(lista)])
print(lista[0 : -1]) # Si queremos incluir el último elemento usar None, nunca -1
print(lista[:]) # Si queremos incluir el último elemento usar None, nunca -1
print(lista[::]) # Si queremos incluir el último elemento usar None, nunca -1
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 2, 4, 6, 8] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ------ [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Modificar un elemento¶
Depende del dato que almacena la secuencia.
Si tenemos una lista de enteros, podemos sumar, restar, ...
Si tenemos una lista de listas, podemos aplicar métodos de listas, ...
lista_enteros : list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
lista_enteros[0] = -1 # Asignar un dato
lista_enteros[0] *= 90 # Hace una operacion y asignar
lista_enteros[1] * 0 # Si no ponemos el operacion de asignación, el dato de la lista no se modifica || Al menos en este caso que el dato es inmmutable.
lista_enteros
[-90, 2, 3, 4, 5, 6, 7, 8, 9]
lista_listas : list = [[], []]
lista_listas[0].append(1)
lista_listas # En este caso al ser mutable, si un método modifica el dato en cuestión pos listo, no es necesario usar el operador de asignación.
[[1], []]
Consejos¶
No modificar e iterar sobre la misma lista
Si tenemos una lista y queremos eliminar o añadir elementos a la vez que recorremos dicha lista, lo correcto es una de dos:
- Recorrer una copia y modificar la lista original
- Recorrer la lista original y modificar la copia
lista : list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in lista:
if i < 5:
lista.remove(i)
lista
[2, 4, 5, 6, 7, 8, 9]
lista : list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in lista[:]:
if i < 5:
lista.remove(i)
lista
[5, 6, 7, 8, 9]
lista : list = [1, 2, 3, 4, 5, 6, 7, 8, 9]
for i in lista.copy():
if i < 5:
lista.remove(i)
lista
[5, 6, 7, 8, 9]
Copiar las listas en vez de asignar tal cual
lista1 : list = [1, 2, 3]
lista2 : list = lista1
lista2.clear()
lista1
[]
lista1 : list = [1, 2, 3]
lista2 : list = lista1.copy()
lista2.clear()
lista1
[1, 2, 3]
Ejercicios¶
- Dada una lista, elimina los elementos repetidos
- Dada la lista [1, 2, 'a', [1, 2]], calcula el índice o posición del elemento 'a'
- Dada una lista, ordénala de mayor a menor
- Dada una lista, obtén los elementos en las posiciones impares y multiplicalos por 2 (cuidado con posibles excepciones)
La Clase Tupla¶
La clase tupla tiene en algunos sentidos un comportamiento similar a la lista.
- Podemos acceder a elementos sueltos
- Podemos hacer slicing y obtener subsecuencias
Pero, la gran diferencia es que la tupla es inmutable, lo que significa que si queremos añadir o quitar elementos, tenemos que crear una nueva tupla. Si queremos modificar elementos, que ya existen en la tupla, no podemos hacerlo a no ser que algunos se sus datos sean mutables.
Métodos de las tuplas¶
count(obj)
tupla : tuple = (1, 2, 3, 4, 1, 1, 1, 2)
tupla.count(1)
4
index(obj)
tupla : tuple = (1, 2, 3, 4, 1, 1, 1, 2)
tupla.index(1)
0
tupla : tuple = (1, 2, 3, 4, 1, 1, 1, 2)
try:
print(tupla.index(-1))
except Exception as exception:
print('Si busco algo que no está en la lista:', f'"{exception}"')
Si busco algo que no está en la lista: "tuple.index(x): x not in tuple"
Modificar elementos mutables pero no inmutables¶
tupla = ([], 1, 2)
try:
tupla[1] = 2
print(tupla)
except Exception as exception:
print(exception)
tupla[0].append(1)
tupla # Cambió la tupla
'tuple' object does not support item assignment
([1], 1, 2)
Lo que la tupla indica es que los elementos de la misma no pueden cambiar de id (digamos posición de memoria).
En otras palabras, la tupla ([], 1, 2) tiene 3 elementos. Cada elemento tiene un id. La tupla garantiza que los ids, no cambian.
O lo que es lo mismo, el dato no puede ser modificado por otro porque 2 datos distintos tienen ids distintos. Se guardan en diferentes zonas de memoria.
Adémas, el error 'tuple' object does not support item assignment se debe a que la clase Tupla no tiene el método setitem que permite hacer lista[key] = value.
La Clase Set¶
La clase Set es una representación directa del concepto conjunto pertenience a las matemáticas.
Un conjunto tiene como caracterítica principal que ningún elemento del conjunto se repite.
Además, en python la clase conjunto tiene la propiedad de estar ordenado. Al menos, si los elementos tienen en su código especificada alguna relación de orden.
Nota: Los conjuntos solo sirven para contener datos, recorrerlos y garantizar que los datos no se repiten.
Pero los elementos no están indexados por lo que no podemos acceder usando la sintaxis set[key].
Métodos de los conjuntos¶
add(obj)
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.add(1)
conjunto.add(23)
conjunto
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 23}
clear()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.clear()
conjunto
set()
copy()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto_1 : set = conjunto
print('ID conjunto_1:', id(conjunto_1), 'ID conjunto:', id(conjunto), '¿Idénticos?', id(conjunto_1) == id(conjunto))
ID conjunto_1: 2365958118912 ID conjunto: 2365958118912 ¿Idénticos? True
conjunto_1 : set = conjunto.copy()
print('ID conjunto_1:', id(conjunto_1), 'ID conjunto:', id(conjunto), '¿Idénticos?', id(conjunto_1) == id(conjunto))
ID conjunto_1: 2365958118688 ID conjunto: 2365958118912 ¿Idénticos? False
difference_update()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.difference_update({1, 2, 4})
conjunto
{3, 5, 6, 7, 8, 9, 10, 11}
difference()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.difference({1, 2, 4})
{3, 5, 6, 7, 8, 9, 10, 11}
intersection_update()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.intersection_update({1, 2, 4})
conjunto
{1, 2, 4}
intersection()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.intersection({1, 2, 4})
{1, 2, 4}
symmetric_difference_update()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.symmetric_difference_update({1, 2, 4})
conjunto
{3, 5, 6, 7, 8, 9, 10, 11}
symmetric_difference()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.symmetric_difference({1, 2, 4})
{3, 5, 6, 7, 8, 9, 10, 11}
update()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.update({1, 2, 4, 34, -90})
conjunto
{-90, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 34}
union()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.union({1, 2, 4, 34, -90})
{-90, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 34}
isdisjoint()
conjunto_1 : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto_2 : set = {-9, -10, -11, -12, -13, -14, -15}
conjunto_3 : set = {0, 1, 3}
print(f'{conjunto_1=}, {conjunto_2=}')
print(f'¿isdisjoint? {conjunto_1.isdisjoint(conjunto_2)} || Intersection {conjunto_1.intersection(conjunto_2)}')
conjunto_1={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, conjunto_2={-15, -14, -13, -12, -11, -10, -9}
¿isdisjoint? True || Intersection set()
print(f'{conjunto_1=}, {conjunto_3=}')
print(f'¿isdisjoint? {conjunto_1.isdisjoint(conjunto_3)} || Intersection {conjunto_1.intersection(conjunto_3)}')
conjunto_1={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, conjunto_3={0, 1, 3}
¿isdisjoint? False || Intersection {1, 3}
issubset()
conjunto_1 : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto_2 : set = {1, 3, 2, 5}
print(f'{conjunto_1=}, {conjunto_2=}')
print(f'¿conjunto_1 issubset conjunto_2? {conjunto_1.issubset(conjunto_2)}')
conjunto_1={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, conjunto_2={1, 2, 3, 5}
¿conjunto_1 issubset conjunto_2? False
print(f'{conjunto_1=}, {conjunto_2=}')
print(f'¿conjunto_2 issubset conjunto_1? {conjunto_2.issubset(conjunto_1)}')
conjunto_1={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, conjunto_2={1, 2, 3, 5}
¿conjunto_2 issubset conjunto_1? True
issuperset()
conjunto_1 : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto_2 : set = {1, 3, 2, 5}
print(f'{conjunto_1=}, {conjunto_2=}')
print(f'¿conjunto_1 issuperset conjunto_2? {conjunto_1.issuperset(conjunto_2)}')
conjunto_1={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, conjunto_2={1, 2, 3, 5}
¿conjunto_1 issuperset conjunto_2? True
print(f'{conjunto_1=}, {conjunto_2=}')
print(f'¿conjunto_2 issuperset conjunto_1? {conjunto_2.issuperset(conjunto_1)}')
conjunto_1={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, conjunto_2={1, 2, 3, 5}
¿conjunto_2 issuperset conjunto_1? False
discard()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
conjunto.discard(1)
conjunto.discard(0)
conjunto
{2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
remove()
conjunto : set = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
print('PRE remove():', conjunto)
try:
conjunto.remove(1)
conjunto.remove(0)
except Exception as exception:
print('remove(obj) y discard(obj) son lo mismo, pero remove falla si no existe el elemento a eliminar')
PRE remove(): {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}
remove(obj) y discard(obj) son lo mismo, pero remove falla si no existe el elemento a eliminar
pop()
conjunto : set = {1, 2}
try:
conjunto.pop()
conjunto.pop()
conjunto.pop()
except Exception as exception:
print('Si hacemos pop() sobre un conjunto vacio falla')
Si hacemos pop() sobre un conjunto vacio falla
Ejemplos de uso¶
Eliminar elementos repetidos de una lista
lista : list = [1, 2, 3, 4, 1, 1, 3, 5, 4, 4]
lista = list(set(lista))
lista
[1, 2, 3, 4, 5]
Errores típicos¶
No se puede acceder a un elemento según un índice
conjunto : set = {1, 2, 3, 4, 5, 6}
try:
conjunto[0]
except Exception as exception:
print(exception)
print('La clase Set es accesible por índice?', '__getitem__' in dir(set))
print('La clase List es accesible por índice?', '__getitem__' in dir(list))
print('La clase Tuple es accesible por índice?', '__getitem__' in dir(tuple))
print('La clase Str es accesible por índice?', '__getitem__' in dir(str))
print('La clase Dict es accesible por índice?', '__getitem__' in dir(dict))
'set' object is not subscriptable La clase Set es accesible por índice? False La clase List es accesible por índice? True La clase Tuple es accesible por índice? True La clase Str es accesible por índice? True La clase Dict es accesible por índice? True
Ejercicios¶
- Dada una lista, obtén los elementos únicos
- Dados dos sets cualquiera de números A y B, obtén aquellos elementos que están en A pero no en B y los elementos que están en B pero no es A
La clase Dict¶
La clase Dict podríamos entenderla como una lista cuyos índices no tienen qué ser únicamente números indicando el orden de inserción de una dato en la lista.
En un diccionario identificamos 2 elementos. Clave y Valor. La clave es el índice con el que accedemos a un valor que dicha clave tiene asociado.
Las claves se almacenan en conjuntos, lo que hace que no puedan repetirse las claves. No puede haber 2 claves iguales en un diccionario. Las claves deben ser inmutables: strings, tuplas, enteros, decimales. Nunca listas. Al menos de forma explícita.
Cada clave puede tener cualquier valor asociado. Es decir, en un mismo diccionario, una clave puede tener asociado un numero y otra clave tener asociada una palabra.
Métodos de los conjuntos¶
clear()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
diccionario.clear()
diccionario
{}
copy()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
diccionario_1 : dict= diccionario
print('ID diccionario_1:', id(diccionario_1), 'ID diccionario:', id(diccionario), '¿Idénticos?', id(diccionario_1) == id(diccionario))
ID diccionario_1: 2365960996672 ID diccionario: 2365960996672 ¿Idénticos? True
diccionario_1 : dict = diccionario.copy()
print('ID diccionario_1:', id(diccionario_1), 'ID diccionario:', id(diccionario), '¿Idénticos?', id(diccionario_1) == id(diccionario))
ID diccionario_1: 2365957433472 ID diccionario: 2365960996672 ¿Idénticos? False
fromkeys(iterable, value=None)
{}.fromkeys([1, 2, 3], 'a')
{1: 'a', 2: 'a', 3: 'a'}
get(key, default=None)
{}.get(1, 'a')
'a'
{1 : 'b', 2 : 'aaa'}.get(1, 'a')
'b'
keys()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
list(diccionario.keys())
['a', 'b', 'c', 'd']
values()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
list(diccionario.values())
[1, 2, 3, 4]
items()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
list(diccionario.items())
[('a', 1), ('b', 2), ('c', 3), ('d', 4)]
pop()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
try:
print(diccionario.pop('a'))
print(diccionario.pop(11))
except KeyError as exception:
print('ERROR:', exception)
diccionario
1 ERROR: 11
{'b': 2, 'c': 3, 'd': 4}
popitem()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
try:
print(diccionario.popitem())
print(diccionario.popitem())
print(diccionario.popitem())
print(diccionario.popitem())
print(diccionario.popitem())
except Exception as exception:
print('ERROR:', exception)
('d', 4)
('c', 3)
('b', 2)
('a', 1)
ERROR: 'popitem(): dictionary is empty'
update()
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
diccionario.update({'a' : 2, 'b' : (1, 2), 'e' : -1})
diccionario
{'a': 2, 'b': (1, 2), 'c': 3, 'd': 4, 'e': -1}
setdefault(key, default=None)
diccionario : dict = {'a' : 1, 'b' : 2, 'c' : 3, 'd' : 4}
try:
print(diccionario.setdefault(1))
print(diccionario.setdefault('a', -1))
print(diccionario.setdefault(22, -1222))
except Exception as exception:
print('ERROR:', exception)
None 1 -1222
Ejemplos de Uso¶
Acceder a un elemento según clave¶
diccionario : set = {1 : (1, 2), 2 : (), 3 : (), 4 : (2, 3, 4)}
diccionario[1]
(1, 2)
Comprobad que existe la clave o usar .get() para que no salte una Excepción
diccionario : set = {1 : (1, 2), 2 : (), 3 : (), 4 : (2, 3, 4)}
diccionario[-1]
--------------------------------------------------------------------------- KeyError Traceback (most recent call last) c:\Users\sergi\Documents\repos\python_course\tutoriales\05_clases_y_objectos.ipynb Cell 217 line 2 <a href='vscode-notebook-cell:/c%3A/Users/sergi/Documents/repos/python_course/tutoriales/05_clases_y_objectos.ipynb#Y444sZmlsZQ%3D%3D?line=0'>1</a> diccionario : set = {1 : (1, 2), 2 : (), 3 : (), 4 : (2, 3, 4)} ----> <a href='vscode-notebook-cell:/c%3A/Users/sergi/Documents/repos/python_course/tutoriales/05_clases_y_objectos.ipynb#Y444sZmlsZQ%3D%3D?line=1'>2</a> diccionario[-1] KeyError: -1
Borrar de un diccionario claves que tienen tuplas vacías¶
Versión mala
diccionario : set = {1 : (1, 2), 2 : (), 3 : (), 4 : (2, 3, 4)}
for key, value in diccionario.items():
if not value:
diccionario.pop(key)
diccionario
--------------------------------------------------------------------------- RuntimeError Traceback (most recent call last) c:\Users\sergi\Documents\repos\python_course\tutoriales\05_clases_y_objectos.ipynb Cell 220 line 3 <a href='vscode-notebook-cell:/c%3A/Users/sergi/Documents/repos/python_course/tutoriales/05_clases_y_objectos.ipynb#Y435sZmlsZQ%3D%3D?line=0'>1</a> diccionario : set = {1 : (1, 2), 2 : (), 3 : (), 4 : (2, 3, 4)} ----> <a href='vscode-notebook-cell:/c%3A/Users/sergi/Documents/repos/python_course/tutoriales/05_clases_y_objectos.ipynb#Y435sZmlsZQ%3D%3D?line=2'>3</a> for key, value in diccionario.items(): <a href='vscode-notebook-cell:/c%3A/Users/sergi/Documents/repos/python_course/tutoriales/05_clases_y_objectos.ipynb#Y435sZmlsZQ%3D%3D?line=3'>4</a> if not value: <a href='vscode-notebook-cell:/c%3A/Users/sergi/Documents/repos/python_course/tutoriales/05_clases_y_objectos.ipynb#Y435sZmlsZQ%3D%3D?line=4'>5</a> diccionario.pop(key) RuntimeError: dictionary changed size during iteration
Versión buena
diccionario : set = {1 : (1, 2), 2 : (), 3 : (), 4 : (2, 3, 4)}
for key, value in diccionario.copy().items():
if not value:
diccionario.pop(key)
diccionario
{1: (1, 2), 4: (2, 3, 4)}
Ejercicios¶
- Crea un diccionario de animales a tu gusto con la información que estimes necesaria para definir a un animal
- Añade animales al diccionario anterior
- Eliminar animales del diccionario anterior
- Crea un diccionario nuevo y trata de combinar los dos diccionarios