1. 抽象类
python中没有接口的概念,但是提供了abc模块来实现接口的目的,其实接口就是一个 规范,定义了一些抽象方法,但是没有具体实现,具体的实现交给继承它的类,如果一个类继承了抽象类,但是没有完全实现父类的抽象方法,就不能实例化对象。
在其他语言比如java go中,没有实现全接口的抽象方法,编译都会报错,但是python里只要不进行实例化,不会报错,毕竟python是一种解释性语言,不是编译型的。
如果说类是抽象对象的共同属性和方法,那么抽象类或接口就是 将 具体类的一些共性给抽象出来的 规范
.
2. 类的__abstractmethods__魔法属性
如果一个类的__abstractmethods__
属性不为空,那么它就不能实例化
class AbstractClass:
pass
AbstractClass.__abstractmethods__ = set(['my_abstract__method'])
AbstractClass()
`TypeError: Can't instantiate abstract class AbstractClass with abstract methods my_abstract__method`
3. abstractmethod方法和ABCMeta类
直接看源码
def abstractmethod(funcobj):
"""A decorator indicating abstract methods.
Requires that the metaclass is ABCMeta or derived from it. A
class that has a metaclass derived from ABCMeta cannot be
instantiated unless all of its abstract methods are overridden.
The abstract methods can be called using any of the normal
'super' call mechanisms.
Usage:
class C(metaclass=ABCMeta):
@abstractmethod
def my_abstract_method(self, ...):
...
"""
funcobj.__isabstractmethod__ = True
return funcobj
abstractmethod()
方法就是一个装饰器,给被装饰的函数添加一个__isabstractmethod__
属性,
如果一个方法的__isabstractmethod__
为True,那么它就是抽象方法。
接下来这个ABCMeta
类就很核心了,python中的抽象类都是它创建的,因为它就是一个元类,一个创建类的类,如果对元类不了解的话,先去了解一下元类的概念。
class ABCMeta(type):
"""Metaclass for defining Abstract Base Classes (ABCs).
Use this metaclass to create an ABC. An ABC can be subclassed
directly, and then acts as a mix-in class. You can also register
unrelated concrete classes (even built-in classes) and unrelated
ABCs as 'virtual subclasses' -- these and their descendants will
be considered subclasses of the registering ABC by the built-in
issubclass() function, but the registering ABC won't show up in
their MRO (Method Resolution Order) nor will method
implementations defined by the registering ABC be callable (not
even via super()).
"""
# A global counter that is incremented each time a class is
# registered as a virtual subclass of anything. It forces the
# negative cache to be cleared before its next use.
# Note: this counter is private. Use `abc.get_cache_token()` for
# external code.
_abc_invalidation_counter = 0
def __new__(mcls, name, bases, namespace, **kwargs):
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
# Compute set of abstract method names
abstracts = {name
for name, value in namespace.items()
if getattr(value, "__isabstractmethod__", False)}
for base in bases:
for name in getattr(base, "__abstractmethods__", set()):
value = getattr(cls, name, None)
if getattr(value, "__isabstractmethod__", False):
abstracts.add(name)
cls.__abstractmethods__ = frozenset(abstracts)
# Set up inheritance registry
cls._abc_registry = WeakSet()
cls._abc_cache = WeakSet()
cls._abc_negative_cache = WeakSet()
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
return cls
这个new方法就是给类动态添加__abstractmethods__
属性,从代码里可以看出来,它先将自身过滤一遍,自身是否有抽象方法,然后在去找父类中的抽象方法,如果类没有实现父类的抽象方法,就添加到
cls.__abstractmethods__
中。
4. 使用
abc模板提供了一个便捷类,供用户的抽象类继承,它就是ABC
类
from abc import abstractmethod, ABC
class Animal(ABC):
@abstractmethod
def walk(self):
pass
class Dog(Animal):
def walk(self):
print('狗走路')
Animal是一个抽象类,Dog类必须实现walk方法,否则不能使用,因为在父类Animal中walk()方式是抽象方法,子类必须实现了该方法,才能实例化。
另外python是允许多重继承的,这样就更灵活了,可以达到java中允许接口多实现的类似功能。
也可以定义个抽象类,指定其元类是ABCMeta,然后定义一系列的抽象方法,这个跟直接继承ABC实现抽象类,其本质都是一样的,因为最终的子类都是会在ABCMeta中new方法中进行改造
,就是动态添加__abstractmethods__
属性,如果该属性不为空,就不能实例化。
5. python自身的应用
python中的collections包中也有一个abc模块,这个模块导入了_collections_abc模块,_collections_abc这个模块,定义了许多python许多原生抽象类,它们都指定ABCMeta为元类,有兴趣的可以去看看。