¿Ha pensado alguna vez en comprobar si los objetos que está utilizando se adhieren a una especificación particular? Es necesario verificar si un objeto implementa un método o propiedad determinada, especialmente al crear una biblioteca donde otros desarrolladores hacen uso de ella. Un desarrollador puede usar los métodos hasattr o isinstance para verificar si la entrada se ajusta a una identidad particular. Pero a veces es un inconveniente usar esos métodos para verificar una gran cantidad de propiedades y métodos diferentes.
Como solución a este inconveniente, Python introdujo un concepto llamado clase base abstracta (abc) . En esta sección, discutiremos la clase base abstracta y su importancia.
- Clase base abstracta
- Declarar una clase base abstracta
- ¿Por qué declarar una clase base abstracta?
- Propiedades abstractas
- Clases abstractas incorporadas
Clase base abstracta
El objetivo principal de la clase base abstracta es proporcionar una forma estandarizada de probar si un objeto se adhiere a una especificación dada. También puede evitar cualquier intento de instanciar una subclase que no invalide un método particular en la superclase. Y finalmente, usando una clase abstracta, una clase puede derivar la identidad de otra clase sin ninguna herencia de objetos.
Declarar una clase base abstracta
Python tiene un módulo llamado abc (clase base abstracta) que ofrece las herramientas necesarias para crear una clase base abstracta. En primer lugar, debe comprender la metaclase ABCMeta proporcionada por la clase base abstracta. La regla es que cada clase abstracta debe usar la metaclase ABCMeta.
La metaclase ABCMeta proporciona un método llamado método de registro que puede ser invocado por su instancia. Al usar este método de registro, cualquier clase base abstracta puede convertirse en un ancestro de cualquier clase concreta arbitraria. Entendamos este proceso considerando un ejemplo de una clase base abstracta que se registra como un ancestro de dict.
Python3
import abc class AbstractClass(metaclass=abc.ABCMeta): def abstractfunc(self): return None print(AbstractClass.register(dict))
Producción:
<class 'dict'>
Aquí, dict se identifica como una subclase de AbstractClass. Hagamos una comprobación.
Python3
import abc class AbstractClass(metaclass=abc.ABCMeta): def abstractfunc(self): return None AbstractClass.register(dict) print(issubclass(dict, AbstractClass))
Producción:
True
¿Por qué declarar una clase base abstracta?
Para comprender la necesidad de declarar una subclase virtual, debemos considerar el ejemplo de un objeto similar a una lista en el que no desea poner una restricción de solo considerar la lista o la tupla. Antes de eso, veamos cómo usar isinstance para verificar una lista o tupla de clase.
isinstance([], (list, tuple))
Esta verificación de instancia cumple el propósito si acepta solo una lista o una tupla. Pero aquí el caso es diferente, no existe tal restricción. Entonces, esta solución no es extensible para un desarrollador que usa su biblioteca para enviar algo más que una lista o una tupla. Aquí viene la importancia de la clase abstracta. Vamos a entender a través del siguiente código.
Python3
import abc class MySequence(metaclass=abc.ABCMeta): pass MySequence.register(list) MySequence.register(tuple) a = [1, 2, 3] b = ('x', 'y', 'z') print('List instance:', isinstance(a, MySequence)) print('Tuple instance:', isinstance(b, MySequence)) print('Object instance:', isinstance(object(), MySequence))
Producción:
List instance: True Tuple instance: True Object instance: False
Como puede ver, cuando realiza una comprobación de instancia, devuelve verdadero tanto para la lista como para la tupla; para los otros objetos, devuelve falso. Consideremos un escenario en el que un desarrollador espera un objeto de clase en sí mismo. En el caso anterior, la instancia devolverá falso. Pero se puede lograr creando una clase personalizada y registrándola con la clase base abstracta.
Aquí ‘MySequence’ es una clase abstracta dentro de la biblioteca. Un desarrollador puede importarlo y registrar una clase personalizada. Echemos un vistazo al siguiente código.
Python3
import abc class MySequence(metaclass=abc.ABCMeta): pass class CustomListLikeObjCls(object): pass MySequence.register(CustomListLikeObjCls) print(issubclass(CustomListLikeObjCls, MySequence))
Producción:
True
Aquí, la instancia de CustomListLikeObjCls se pasa a la biblioteca registrándola con MySequence. Por lo tanto, la verificación de la instancia devuelve True. Además del método anterior, también puede usar el método de registro como decorador para registrar una clase personalizada. Veamos cómo usar el método de registro como decorador .
Python3
import abc class MySequence(metaclass=abc.ABCMeta): pass @MySequence.register class CustomListLikeObjCls(object): pass print(issubclass(CustomListLikeObjCls, MySequence))
Producción:
True
Registrar una clase utilizando el método implementado anteriormente cumple el propósito. Sin embargo, debe realizar un registro manual para cada subclase prevista. ¿Qué tal la subclasificación automática basada en un método particular? Una clase abstracta tiene un concepto llamado __subclasshook__ para subclasificar las clases.
__subclasshook___
Es un método mágico especial definido por ABCMeta. El __subclasshook__ debe definirse como un método de clase usando el decorador @classmethod. Toma un argumento posicional adicional además de la clase y puede devolver cualquiera de los tres valores: verdadero, falso o no implementado. Veamos la siguiente implementación.
Python3
import abc class AbstractClass(metaclass=abc.ABCMeta): @classmethod def __subclasshook__(cls, other): print('subclass hook:', other) hookmethod = getattr(other, 'hookmethod', None) return callable(hookmethod) class SubClass(object): def hookmethod(self): pass class NormalClass(object): hookmethod = 'hook' print(issubclass(SubClass, AbstractClass)) print(issubclass(NormalClass, AbstractClass))
Producción:
subclass hook: <class '__main__.SubClass'> True subclass hook: <class '__main__.NormalClass'> False
De la discusión anterior, comprendió cómo enlazar subclases automáticamente. Ahora veremos cómo evitar instanciar una subclase que no anula un método particular en la superclase. Esta característica se puede lograr usando @abc.abstractmethod.
@abc.abstractmethod
@abc.abstractmethod evita cualquier intento de instanciar una subclase que no anula un método particular en la superclase. Echemos un vistazo al siguiente código:
Python3
import abc class AbstractClass(metaclass=abc.ABCMeta): @abc.abstractmethod def abstractName(self): pass class InvalidSubClass(AbstractClass): pass isc = InvalidSubClass()
Dado que InvalidSubclass no anula el método abstractName, @abc.abstractmethod evita que se cree una instancia de la subclase y arroja el siguiente error.
Rastreo (última llamada más reciente):
Archivo «/home/553d5199a662239eae3ff58efb37b6ec.py», línea 11, en <módulo>
isc = InvalidSubClass()
TypeError: No se puede crear instancias de la clase abstracta InvalidSubClass con métodos abstractos abstractName
Veamos otro ejemplo en el que la subclase anula el método abstracto.
Python3
import abc class AbstractClass(metaclass=abc.ABCMeta): @abc.abstractmethod def abstractName(self): pass class ValidSubClass(AbstractClass): def abstractName(self): return 'Abstract 1' vc = ValidSubClass() print(vc.abstractName())
Producción:
Abstract 1
A continuación, veamos cómo declarar propiedades como una clase abstracta.
Propiedades abstractas
Podemos usar @property decorator y @abc.abstractmethod para declarar propiedades como una clase abstracta. Veamos el siguiente código.
Python3
import abc class AbstractClass(metaclass=abc.ABCMeta): @property @abc.abstractmethod def abstractName(self): pass class ValidSubClass(AbstractClass): @property def abstractName(self): return 'Abstract 1' vc = ValidSubClass() print(vc.abstractName)
Producción:
Abstract 1
Clases abstractas incorporadas
La biblioteca estándar de Python 3 proporciona algunas clases abstractas integradas para métodos abstractos y no abstractos. Estos incluyen secuencia, secuencia mutable, iterable, etc. A menudo sirve como una alternativa a la creación de subclases de una clase de Python integrada. Por ejemplo, la subclasificación de MutableSequence puede sustituir la subclasificación de list o str. El propósito principal de usar la clase Abstract es que le permite considerar un tipo común de colección en lugar de codificar para cada tipo de colección. Aquí discutiremos ABC de método único y ABC de colección alternativa.
- ABC de método único
- ABC de colección alternativa
ABC de método único
Python tiene cinco clases base abstractas. Son los siguientes:
- Llamable (__call__)
- Contenedor (__contiene__)
- Hashable (__hash__)
- Iterable (__iter__)
- Tamaño (__len__)
Estas clases base abstractas contienen un método abstracto cada una. Consideremos un ejemplo del método __len__.
Python3
from collections.abc import Sized class SingleMethod(object): def __len__(self): return 10 print(issubclass(SingleMethod, Sized))
Producción:
True
Cualquier clase que tenga el método apropiado se considera como la subclase de la clase base abstracta. De las cinco clases base abstractas anteriores, el iterador es ligeramente diferente. Proporciona una implementación para __iter__ y agrega un método abstracto llamado __next__ .
ABC de colección alternativa
Los ABC de colección alternativa son clases base abstractas integradas que identifican subclases, que tienen propósitos similares. Se pueden dividir en tres categorías. Repasemos uno por uno.
- Secuencia y Secuencia mutable : Secuencia y Secuencia mutable son clases base abstractas que generalmente se comportan como tuplas o listas. Una clase base abstracta de secuencia requiere __getitem__ y __len__, mientras que una secuencia mutable necesita __setitem__ y __getitem__ .
- Mapeo : el mapeo viene con un mapeo mutable, que es principalmente para objetos similares a diccionarios
- Conjunto : el conjunto viene con un conjunto mutable que está destinado a colecciones desordenadas.
Resumen
El propósito clave de la clase abstracta es verificar si un objeto se ajusta a un protocolo particular. Es una clase valiosa para probar ciertos atributos de una clase o para probar la clase misma. Sin embargo, hay muchas otras cosas que la clase abstracta no verifica. Algunos de ellos son firmas, tipo de devolución, etc. Otra ventaja es que proporciona una forma flexible para que los desarrolladores prueben tipos comunes de colecciones.
Publicación traducida automáticamente
Artículo escrito por SonuGeorge y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA