¿Qué es la programación orientada a objetos en Python?
Python es un lenguaje que soporta tanto la forma tradicional de programar, como es la programación funcional en el que el código se va ejecutando de forma secuencial, como también la programación orientada a objetos. Esta programación orientada a objetos cumple con los cuatro paradigmas de este tipo de programación como son:
- Encapsulamiento.
- Herencia.
- Polimorfismo.
- Abstracción.
Vamos a verlo de forma global y practicaremos estas características en esta píldora.
Básicamente una clase es una plantilla de la cual vamos a poder crear instancias u objetos. La clase representa un grupo de características comunes de un grupo de objetos. Mientras que la instancia es un ejemplar que pertenece a una determinada clase. Como este tema es un poco abstracto vamos a verlo con un ejemplo.
Imaginemos que tenemos una librería. En esa librería vendemos libros de muchas clases pero una de ellas, son libros de poesía. Traspasando esto al ámbito informático, los libros de poesía serían de una clase. Esos libros tienen una serie de características comunes como son el título, el número de poemas, el autor y el precio. Cada uno de los diferentes libros de poesía que tenemos serían objetos, libros distintos, que son instancias de la clase Poesía. En informática cuando definimos un objeto decimos que estamos instanciando ese objeto de una clase.
Vamos a crear un clase llamada Poesía para un supuesto software que lleve las ventas de una librería.
class Poesia:
def __init__(self, titulo, poemas, autor, precio):
self.titulo = titulo
self.poemas = poemas
self.autor = autor
self.precio = precio
La clase poesía se inicializa usando el método especial __init__ al que tenemos que pasar una serie de parámetros que aunque podemos llamarlos de la manera que queramos, lo normal es que coincidan con los atributos de la clase. En este caso los atributos de la clase Poesía son titulo, el número de poemas, autor y precio.
Es una costumbre en Python que las clases definidas por el usuario estén en formato "Camello" es decir con la primera letra en Mayúscula y si hubiera otra palabra más, en mayúscula la primera letra también. Si nuestra clase se llamará libros de poesía, en formato "Camello" su nombre de clase sería LibrosPoesia. Además el archivo donde se guarda, en el disco duro, esta clase suele tener el mismo nombre que la clase.
Con esta clase que tenemos, se pueden crear o mejor dicho instanciar tantos objetos (representaciones de libros en nuestro caso) como queramos. Por ejemplo vamos a instanciar tres objetos.
libro_1 = Poesia("cantigas desconocidas", 323, "Fray Perico", 10)
libro_2 = Poesia("yo y mi llama", 125, "Gloria Strong", 15)
libro_3 = Poesia("juramento perdido", 232, "Benito Vercimuelle", 8)
libro_1, libro_2 y libro_3 son objetos distintos. Tenemos tres instancias distintas de la clase poesía. El termino "self" que aparece en la construcción de la clase hace referencia a las instancias correspondientes (objetos), es decir hace referencia al objeto que se va a crear.
print(libro_1)
print(libro_2)
print(libro_3)
Salida:
<__main__.Poesia object at 0x7f0d655af4c0>
<__main__.Poesia object at 0x7f0d654591f0>
<__main__.Poesia object at 0x7f0d65459430>
Al mandar imprimir los objetos vemos que Python nos indica a que clase pertenecen y su posición en memoria. Sin embargo estaría mejor ver algo más de información sobre el objeto lo cual se puede lograr usando un método especial llamado __repr__. Un método especial en Python es aquel que empieza y termina por dos guiones bajos y que se llama bajo determinadas circunstancias. En algunos sitios aparecen este tipo de métodos como de tipo "dunder" del inglés "double underscore".
Resumiendo:
class Poesia:
def __init__(self, titulo, poemas, autor, precio):
self.titulo = titulo
self.poemas = poemas
self.autor = autor
self.precio = precio
def __repr__(self):
return f"Libro de Poesia: {self.titulo} by {self.autor},\
{self.precio} €"
# Instanciando o creando los objetos.
libro_1 = Poesia("cantigas desconocidas", 323, "Fray Perico", 10)
libro_2 = Poesia("yo y mi llama", 125, "Gloria Strong", 15)
libro_3 = Poesia("juramento perdido", 232, "Benito Vercimue", 8)
#imprimiendo los objetos
print(libro_1)
print(libro_2)
print(libro_3)
Salida:
Libro de Poesia: cantigas desconocidas by Fray Perico,10 €
Libro de Poesia: yo y mi llama by Gloria Strong,15 €
Libro de Poesia: juramento perdido by Benito Vercimue,8 €
Y así como las clases tienen atributos, también pueden tener métodos. Esos métodos son acciones o funciones que modifican los objetos de las clases. Si por ejemplo queremos aplicar un descuento a un determinado libro, podemos crear un método llamado descontar que aplique el descuento al precio de ese libro.
class Poesia:
def __init__(self, titulo, poemas, autor, precio):
self.titulo = titulo
self.poemas = poemas
self.autor = autor
self.precio = precio
def descontar(self, porcentaje):
self.precio = self.precio * (1 - porcentaje/100)
def __repr__(self):
return f"Libro de Poesia: {self.titulo} by {self.autor},\
{self.precio} €"
# Instanciando o creando los objetos.
libro_1 = Poesia("cantigas desconocidas", 323, "Fray Perico", 10)
# Aplicando el descuento.
libro_1.descontar(5)
print(libro_1.precio)
Salida
9.5
Encapsulación.
La encapsulación es el proceso de hacer que ciertos atributos sean inaccesibles fuera del código de la propia clase y solo pueda accederse a ellos a través de ciertos métodos.
Estos atributos se denominan privados y comienzan con dos guiones bajos. (También puedes en los programas de Python ciertos atributos que comienzan con un solo guion que aunque no son estrictamente privados el programador considera que no deben se modificados fuera de la clase a la que pertenecen).
Cuando se ponen dos guiones bajos delante del nombre de métodos, suelen ser funciones auxiliares de alguna de los métodos principales de la clase, que queremos que solo se puedan usar dentro de la misma.
En nuestra clase Poesia vamos a convertir el atributo precio en un atributo privado (__precio) y vamos a intentar imprimirlo desde fuera de la clase para ver que tipo de error nos da.
class Poesia:
def __init__(self, titulo, poemas, autor, precio):
self.titulo = titulo
self.poemas = poemas
self.autor = autor
self.__precio = precio
def descontar(self, porcentaje):
self.__precio = self.__precio * (1 - porcentaje/100)
def __repr__(self):
return f"Libro de Poesia: {self.titulo} by {self.autor},\
{self.__precio} €"
# Instanciando o creando los objetos.
libro_1 = Poesia("cantigas desconocidas", 323, "Fray Perico", 10)
# Aplicando el descuento.
libro_1.descontar(5)
print(libro_1.__precio)
Salida:
Traceback (most recent call last):
File "main.py", line 19, in <module>
print(libro_1.__precio)
AttributeError: 'Poesia' object has no attribute '__precio'
Existen varias formas de asignar valores a atributos privados. Uno de ellos es a través de métodos llamados setter y getter. Como en el ejemplo convertí la variable precio en privada, vamos a crear un método setter para establecer el precio y un método getter para leerlo.
class Poesia:
def __init__(self, titulo, poemas, autor, precio):
self.titulo = titulo
self.poemas = poemas
self.autor = autor
self.__precio = precio
def get_precio(self):
''' Método getter para leer el precio.'''
return self.__precio
def set_precio(self, precio):
''' Método setter para establecer el precio.'''
self.__precio = precio
def descontar(self, porcentaje):
self.__precio = self.__precio * (1 - porcentaje/100)
def __repr__(self):
return f"Libro de Poesia: {self.titulo} by {self.autor},\
{self.__precio} €"
# Instanciando o creando los objetos.
libro_1 = Poesia("cantigas desconocidas", 323, "Fray Perico", 10)
# Cambiamos el precio a 20
libro_1.set_precio(20)
# Aplicando el descuento.
libro_1.descontar(5)
print(libro_1.get_precio())
19.0
class Poesia:
def __init__(self, titulo, poemas, autor, precio):
self.titulo = titulo
self.poemas = poemas
self.autor = autor
self.__precio = precio
@property
def precio(self):
return self.__precio
@precio.setter
def precio(self, precio):
if precio != "":
print("modificando el precio.")
self.__precio = precio
else:
print("El precio esta vacio.")
def descontar(self, porcentaje):
self.__precio = self.__precio * (1 - porcentaje/100)
def __repr__(self):
return f"Libro de Poesia: {self.titulo} by {self.autor},\
{self.__precio} €"
# Instanciando o creando los objetos.
libro_1 = Poesia("cantigas desconocidas", 323, "Fray Perico", 10)
# Cambiamos el precio a 20
libro_1.precio = 20
# Aplicando el descuento.
libro_1.descontar(5)
print(libro_1.precio)
Salida:
modificando el precio.
19.0
Herencia.
class Libros:
def __init__(self, titulo, autor, precio):
self.titulo = titulo
self.autor = autor
self.__precio = precio
@property
def precio(self):
return self.__precio
@precio.setter
def precio(self, precio):
if precio != "":
print("modificando el precio.")
self.__precio = precio
else:
print("El precio esta vacio.")
def descontar(self, porcentaje):
self.__precio = self.__precio * (1 - porcentaje/100)
print(f'Aplicando un descuento del {porcentaje} %')
class Poesia(Libros):
def __init__(self, titulo, autor, precio, poemas):
super().__init__(titulo, autor, precio)
# También se podrían pasar los parámetros a la clase padre en vez
# de usar el comando super() como:
# Libros.__init__(self, titulo, autor, precio)
self.poemas = poemas
def __repr__(self):
return f"Libro de Poesia: {self.titulo} by {self.autor},\
{self.precio} €"
poem_1 = Poesia('Odas a un juguete', 'Perico', 33, 120)
poem_1.precio = 30
poem_1.descontar(20)
print(poem_1)
modificando el precio.
Aplicando un descuento del 20 %
Libro de Poesia: Odas a un juguete by Perico, 24.0 €
class Teatro(Libros):
def __init__(self, titulo, autor, precio, genero):
super().__init__(titulo, autor, precio)
self.genero = genero
def __repr__(self):
return f"Libro de Teatro: {self.titulo} by {self.autor},\
{self.precio} €"
class Novela(Libros):
def __init__(self, titulo, autor, precio, paginas):
super().__init__(titulo, autor, precio)
self.paginas = paginas
def __repr__(self):
return f"Libro de Novelas: {self.titulo} by {self.autor},\
{self.precio} €"
teat_1 = Teatro('La celestina', 'Fernando de Rojas', 10, 'Clásico')
nove_1 = Novela('Nunca', 'Ken Follet', 30, 890)
print(teat_1)
print(nove_1)
Libro de Teatro: La celestina by Fernando de Rojas, 10 €
Libro de Novelas: Nunca by Ken Follet, 30 €
Polimorfismo.
class Libros: def __init__(self, titulo, autor, precio): self.titulo = titulo self.autor = autor self.__precio = precio @property def precio(self): return self.__precio @precio.setter def precio(self, precio): if precio != "": print("modificando el precio.") self.__precio = precio else: print("El precio esta vacio.") def descontar(self, porcentaje): self.__precio = self.__precio * (1 - porcentaje/100) print(f'Aplicando un descuento del {porcentaje} %') def __repr__(self): return f"Libro: {self.titulo} by {self.autor},\ {self.precio} €" class Poesia(Libros): def __init__(self, titulo, autor, precio, poemas): super().__init__(titulo, autor, precio) self.poemas = poemas class Teatro(Libros): def __init__(self, titulo, autor, precio, genero): super().__init__(titulo, autor, precio) self.genero = genero class Novela(Libros): def __init__(self, titulo, autor, precio, paginas): super().__init__(titulo, autor, precio) self.paginas = paginas def __repr__(self): return f'{self.titulo} escrito por {self.autor} y tiene {self.paginas} páginas.,\ Precio {self.precio}'
poem_1 = Poesia('Odas a un juguete', 'Perico', 33, 120) teat_1 = Teatro('La celestina', 'Fernando de Rojas', 10, 'Clásico') nove_1 = Novela('Nunca', 'Ken Follet', 30, 890) print(poem_1) print(teat_1) print(nove_1)
Libro: Odas a un juguete by Perico, 33 €
Libro: La celestina by Fernando de Rojas, 10 €
Nunca escrito por Ken Follet y tiene 890 páginas.,Precio 30
Abstracción.
#ABC = abstract base class from abc import ABC, abstractmethod class Libros(ABC): def __init__(self, titulo, autor, precio): self.titulo = titulo self.autor = autor self.__precio = precio @property def precio(self): return self.__precio @precio.setter def precio(self, precio): if precio != "": print("modificando el precio.") self.__precio = precio else: print("El precio esta vacio.") def descontar(self, porcentaje): self.__precio = self.__precio * (1 - porcentaje/100) print(f'Aplicando un descuento del {porcentaje} %') @abstractmethod def __repr__(self): # No podemos código ya que solo queremos obligar a que las clases que hereden # de está tengan un método __repr__ propio -> Abstracion. pass class Poesia(Libros): def __init__(self, titulo, autor, precio, poemas): super().__init__(titulo, autor, precio) self.poemas = poemas class Teatro(Libros): def __init__(self, titulo, autor, precio, genero): super().__init__(titulo, autor, precio) self.genero = genero class Novela(Libros): def __init__(self, titulo, autor, precio, paginas): super().__init__(titulo, autor, precio) self.paginas = paginas def __repr__(self): return f'{self.titulo} escrito por {self.autor} y tiene {self.paginas} páginas.,\ Precio {self.precio}'
libro = Libros('un libro','desconocido', 1)
Traceback (most recent call last):
File "main.py", line 51, in <module>
Libros('un libro','desconocido', 1)
TypeError: Can't instantiate abstract class Libros with abstract methods __repr__
poem_1 = Poesia('Odas a un juguete', 'Perico', 33, 120)
Traceback (most recent call last):
File "main.py", line 50, in <module>
poem_1 = Poesia('Odas a un juguete', 'Perico', 33, 120)
TypeError: Can't instantiate abstract class Poesia with abstract methods __repr__
#ABC = abstract base class
from abc import ABC, abstractmethod
class Libros(ABC):
def __init__(self, titulo, autor, precio):
self.titulo = titulo
self.autor = autor
self.__precio = precio
@property
def precio(self):
return self.__precio
@precio.setter
def precio(self, precio):
if precio != "":
print("modificando el precio.")
self.__precio = precio
else:
print("El precio esta vacio.")
def descontar(self, porcentaje):
self.__precio = self.__precio * (1 - porcentaje/100)
print(f'Aplicando un descuento del {porcentaje} %')
@abstractmethod
def __repr__(self):
pass
class Poesia(Libros):
def __init__(self, titulo, autor, precio, poemas):
super().__init__(titulo, autor, precio)
self.poemas = poemas
def __repr__(self):
return f"Libro: {self.titulo} by {self.autor},\
{self.precio} €"
class Teatro(Libros):
def __init__(self, titulo, autor, precio, genero):
super().__init__(titulo, autor, precio)
self.genero = genero
def __repr__(self):
return f"Libro: {self.titulo} by {self.autor},\
{self.precio} €"
class Novela(Libros):
def __init__(self, titulo, autor, precio, paginas):
super().__init__(titulo, autor, precio)
self.paginas = paginas
def __repr__(self):
return f'{self.titulo} escrito por {self.autor} y tiene {self.paginas} páginas.,\
Precio {self.precio}'
poem_1 = Poesia('Odas a un juguete', 'Perico', 33, 120)
teat_1 = Teatro('La celestina', 'Fernando de Rojas', 10, 'Clásico')
nove_1 = Novela('Nunca', 'Ken Follet', 30, 890)
print(poem_1)
print(teat_1)
print(nove_1)
Libro: Odas a un juguete by Perico, 33 €
Libro: La celestina by Fernando de Rojas, 10 €
Nunca escrito por Ken Follet y tiene 890 páginas.,Precio 30
class Pila:
class Pila: def __init__(self): print("Soy la clase Pila.") un_objeto_pila = Pila()
- el nombre del constructor es siempre __init__
- Tiene que tener al menos un párametro, el cual se usa para representar al objeto recien creado.
- Este parámetro obligatorio generalmente se llama self, aunque puede tener cualquier otro nombre. No obstante por convenció, deberías llamarla self ya que esto facilita el proceso de lectura y comprensión del código.
Soy la clase Pila.
class Pila: def __init__(self): self.lista_pila = [] un_objeto_pila = Pila() print(len(un_objeto_pila.lista_pila))
0
- Hemos usado el punto '. ' para acceder a las propiedades del objeto. Debes nombrar el objeto, poner un punto '.' después del él y especificar el nombre de la propiedad deseada. ¡No uses paréntesis! No quieres ejecutar un método si no acceder a una propiedad o atributo del objeto.
- Si estableces el valor de una propiedad por primera vez (como pasa en el constructor) lo estás creando. A partir de ese momento el objeto tiene esa propiedad y esta lista para usarse.
- Hemos accedido a la propiedad lista_pila desde fuera de la clase para verificar la longitud actual de la lista.
class Pila: def __init__(self): self.__lista_pila = [] un_objeto_pila = Pila() print(len(un_objeto_pila.lista_pila))
Traceback (most recent call last): File "main.py", line 6, in <module> print(len(un_objeto_pila.lista_pila)) AttributeError: 'Pila' object has no attribute 'lista_pila'
class Pila: def __init__(self): self.__lista_pila = [] def agregar(self, valor): self.__lista_pila.append(valor) def quitar(self): valor = self.__lista_pila[-1] del self.__lista_pila[-1] return valor un_objeto_pila = Pila() un_objeto_pila.agregar(3) un_objeto_pila.agregar(2) un_objeto_pila.agregar(1) print(un_objeto_pila.quitar()) print(un_objeto_pila.quitar()) print(un_objeto_pila.quitar())
1 2 3
class Pila: def __init__(self): self.__lista_pila = [] def agregar(self, valor): self.__lista_pila.append(valor) def quitar(self): valor = self.__lista_pila[-1] del self.__lista_pila[-1] return valor un_objeto_pila_1 = Pila() un_objeto_pila_2 = Pila() un_objeto_pila_1.agregar(3) un_objeto_pila_2.agregar(un_objeto_pila_1.quitar()) print(un_objeto_pila_2.quitar())
3
class SumaPila(Pila): pass
- Que el método agregar no solo añada un valor a la pila, sino además sume su valor a una nueva variable que llamaremos suma.
- Queremos que la función quitar no solo extraiga un valor de la pila, sino que reste el mismo de la variable suma.
class SumaPila(Pila): def __init__(self): Pila.__init__(self) self.__suma = 0
- Agrege el valor que le pasemos a la variable __sum.
- Agrege el valor a la pila.
def agregar(self, valor): self.__suma += valor Pila.agregar(self, valor)
def quitar(self): valor = Pila.quitar(self) self.__suma -= valor return valor
def get_suma(self): return self.__suma
class Pila: def __init__(self): self.__lista_pila = [] def agregar(self, valor): self.__lista_pila.append(valor) def quitar(self): valor = self.__lista_pila[-1] del self.__lista_pila[-1] return valor class SumaPila(Pila): def __init__(self): Pila.__init__(self) self.__suma = 0 def agregar(self, valor): self.__suma += valor Pila.agregar(self, valor) def quitar(self): valor = Pila.quitar(self) self.__suma -= valor return valor def get_suma(self): return self.__suma objeto_1 = SumaPila() for i in range(5): objeto_1.agregar(i) print(objeto_1.get_suma()) for i in range(5): print(objeto_1.quitar())
10
4 3 2 1
PROPIEDADES.
class Ejemplo: def __init__(self, valor = 1): self.primero = valor def set_segundo(self, valor): self.segundo = valor ejemplo_objeto_1 = Ejemplo() ejemplo_objeto_2 = Ejemplo(2) ejemplo_objeto_2.set_segundo(3) ejemplo_objeto_3 = Ejemplo(4) ejemplo_objeto_3.tercero = 5 # A este objeto sobre la marcha le hemos añadido una nueva propiedad (tercero) # fuera del código de la clase. print("El primer objeto tiene las propiedades ", ejemplo_objeto_1.__dict__) print("El segundo objeto tiene las propiedades", ejemplo_objeto_2.__dict__) print("El tercer objeto tiene las propiedades ", ejemplo_objeto_3.__dict__)
El primer objeto tiene las propiedades {'primero': 1} El segundo objeto tiene las propiedades {'primero': 2, 'segundo': 3} El tercer objeto tiene las propiedades {'primero': 4, 'tercero': 5}
class Ejemplo: def __init__(self, valor = 1): self.__primero = valor def set_segundo(self, valor = 2): self.__segundo = valor ejemplo_objeto_1 = Ejemplo() ejemplo_objeto_2 = Ejemplo(2) ejemplo_objeto_2.set_segundo(3) ejemplo_objeto_3 = Ejemplo(4) ejemplo_objeto_3.__tercero = 5 print("El primer objeto tiene las propiedades ", ejemplo_objeto_1.__dict__) print("El segundo objeto tiene las propiedades", ejemplo_objeto_2.__dict__) print("El tercer objeto tiene las propiedades ", ejemplo_objeto_3.__dict__)
El primer objeto tiene las propiedades {'_Ejemplo__primero': 1}
El segundo objeto tiene las propiedades {'_Ejemplo__primero': 2, '_Ejemplo__segundo': 3}
El tercer objeto tiene las propiedades {'_Ejemplo__primero': 4, '__tercero': 5}
print(ejemplo_objeto_1._Ejemplo__primero)
1
class Ejemplo: contador = 0 def __init__(self, valor = 1): self.__primero = valor Ejemplo.contador += 1 objeto_1 = Ejemplo() objeto_2 = Ejemplo(2) objeto_3 = Ejemplo(4) print(objeto_1.__dict__, objeto_1.contador) print(objeto_2.__dict__, objeto_2.contador) print(objeto_3.__dict__, objeto_3.contador)
{'_Ejemplo__primero': 1} 3 {'_Ejemplo__primero': 2} 3 {'_Ejemplo__primero': 4} 3
- Las variables de clase no se muestran en el diccionario de un objeto. Lo cual es lógico ya que no son parte del mismo.
- Una variable de clase siempre presenta el mismo valor en todas las instancias de la clase (objetos)
class Ejemplo: __contador = 0 def __init__(self, valor = 1): self.__primero = valor Ejemplo.__contador += 1 objeto_1 = Ejemplo() objeto_2 = Ejemplo(2) objeto_3 = Ejemplo(4) print(objeto_1.__dict__, objeto_1._Ejemplo__contador) print(objeto_2.__dict__, objeto_2._Ejemplo__contador) print(objeto_3.__dict__, objeto_3._Ejemplo__contador)
{'_Ejemplo__primero': 1} 3 {'_Ejemplo__primero': 2} 3 {'_Ejemplo__primero': 4} 3
class Ejemplo:
variable = 1
def __init__(self, val):
Ejemplo.variable = val
print(Ejemplo.__dict__)
objeto_1 = Ejemplo(5)
print(Ejemplo.__dict__)
print(objeto_1.__dict__)
{'__module__': '__main__', 'variable': 1, '__init__': <function Ejemplo.__init__ at 0x7f8bb2157dc0>, '__dict__': <attribute '__dict__' of 'Ejemplo' objects>, '__weakref__': <attribute '__weakref__' of 'Ejemplo' objects>, '__doc__': None} {'__module__': '__main__', 'variable': 5, '__init__': <function Ejemplo.__init__ at 0x7f8bb2157dc0>, '__dict__': <attribute '__dict__' of 'Ejemplo' objects>, '__weakref__': <attribute '__weakref__' of 'Ejemplo' objects>, '__doc__': None} {}
class Ejemplo: def __init__(self, valor): if valor % 2 != 0: self.a = 1 else: self.b = 1 objeto_1 = Ejemplo(1) print(objeto_1.a) print(objeto_1.b)
1 Traceback (most recent call last): File "main.py", line 10, in <module> print(objeto_1.b) AttributeError: 'Ejemplo' object has no attribute 'b'
class Ejemplo: def __init__(self, valor): if valor % 2 != 0: self.a = 1 else: self.b = 1 objeto_1 = Ejemplo(1) print(objeto_1.a) try: print(objeto_1.b) except AttributeError: pass
1
- La clase o el objeto que se quiere verificar.
- El nombre de la propiedad cuya existencia se quiere verificar. Hay que pasar el nombre en forma de cadena.
class Ejemplo: def __init__(self, valor): if valor % 2 != 0: self.a = 1 else: self.b = 1 objeto_1 = Ejemplo(1) print(objeto_1.a) if hasattr(objeto_1, 'b'): print(objeto_1.b)
1
class Ejemplo:
atributo = 1
if hasattr(Ejemplo, 'atributo'):
print(Ejemplo.atributo)
1
class Clase:
def metodo(self):
print("método")
obj = Clase()
obj.metodo()
método
class Clase:
def metodo(self, numero):
print("método", numero)
obj = Clase()
obj.metodo(1)
obj.metodo(2)
obj.metodo(3)
método 1
método 2
método 3
class Clase:
variable = 2
def metodo(self):
print(self.variable, self.valor)
obj = Clase()
obj.valor = 3
obj.metodo()
2 3
class Clase:
def otro(self):
print("otro")
def metodo(self):
print("método")
self.otro()
obj = Clase()
obj.metodo()
método
otro
- Está obligado a tener el parámetro self.
- Pudiera o no tener más parámetros que solo self.
- Se puede utilizar para configurar el objeto, es decir inicializa su estado interno, crea las variables de instancia, crea instancias de cualquier otro objeto si fuera necesario.
- no puede retornar un valor.
- no se puede invocar directamente desde el objeto o desde dentro de la clase (si que puedes invocar un constructor de una superclase como veremos más adelante)
class Clase:
def __init__(self, valor = None):
self.var = valor
objeto_1 = Clase('objeto')
objeto_2 = Clase()
print(objeto_1.var)
print(objeto_2.var)
objeto
None
class Clase:
def visible(self):
print("visible")
def __oculto(self):
print("oculto")
objeto_1 = Clase()
print(objeto_1.visible())
try:
objeto_1.__oculto()
except:
print("fallo!")
objeto_1._Clase__oculto()
visible
fallo!
oculto
La vida interior de las clases y objetos.
class Clase:
variable = 1
def __init__(self):
self.var = 2
def metodo(self):
pass
def __oculto(self):
pass
objeto_1 = Clase()
print(objeto_1.__dict__)
print(Clase.__dict__)
{'var': 2}
{'__module__': '__main__', 'variable': 1,
'__init__': <function Clase.__init__ at 0x7f992083bdc0>,
'metodo': <function Clase.metodo at 0x7f992083be50>,
'_Clase__oculto': <function Clase.__oculto at 0x7f992083bee0>,
'__dict__': <attribute '__dict__' of 'Clase' objects>,
'__weakref__': <attribute '__weakref__' of 'Clase' objects>,
'__doc__': None}__dict__ es un diccionario.
class Clase: pass print(Clase.__name__) objeto = Clase() print(type(objeto)) print(f"La clase del objeto se llama {type(objeto).__name__}")
Clase <class '__main__.Clase'> La clase del objeto se llama Clase
class Clase: pass print(Clase.__module__) objeto = Clase() print(objeto.__module__)
__main__ __main__
class Padre1: pass class Padre2: pass class Hija(Padre1, Padre2): pass def printBases(cls): print("( ", end='') for x in cls.__bases__: print(x.__name__, end=' ') print(')') printBases(Padre1) printBases(Padre2) printBases(Hija)
( object )
( object )
( Padre1 Padre2 )
class Ejemplo: pass obj = Ejemplo() obj.a = 1 obj.b = 2 obj.i = 3 obj.irreal = 3.5 obj.integer = 4 obj.z = 5 def caracteristicas(obj): for name in obj.__dict__.keys(): if name.startswith('i'): # getattr obtiene el valor de la instancia del objeto val = getattr(obj, name) # Si el valor de la instancia es un numero entero if isinstance(val, int): # Incrementamos el valor en 1 setattr(obj, name, val + 1) print(obj.__dict__) caracteristicas(obj) print(obj.__dict__)
{'a': 1, 'b': 2, 'i': 3, 'irreal': 3.5, 'integer': 4, 'z': 5} {'a': 1, 'b': 2, 'i': 4, 'irreal': 3.5, 'integer': 5, 'z': 5}
HERENCIA DE CLASES ¿POR QUE Y COMO?
class Estrella: def __init__(self, estrella, galaxia): self.nombre = estrella self.galaxia = galaxia sol = Estrella('Sol', 'Via Lactea') print(sol)
class Estrella: def __init__(self, estrella, galaxia): self.nombre = estrella self.galaxia = galaxia def __str__(self): return self.nombre + ' en ' + self.galaxia sol = Estrella('Sol', 'Via Lactea') print(sol)
Sol en Via Lactea
class Vehiculo: pass class Vehiculo_terrestre(Vehiculo): pass class Coche(Vehiculo_terrestre): pass
issubclass()
issubclass(clase_1, clase_2)
La función devuelve True si clase_1 es una subclase de clase_2 y False en caso contrario.
Vamos a ver como funciona:
class Vehiculo: pass class Vehiculo_terrestre(Vehiculo): pass class Coche(Vehiculo_terrestre): pass for clase_1 in [Vehiculo, Vehiculo_terrestre, Coche]: for clase_2 in [Vehiculo, Vehiculo_terrestre, Coche]: print(clase_1.__name__+' - ' + clase_2.__name__,issubclass(clase_1, clase_2), end=' ') print()
Salida:
Vehiculo - Vehiculo True Vehiculo - Vehiculo_terrestre False Vehiculo - Coche False Vehiculo_terrestre - Vehiculo True Vehiculo_terrestre - Vehiculo_terrestre True Vehiculo_terrestre - Coche False Coche - Vehiculo True Coche - Vehiculo_terrestre True Coche - Coche True
isinstance()
isinstance(nombre_objeto, nombre_clase)
class Vehiculo: pass class Vehiculo_terrestre(Vehiculo): pass class Coche(Vehiculo_terrestre): pass mi_vehiculo = Vehiculo() mi_vehiculo_terrestre = Vehiculo_terrestre() mi_coche = Coche() for obj in [mi_vehiculo, mi_vehiculo_terrestre, mi_coche]: for clase in [Vehiculo, Vehiculo_terrestre, Coche]: print(isinstance(obj, clase), end="\t") print()
True False False True True False True True True
Hagamos que el resultado sea más legible
↓ es una instancia de → | Vehiculo | Vehiculo_terrestre | Coche |
---|---|---|---|
mi_vehiculo | True | False | False |
mi_vehiculo_terrestre | True | True | False |
mi_coche | True | True | True |
El operador is.
class Ejemplo: def __init__(self, valor): self.valor = valor objeto_1 = Ejemplo(0) objeto_2 = Ejemplo(2) objeto_3 = objeto_1 print(objeto_1 is objeto_2) print(objeto_2 is objeto_3) print(objeto_3 is objeto_1) print(objeto_1.valor, objeto_2.valor, objeto_3.valor) cadena_1 = "Maria tenía un " cadena_2 = "Maria tenía un corderito" cadena_1 += "corderito" print(cadena_1==cadena_2, cadena_1 is cadena_2)
False False True 0 2 0 True False
¿Cómo encuentra Python las propiedades y los métodos?
class Padre: def __init__(self, nombre): self.nombre = nombre def __str__(self): return "Mi nombre es " + self.nombre + "." class Hija(Padre): def __init__(self, nombre): Padre.__init__(self, nombre) obj = Hija('Alberto') print(obj)
- Existe una clase llamada Padre, que define su propio constructor para asignar la propiedad al objeto, llamada "nombre".
- Las clase también define el método __str__(), lo que permite que la clase pueda presentar su identidad en forma de texto.
- La clase se usa luego para crear una subclase llamada Hija. La clase Hija define su propio constructor, que invoca el de la superclase o clase Padre. Padre.__init__(self, nombre)
- Hemos instanciado un objeto de la clase Hija y lo hemos impreso.
class Padre: def __init__(self, nombre): self.nombre = nombre def __str__(self): return "Mi nombre es " + self.nombre + "." class Hija(Padre): def __init__(self, nombre): super().__init__(nombre) obj = Hija('Alberto') print(obj)
class Padre: supVar = 1 class Hija(Padre): subVar = 2 obj = Hija() print(obj.supVar) print(obj.subVar)
1 2
class Padre: def __init__(self): self.supVar = 1 class Hija(Padre): def __init__(self): super().__init__() self.subVar = 2 obj = Hija() print(obj.supVar) print(obj.subVar)
1 2
- Encontrarlas dentro del objeto mismo.
- Encontrarlas en todas las clases involucradas en la línea de herencia del objeto de abajo hacia arriba.
class Level1: variable_1 = 100 def __init__(self): self.var_1 = 101 def fun_1(self): return 102 class Level2(Level1): variable_2 = 200 def __init__(self): super().__init__() self.var_2 = 201 def fun_2(self): return 202 class Level3(Level2): variable_3 = 300 def __init__(self): super().__init__() self.var_3 = 301 def fun_3(self): return 302 obj = Level3() print(obj.variable_1, obj.var_1, obj.fun_1()) print(obj.variable_2, obj.var_2, obj.fun_2()) print(obj.variable_3, obj.var_3, obj.fun_3())
100 101 102 200 201 202 300 301 302
class SuperA:
var_a = 10
def fun_a(self):
return 11
class SuperB:
var_b = 20
def fun_b(self):
return 21
class Sub(SuperA, SuperB):
pass
obj = Sub()
print(obj.var_a, obj.fun_a())
print(obj.var_b, obj.fun_b())
10 11 20 21
class Level1: var = 100 def fun(self): return 101 class Level2(Level1): var = 200 def fun(self): return 201 class Level3(Level2): pass obj = Level3() print(obj.var, obj.fun())
200 201
class Izquierda: var = 100 def fun(self): return 101 class Derecha: var = 200 def fun(self): return 201 class Level3(Izquierda, Derecha): pass obj = Level3() print(obj.var, obj.fun())
100 101
- Primero busca dentro del objeto mismo.
- Luego en sus superclases de abajo hacia arriba.
- Finalmente si hay más de una clase en la ruta de herencia, de izquierda a derecha.