En el tutorial anterior aprendimos sobre las secuencias y colecciones desordenadas en Python, incluidas las operaciones, funciones y métodos aplicables a ellas. Sin embargo, puede haber estructuras de datos más complejas en una aplicación, conocidas como clases y objetos definidos por el usuario.
Cualquier aplicación utiliza datos (valores/objetos/clases/objetos definidos por el usuario) y comportamiento del código (cómo se manipulan los datos).
Si una aplicación está diseñada de manera que se centre en:
- Funcionalidad (comportamiento del código): se llama programación procedimental o funcional.
- Datos (valores/objetos/objetos definidos por el usuario): se denomina programación orientada a objetos
Aunque Python es un lenguaje de programación orientado a objetos, no obliga a que una aplicación se defina mediante este tipo de patrón de diseño. Esto significa que una aplicación se puede diseñar en una variedad de patrones, incluido el uso de procedimientos y control de control de vista de modelo (MVC). También es posible mezclar múltiples patrones de diseño en una sola aplicación.
Pero vale la pena explorar las capacidades orientadas a objetos de Python, ya que es útil para crear estructuras de datos definidas por el usuario y diseñar ciertas aplicaciones de manera eficiente.
Clases de Python
Las clases son similares a un modelo para objetos reales o estructuras de datos. Así, en cualquier aplicación, una clase puede representar un objeto real o una estructura de datos.
Las propiedades de la estructura de datos/objeto se definen como atributos de la clase. Los atributos son referencias que pueden contener objetos integrados o definidos por el usuario. Una clase también puede tener funciones que vinculan o manipulan atributos asociados a ella. Estas funciones se denominan métodos.
En Python, no hay diferencia entre atributos y métodos. Los métodos se tratan como atributos invocables.
Las clases de Python también son objetos, que pueden usarse como tipos para crear otros objetos. Los tipos de datos en Python también son objetos, pero se utilizan para definir el tipo de otros objetos. Cuando se crea un objeto de un tipo de clase particular, se le llama instancia u objeto de instancia de esa clase.
El objeto de instancia tiene todos los atributos (invocables y no invocables) definidos para su clase. Algunos atributos de un objeto heredado de su clase pueden tener valores de datos predeterminados. Estos atributos invocables y no invocables se denominan descriptores. Cualquier objeto puede tener atributos adicionales definidos.
Una clase puede ser una subclase de otra clase, donde la clase principal se conoce como superclase. Así como un objeto hereda los atributos de su clase, si su clase es una subclase, implícitamente heredará los atributos de su superclase. Una subclase también hereda implícitamente los atributos y métodos de su superclase.
La declaración de clase define clases y es una declaración compuesta con esta sintaxis:
clase nombre de clase (clases base)
declaraciones)
El nombre de clase puede ser cualquier identificador que sirva como referencia para la clase. La definición de clase puede tener clases base como argumentos delimitados por comas. Las clases base son superclases, donde esta clase es una subclase. Las clases base son opcionales y solo deben incluirse cuando la clase definida es una subclase de otra clase.
Este es un ejemplo ideal del uso de clases como objetos. Cuando las clases base se pasan como argumentos, Python las trata como objetos. Las declaraciones incluidas en la definición de una clase se denominan cuerpo de la clase.
Los atributos (invocables o no invocables) que se vinculan a una clase se denominan atributos de clase. Estos atributos se pueden definir dentro del cuerpo de la clase y fuera de la definición de la clase. Se accede a los atributos de clase mediante la sintaxis nombre_clase.nombre_atributo.
A continuación se muestra un ejemplo válido de cómo definir un atributo de clase dentro del cuerpo de la clase:
clase C1:
x = 25
imprimir (C1.x)
Ahora aquí hay un ejemplo válido de cómo definir un atributo de clase fuera de la definición de clase:
clase C1:
gastar
C1.x = 25
imprimir (C1.x)
Las funciones vinculadas a una clase se llaman métodos. Aunque los atributos de clase se definen en el cuerpo de la clase, cuando se utilizan definiciones de métodos, se debe hacer referencia a ellos en la sintaxis nombre_clase.nombre_atributo.
Este es un ejemplo válido de un método que accede a los atributos de la clase:
clase C1:
x = 25
definición:
C1.x +=1
imprimir (C1.x)
Cuando se llama a un atributo y el identificador se utiliza como referencia para ese atributo de clase, o un método comienza con dos guiones bajos, el nombre de la clase con un guión bajo inicial se antepone al nombre del atributo. Por lo tanto, si se llama a un identificador __getData, el compilador de Python cambiará implícitamente el identificador a _className__getData.
Estos identificadores se denominan nombres privados o variables de clase privadas. Esto hace que las referencias a atributos y métodos vinculados a la clase sean privadas y reduce cualquier posibilidad de colisión con otras referencias en el código.
Una clase también puede tener métodos especiales con dos guiones bajos iniciales y dos finales como parte de un identificador utilizado como referencia. Por ejemplo, __init__, __get__, __set__ y __delete__ son métodos especiales que puede proporcionar una clase.
Estos métodos implícitos tienen un propósito especial en cualquier clase y se denominan métodos dunder, métodos mágicos o métodos especiales.
Algunos atributos de clase pueden tener acceso administrado. Los valores de datos se pueden vincular, volver a vincular o desvincular a ellos solo mediante métodos dunder. De manera similar a cómo pueden vincularse a valores/objetos de datos estándar en el método __init__, pueden:
- Devuelve su valor sólo mediante el método __get__
- Modificar su valor únicamente mediante el método __set__
- Solo se puede eliminar mediante el método __delete__.
Para inicializar, obtener, establecer o eliminar estos atributos, los métodos __init__, __get__, __set__ y __delete__ deben definirse explícitamente en el cuerpo de la clase con las instrucciones apropiadas.
Estos atributos se denominan descriptores. Si un descriptor no tiene definido el método __set__, su valor no se puede cambiar. Estos descriptores se denominan descriptores no sustitutivos o sin datos. Si un descriptor tiene definido el método __set__, su valor se puede cambiar en el código llamando al método __set__ correspondiente.
Los descriptores con valor se pueden cambiar y se denominan descriptores de reemplazo. Estos descriptores no pueden vincularse, volverse a vincular ni desvincularse mediante instrucciones de asignación habituales.
A continuación se muestra un ejemplo válido de descriptores:
clase C1 (objeto):
def __init__(yo, valor):
valor propio = valor
def __set__(yo, *_):
gastar
def __get__(yo, *_):
devolver valor propio
clase C2 (objeto):
c = C1(25)
x = C2 #instancias objeto de clase C2
imprimir(xc) # Impresiones 25
xc = 52
print(xc) # Impresiones fijas 25
Instancias de clase
Al crear una instancia (objeto de instancia) de una clase, se puede asignar una referencia al objeto de clase como una función.
Este es un ejemplo válido de creación de una instancia:
X = C1 #C1 es una clase
Si se proporciona el método __init__ para una clase, puede haber una inicialización predeterminada de los atributos de ese método. Cuando se crea una instancia, se deben pasar todos los argumentos necesarios a la instancia para completar la inicialización.
Aquí hay un ejemplo:
clase C1 (objeto):
def __init__(self, valor1, valor2):
self.a = valor1
self.b = valor2
y = C1(25, 34)
print(ya) #Impresiones 25
imprimir(yb) #Impresiones 34
En este caso, __init__ funciona como una función constructora en otros lenguajes orientados a objetos. Una instancia también puede heredar el método __init__ de la superclase de su clase. Si no hay ningún método __init__ definido para la clase o su superclase en la instanciación, la clase debe llamarse sin argumentos.
Esto significa que no debería haber argumentos específicos de la instancia. Además, el método __init__ no puede devolver ningún valor que no sea Ninguno. Una instancia puede tener sus propios atributos que no han sido definidos en el cuerpo de la clase o para su clase.
También puede crear una instancia asignando una función a una referencia. En este caso, la función debe devolver un objeto de clase. Estas funciones se denominan funciones de fábrica.
Herencia
La herencia es una característica poderosa en la programación orientada a objetos. Como comentamos, cualquier definición de clase puede tener clases base que sean argumentos. Luego, esta clase se denomina subclase y la clase base se denomina superclase o clase principal.
Una subclase hereda implícitamente todos los atributos y métodos de su clase base. De manera similar, una instancia de subclase hereda todos los atributos y métodos de su clase, así como la clase base de su clase. Esta característica de los atributos y métodos heredados de un objeto padre se denomina herencia en el paradigma orientado a objetos.
Una clase puede tener varias clases base. Como tal, hereda los atributos y métodos de todas las clases base. Esto se llama herencia múltiple. También es posible que la clase base tenga otra clase base. En este caso, el objeto hereda los atributos y métodos de su clase base y la clase base de su clase base. Esto se llama herencia multinivel.
En el próximo tutorial cubriremos cómo diseñar interfaces gráficas en Python.