una interfaces hacer como clases clase abstractas abstracta python abstract-class abc

interfaces - como hacer una clase abstracta en python



Determine si una clase de Python es una clase base abstracta o concreta (3)

Las clases abstractas y sus implementaciones concretas tienen un atributo __abstractmethods__ contiene los nombres de los métodos abstractos y las propiedades que no se han implementado. Este comportamiento se describe en PEP 3199 :

Implementación: el decorador @abstractmethod establece el atributo de función __isabstractmethod__ en el valor True . El método ABCMeta.__new__ calcula el atributo de tipo __abstractmethods__ como el conjunto de todos los nombres de métodos que tienen un atributo __isabstractmethod__ cuyo valor es verdadero. Lo hace combinando los atributos __abstractmethods__ de las clases base, agregando los nombres de todos los métodos en el nuevo __isabstractmethod__ clase que tienen un verdadero atributo __isabstractmethod__ y eliminando los nombres de todos los métodos en el nuevo dictado de clase que no tienen un verdadero __isabstractmethod__ atributo. Si el conjunto __abstractmethods__ resultante no está vacío, la clase se considera abstracta e intenta crear una instancia de TypeError. (Si esto se implementara en CPython, se podría utilizar un indicador interno Py_TPFLAGS_ABSTRACT para acelerar esta comprobación).

Entonces, en clases concretas, este atributo o no existirá o será un conjunto vacío. Esto es fácil de verificar:

def is_abstract(cls): if not hasattr(cls, "__abstractmethods__"): return False # an ordinary class elif len(cls.__abstractmethods__) == 0: return False # a concrete implementation of an abstract class else: return True # an abstract class

O más sucintamente:

def is_abstract(cls): return bool(getattr(cls, "__abstractmethods__", False))

print(is_abstract(object)) # False print(is_abstract(MessageDisplay)) # True print(is_abstract(FriendlyMessageDisplay)) # True print(is_abstract(FriendlyMessagePrinter)) # False

Mi aplicación Python contiene muchas clases abstractas e implementaciones. Por ejemplo:

import abc import datetime class MessageDisplay(object): __metaclass__ = abc.ABCMeta @abc.abstractproperty def display(self, message): pass class FriendlyMessageDisplay(MessageDisplay): def greet(self): hour = datetime.datetime.now().timetuple().tm_hour if hour < 7: raise Exception("Cannot greet while asleep.") elif hour < 12: self.display("Good morning!") elif hour < 18: self.display("Good afternoon!") elif hour < 20: self.display("Good evening!") else: self.display("Good night.") class FriendlyMessagePrinter(FriendlyMessageDisplay): def display(self, message): print(message)

FriendlyMessagePrinter es una clase concreta que podemos usar ...

FriendlyMessagePrinter().greet()

Good night.

... pero MessageDisplay y FriendlyMessageDisplay son clases abstractas y al intentar crear una instancia se generará un error:

TypeError: Can''t instantiate abstract class MessageDisplay with abstract methods say

¿Cómo puedo verificar si un objeto de clase dado es una clase abstracta (no insustituible)?


Podrías hacer esto con el módulo _ast . Por ejemplo, si su código de ejemplo estuviera en foo.py , podría invocar esta función con "foo.py" y "FriendlyMessagePrinter" como argumentos.

def is_abstract(filepath, class_name): astnode = compile(open(filename).read(), filename, ''exec'', _ast.PyCF_ONLY_AST) for node in astnode.body: if isinstance(node, _ast.ClassDef) and node.name == class_name: for funcdef in node.body: if isinstance(funcdef, _ast.FunctionDef): if any(not isinstance(n, _ast.Pass) for n in funcdef.body): return False return True print ''class %s not found in file %s'' %(class_name, filepath)


import inspect print(inspect.isabstract(object)) # False print(inspect.isabstract(MessageDisplay)) # True print(inspect.isabstract(FriendlyMessageDisplay)) # True print(inspect.isabstract(FriendlyMessagePrinter)) # False

Esto comprueba que el indicador interno TPFLAGS_IS_ABSTRACT está establecido en el objeto de clase, por lo que no se puede engañar tan fácilmente como su implementación:

class Fake: __abstractmethods__ = ''bluh'' print(is_abstract(Fake), inspect.isabstract(Fake)) # True, False