La puerta de entrada al paradigma orientado a objetos comienza con la creación de clases, que luego da paso a constructores para crear objetos. En Unity usando C#, todo archivo nuevo era una clase, tanto el nombre del archivo como el nombre de la clase debía coincidir, luego dentro del mismo archivo, podía ir mas clases, las que personalmente yo usaba seguido, eran las clases serializables, las clases con constructor las detestaba, pero tenía la elección de convertir una clase en un objeto o solo usarlo como una librería.
En Python usar la palabra "class", siempre hay una función o método __init__() que hace las veces de constructor, la única forma de usar muy entre comillas "una clase" como librería o módulo es sin usar la palabra "class".
"Clase" como librería.
Tengan en cuenta que en el título escribo clase entre comillas, aunque en C# esto es válido como clase, en Python a esto se le llama #módulo.
modulo.py
def sumar(a,b):
return a+b
def restar(a,b):
return a-b
PI = 3.141516
------------------------
main.py
import modulo
def main():
resultado = modulo.sumar(10,5)
print(resultado)
print(modulo.PI)
if __name__ == "__main__":
main()
Supongamos que en este trozo de código hay dos archivos *.py, modulo.py y main.py ambos se encuentran en la misma carpeta, el intérprete de Python mediante la palabra reservada "import" seguido del nombre del archivo (sin la extensión) busca automáticamente dentro de la misma carpeta, si no coincide el nombre, va y lo busca en la carpeta de librerías de Python el nombre del archivo, si no coincide, entonces arroja un "Import Error"
Clase como objeto.
class MiObjeto:
def __init__(self):
self.nombre = "Esfera"
self.color = "Rojo"
self.forma = "Redonda"
objeto = MiObjeto()
Al hacer uso de la palabra "class" con el nombre de la clase "MiObjeto" para este caso, pero puede ser cualquier nombre, lo importante es que por convenio la primera letra sea mayúscula, para que se distinga de las variables.
Se define un método (función) que siempre se llamará __init__(self): este es un método especial le dice al intérprete que este será la puerta de acceso a la clase, funcionando así como un constructor.
Nota: función es cuando está fuera de una clase, método es cuando está dentro de una clase, pero para efectos prácticos es lo mismo.
#Self es un parámetro especial que vendría siendo similar a la palabra reservada #this de otros lenguajes. Este parámetro le dice al intérprete que las variables a usar para el objeto, son propias del objeto y no de la clase.
Sé que es un poco raro la explicación, pero tratando de sintetizar mejor la idea, la clase en sí es una plantilla o molde que usaremos para crear los objetos. Estas clases tienen sus propios parámetros, pero cuando creamos un objeto partiendo de este molde, el objeto en sí tiene sus propios parámetros (#atributos) distintos a los del molde, para diferenciar los parámetros del objeto con respecto al molde se hace uso de la palabra self.
De self, parten unos parámetros de nombre, color, forma, el ejemplo que puse, realmente no se usa, pero lo hice para demostrar que se puede crear un objeto hardcodeado, luego de tener la clase creada solo basta con crear una nueva variable que llame a la clase MiObjeto(), para crear una instancia o copia del objeto.
class Clase
def funcion(self, parametro):
self.atributo = parametro
objeto = Clase(argumento)
Nota: parámetros son las variables declaradas dentro de las funciones/métodos, argumentos los valores que se introducen a la función y atributos, son las variables propias del objeto. Espero que con este croquis haber explicado un poco mejor tanta palabra fetiche.
class MiObjeto:
def __init__(self, nombre, color, forma):
self.nombre = nombre
self.color = color
self.forma = forma
def mostrar(self):
print(f'Nombre {self.nombre}, Color {self.color}, Forma {self.forma}')
esfera = MiObjeto("Esfera","Rojo","Esferico")
esfera.mostrar()
cubo = MiObjeto("Rubik","Varios","Cubico")
cubo.mostrar()
Desde que invocamos la clase para crear el objeto no necesitamos introducir ningún argumento para self, pero si introdujimos los argumentos para el nombre, el color y la forma.
Igualmente ya cuando tenemos creado el objeto, podemos llamar a sus métodos, en este caso el método mostrar, pero no necesitamos introducir el argumento para el parámetro self.
class MiObjeto:
def __init__(self,*args,**kwargs)
self.args = args
self.kwargs = kwargs
Si desconocemos la cantidad de parámetros que deseamos para nuestro constructor, podemos hacer uso de (* y **) para tuplas y diccionarios opcionales.
esfera = MiObjeto("Esfera","Rojo","Esferico")
print(esfera.color) # Rojo
esfera.color = "Verde"
print(esfera.color) # Verde
esfera.material = "Caucho"
print(esfera.material) # Caucho
cubo = ("Rubik","Varios","Cubico")
print(cubo.material) # Error
Es posible acceder a los atributos de un objeto y asignarles un nuevo valor en caliente, pero los que si es realmente distinto y extraño es poder crear nuevos atributos en caliente, por ejemplo: esfera.material = "Caucho", es un nuevo atributo que solo le pertenece a esfera y que no le pertenece a cubo a pesar de venir ambos del mismo molde.
Encapsulamiento.
Ya vimos como crear una clase, como usar un objeto, como acceder a sus atributos, pero muchas veces queremos controlar el acceso a los atributos, no queremos que todo sea accesible y publico todo el tiempo, sino que queremos algunos atributos de solo lectura, otros de solo escritura y otras de lectura y escritura.
Lo primero es convertir en privado los atributos para no acceder a ellos, para hacer eso simplemente anteponemos un #underscore (_) al atributo o variable.
class MiObjeto:
def __init__(self, nombre, color, forma)
self._nombre = nombre
self._color = color
self._forma = forma
objeto1 = MiObjeto("Esfera","Rojo","Esférico")
objeto1.nombre = "Tetraedro" # Error???
Se supone que no puedes acceder porque no conoces el nombre del atributo por ser "privado" entre comillas, pero en Python nada es privado, todo es público y esto supone un problema de seguridad, pero debes confiar en la ética de los programadores y en que la filosofía de Python en tratarte como un ser pensante, que tú no querrás nunca acceder a un atributo o variable privada.
objeto1._nombre = "Tetraedro" # Siendo privado aún es modificable.
Aquí simplemente accediste al nombre de un atributo que supuestamente era privado, si haces un #print, verás que el valor ha cambiado, quizás lo único que hará tu IDE es que no te permita autocompletar el nombre del atributo.
objeto1.nombre = "Tetraedro"
Aquí sin querer creaste de la nada un atributo llamado nombre.
class MiObjeto:
def __init__(self, nombre):
self.__nombre = nombre
objeto1.__nombre = "Tetraedro"
Estamos tratando de acceder al atributo doblemente privado con un #DobleUnderscore (__) este es otro estilo de notación poco común, pero en mi caso funcionó un poco mejor, pues al tratar de cambiar el valor del atributo, no me genera ningún error, a la hora de imprimir el valor del atributo, el cambio no se ve reflejado, pero eso no quiere decir que no se haya creado un atributo nuevo.
Ahora si, ¿cómo hacemos atributos de lectura y escritura?, para ello, creamos funciones dentro de la clase con #decoradores en él. El decorador @property como getter y el decorador @atributo.setter como setter.
class MiObjeto:
def __init__(self, nombre):
self.__nombre = nombre
@property
def nombre(self):
return self.__nombre
@nombre.setter
def nombre(self, nombre):
self.__nombre == nombre
objeto1.nombre = "Tetraedro"
print(objeto1.nombre) # Tetraedro
¿Quieres volver un atributo solo escritura?, eliminas todo el método con el decorador @property.
¿Quieres volver el atributo de solo lectura?, eliminas todo el método con el decorador @x.setter
Por ahora eso es todo, en el próximo post, trataré el tema de la herencia.
Comments