大家好,又见面了,我是全栈君
抽象基类的核心定义在abc模块中,模块中包括了创建抽象基类需要的修饰符和元类型
abc.ABCMeta
用来生成抽象基础类的元类。由它生成的类可以被直接继承。abc.ABC
辅助类,让你可以不用关心元类概念,直接继承它,就有了ABCMeta元类。使用时注意元类冲突@abc.abstractmethod
定义抽象方法,除了这个装饰器,其余装饰器都被deprecated了
1. 抽象类示例
代码语言:javascript复制from abc import ABCMeta,abstractmethod
class Animal(metaclass = ABCMeta):
test = "hello world"
def __init__(self):
self.food = None
@abstractmethod
def eat(self):
pass
@abstractmethod
def run(self):
pass
@classmethod
def __subclasshook__(cls, subclass): #重写__subclasshook__方法,判断是否为子类
print ("come in")
if cls is Animal:
if any("eat" in b.__dict__ for b in subclass.__mro__):
return True
return NotImplemented
以上为Animal的抽象基类,注意重写了__subclasscheck__(cls, subclass)方法来改变issubclass或者isinstance的行为,__subclasscheck__(cls, subclass)必须为@classmethod
2. 具体化抽象类的两种方式
具体化抽象类可以有两种方式,一种通过注册(register),另外一种通过继承。
register方法
代码语言:javascript复制class Monkey:
def __init__(self):
#不会出现在类的__mro__,所以不会通过super()方法调用基类方法
super().__init__()
self.food= "banana"
def eat(self):
print ("{0} eat {1}".format(self.__class__.__name__, self.food))
#没有实现抽象方法时,实例化的时候不会报错,只有在调用的时候才会报错
#def run(self):
#pass
if __name__ == "__main__":
Animal.register(Monkey)
print (issubclass(Animal,Monkey))
m = Monkey()
m.eat()
>>>> come in >>>> True >>>> Monkey eat banana >>>> (<class ‘__main__.Monkey’>, <class ‘object’>)
注册方式的缺点:不会出现在类的
MRO
(Method Resolution Order),故而也不能通过super()
来调用抽象方法。当没有实现抽象方法时,实例化时候不会报错,只有在调用时候才会报错。
继承方法
直接从抽象基类派生子类有一个好处,除非子类实现抽象基类的抽象方法,否则子类不能实例化。
代码语言:javascript复制class Monkey(Animal):
def __init__(self):
super().__init__()
self.food= "banana"
def eat(self):
print ("{0} eat {1}".format(self.__class__.__name__, self.food))
def run(self):
pass
if __name__ == "__main__":
Animal.register(Monkey)
print (issubclass(Animal,Monkey)))
m = Monkey()
m.eat()
print (Monkey.__mro__)
3. ABCMeta类和ABC类源码
代码语言:javascript复制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):
cls = super().__new__(mcls, name, bases, namespace)
# 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
def register(cls, subclass):
"""Register a virtual subclass of an ABC.
Returns the subclass, to allow usage as a class decorator.
"""
if not isinstance(subclass, type):
raise TypeError("Can only register classes")
if issubclass(subclass, cls):
return subclass # Already a subclass
# Subtle: test for cycles *after* testing for "already a subclass";
# this means we allow X.register(X) and interpret it as a no-op.
if issubclass(cls, subclass):
# This would create a cycle, which is bad for the algorithm below
raise RuntimeError("Refusing to create an inheritance cycle")
cls._abc_registry.add(subclass)
ABCMeta._abc_invalidation_counter = 1 # Invalidate negative cache
return subclass
def _dump_registry(cls, file=None):
"""Debug helper to print the ABC registry."""
print("Class: %s.%s" % (cls.__module__, cls.__qualname__), file=file)
print("Inv.counter: %s" % ABCMeta._abc_invalidation_counter, file=file)
for name in sorted(cls.__dict__.keys()):
if name.startswith("_abc_"):
value = getattr(cls, name)
print("%s: %r" % (name, value), file=file)
def __instancecheck__(cls, instance):
"""Override for isinstance(instance, cls)."""
# Inline the cache checking
subclass = instance.__class__
if subclass in cls._abc_cache:
return True
subtype = type(instance)
if subtype is subclass:
if (cls._abc_negative_cache_version ==
ABCMeta._abc_invalidation_counter and
subclass in cls._abc_negative_cache):
return False
# Fall back to the subclass check.
return cls.__subclasscheck__(subclass)
return any(cls.__subclasscheck__(c) for c in {subclass, subtype})
def __subclasscheck__(cls, subclass):
"""Override for issubclass(subclass, cls)."""
# Check cache
if subclass in cls._abc_cache:
return True
# Check negative cache; may have to invalidate
if cls._abc_negative_cache_version < ABCMeta._abc_invalidation_counter:
# Invalidate the negative cache
cls._abc_negative_cache = WeakSet()
cls._abc_negative_cache_version = ABCMeta._abc_invalidation_counter
elif subclass in cls._abc_negative_cache:
return False
# Check the subclass hook
ok = cls.__subclasshook__(subclass)
if ok is not NotImplemented:
assert isinstance(ok, bool)
if ok:
cls._abc_cache.add(subclass)
else:
cls._abc_negative_cache.add(subclass)
return ok
# Check if it's a direct subclass
if cls in getattr(subclass, '__mro__', ()):
cls._abc_cache.add(subclass)
return True
# Check if it's a subclass of a registered class (recursive)
for rcls in cls._abc_registry:
if issubclass(subclass, rcls):
cls._abc_cache.add(subclass)
return True
# Check if it's a subclass of a subclass (recursive)
for scls in cls.__subclasses__():
if issubclass(subclass, scls):
cls._abc_cache.add(subclass)
return True
# No dice; update negative cache
cls._abc_negative_cache.add(subclass)
return False
class ABC(metaclass=ABCMeta):
"""Helper class that provides a standard way to create an ABC using
inheritance.
"""
pass
4.Python中的抽象基类
collections.abc.Callable
collections.abc.Iterator
collections.abc.Mapping
numbers
以上为Python提供的常用抽象基类,若要了解各抽象基类的相关信息,请参考具体的文档
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/120189.html原文链接:https://javaforall.cn