abc模块有以下两个主要功能:
- 某种情况下,判定某个对象的类型,如:isinstance(a, Sized)
- 强制子类必须实现某些方法,即ABC类的派生类
Python的ABC模块(Abstract Base Classes)提供了一种基于抽象基类的继承机制,用于实现多态性和代码重用。ABC模块包含了一组用于定义抽象基类的ABC类,以及一些常用的ABC子类。
以下是ABC模块中一些常用的ABC子类:
- ABC:这是一个最顶层的ABC类,它继承自object类,用于定义所有抽象基类的接口。所有ABC子类都必须实现一个方法__subclasses__(),返回一个包含其子类的元组。
- abstractbaseclasses:这是一个包含了所有抽象基类的模块,例如abc.ABC、abc.ABCMeta等。这些抽象基类提供了一些常量和类方法,用于简化多态性的实现。
- abc.ABCMeta:这是一个元类,用于创建抽象基类。它提供了__prepare__()方法,用于定义抽象基类的命名空间,并提供了__mro__()方法,用于确定抽象基类的多重继承顺序。
- abc.abstractclassmethod:这是一个定义了抽象方法的ABC子类。这些方法只定义了__get__()方法,但不允许在定义时调用它们。
- abc.abstractmethod:这是一个定义了抽象方法的ABC子类。这些方法没有定义__get__()方法,但允许在定义时调用它们。
- abc.abstractproperty:这是一个定义了抽象属性的ABC子类。这些属性没有定义__get__()和__set__()方法,但允许在定义时调用它们。
使用ABC模块可以实现以下功能:
- 定义抽象基类:使用ABC模块中的ABC子类来定义抽象基类,这些基类提供了默认的抽象方法实现,并且可以继承其他基类。
- 实现多态性:使用抽象基类和其子类来实现多态性,即通过调用对象的__getattribute__()方法来获取对象的属性值,而不是直接访问对象的属性。
- 自动实现抽象方法:在抽象基类的子类中定义实现这些抽象方法的代码,以便在使用该基类的子类对象时自动调用它们。
- 静态多态性:通过将类名和对象作为参数传递给该类的__new__()方法来创建新的实例对象,然后调用该实例对象的静态方法来访问对象的属性值。
定义抽象基类
ABC模块是Python中的一个抽象基类模块,它提供了一些用于定义抽象基类的工具。下面是ABC模块中的四个关键概念:
- abstractmethod:这是一个特殊的方法,用于定义抽象基类中的方法。它是一个抽象方法,表示该方法必须被子类实现。
- abstractproperty:这是一个特殊的数据属性,用于定义抽象基类中的属性。它也是一个抽象方法,表示该属性必须被子类实现。
- abstractclassmethod:这是一个特殊的方法,用于定义抽象基类中的类方法。它是一个抽象方法,表示该方法必须被子类实现。
- abstractstaticmethod:这是一个特殊的方法,用于定义抽象基类中的静态方法。它也是一个抽象方法,表示该方法必须被子类实现。
这些概念的作用如下:
-
abstractmethod:定义了一个抽象方法,表示该方法必须在子类中实现。它是一个特殊的方法,具有以下特点:
-
必须被子类实现。
-
不能直接调用,必须通过子类实例调用。
-
可以使用@abc.abstractmethod装饰器来定义。
-
-
abstractproperty:定义了一个抽象属性,表示该属性必须在子类中实现。它是一个特殊的数据属性,具有以下特点:
-
必须被子类实现。
-
不能直接访问,必须通过子类实例访问。
-
可以使用@abc.abstractproperty装饰器来定义。
-
-
abstractclassmethod:定义了一个抽象类方法,表示该方法必须在子类中实现。它是一个特殊的方法,具有以下特点:
-
必须被子类实现。
-
不能直接调用,必须通过子类实例调用。
-
可以使用@abc.abstractclassmethod装饰器来定义。
-
-
abstractstaticmethod:定义了一个抽象静态方法,表示该方法必须在子类中实现。它是一个特殊的方法,具有以下特点:
-
必须被子类实现。
-
不能直接调用,必须通过子类实例调用。
-
可以使用@abc.abstractstaticmethod装饰器来定义。
-
下面是一个简单的示例,演示如何使用ABC模块定义一个抽象基类:
from abc import ABC, abstractmethod
class MyBaseClass(ABC):
@abstractmethod
def do_something(self):
pass
在上面的示例中,我们使用ABC和abstractmethod来定义一个名为MyBaseClass的抽象基类,其中包含一个名为do_something的纯虚函数。该函数的实现需要在派生类中实现。
abstractmethod
abc模块中的AbstractMethod类定义了抽象方法的基类,它只定义了方法签名,而没有实现方法的具体实现。
使用AbstractMethod类可以定义一个抽象方法,该方法没有实现,需要在子类中实现。
下面是一个示例代码:
from abc import ABC, abstractmethod
class MyBaseClass(ABC):
@abstractmethod
def my_method(self):
pass
class MyDerivedClass(MyBaseClass):
def my_method(self):
print("Hello, world!")
obj = MyDerivedClass()
obj.my_method() # 输出 "Hello, world!"
在上面的示例中,MyBaseClass是一个抽象基类,它定义了一个抽象方法my_method。MyDerivedClass继承自MyBaseClass,并实现了my_method方法。在实例化MyDerivedClass对象时,可以直接调用该对象的方法。
abstractproperty
ABC模块是Python中的一个抽象基类模块,它定义了一组抽象基类,这些抽象基类定义了一组纯虚函数和抽象类,这些抽象类不能被实例化,但可以被继承。
其中,abc模块中的ABCMeta类是ABC模块的核心,它是一个元类,可以用来创建抽象基类。ABCMeta类定义了以下两个方法:
- prepare(self, name, bases, namespace):这个方法用于生成抽象基类的元类。
- new(self, name, bases, namespace):这个方法用于创建抽象基类的实例。
使用ABC模块可以定义一些通用的抽象基类,这些抽象基类可以作为其他类的基类,使得这些其他类必须实现其中的纯虚函数。这样,在继承这些抽象基类的子类中,必须实现这些纯虚函数,否则这些子类也会成为抽象基类。
例如,可以使用abc.ABC作为抽象基类的基类,这样所有的子类都必须实现__init__和__repr__方法。
下面是一个简单的示例:
from abc import ABC, abstractmethod
class MyAbstractClass(ABC):
@abstractmethod
def my_method(self):
pass
class MySubClass(MyAbstractClass):
def my_method(self):
print("Hello, world!")
MySubClass() # 报错,因为MySubClass没有实现my_method方法
在上面的示例中,MyAbstractClass是一个抽象基类,它定义了一个纯虚函数my_method。MySubClass继承了MyAbstractClass,但是它没有实现my_method方法,因此它会报错。
abstractclassmethod
在Python中,ABC模块(Abstract Base Classes)提供了一种抽象基类的机制,它允许你定义一个抽象基类,该基类不提供具体实现,但要求派生类实现其中的方法。ABC模块提供了以下几个重要的类:
- ABCMeta:元类(metaclass)类,用于定义抽象基类。
- ABC:抽象基类类,定义了抽象基类的元方法。
- abstractmethod:定义了派生类必须实现的抽象方法。
- abstractclassmethod:定义了派生类可以调用抽象方法的抽象方法。
在ABC模块中,可以通过在派生类上添加ABC.abstractmethods、ABC.abstractclassmethod、ABC.abstractstaticmethod或ABC.abstractclassmethod方法来实现。其中,abc.ABCMeta可以作为类类元的方法(meta-class),通过设置元方法可以添加新的方法、重写已有方法、以及从已有的方法中添加参数。
以下是一个示例,展示如何使用ABC模块来定义一个抽象基类:
from abc import ABC, abstractmethod
class MyAbstractClass(ABC):
@abstractmethod
def my_abstract_method(self):
pass
@abstractclassmethod
def my_abstract_classmethod(cls):
pass
在这个示例中,我们定义了一个名为MyAbstractClass的抽象基类,其中包含一个名为my_abstract_method的抽象方法和一个名为my_abstract_classmethod的抽象类方法。注意,这里我们使用了@abstractmethod和@abstractclassmethod装饰器来标记这两个方法为抽象方法。
abstractstaticmethod
abc.abstractmethod是一个装饰器,用于定义抽象方法。抽象方法是一个不能直接实例化的方法,必须由其子类实现。使用abc.abstractmethod装饰器可以将一个普通方法转换为抽象方法。
下面是一个示例代码,演示如何使用ABC模块实现一个抽象基类:
from abc import ABC, abstractmethod
class MyBaseClass(ABC):
@abstractmethod
def my_abstract_method(self):
pass
class MySubClass(MyBaseClass):
def my_abstract_method(self):
print("Hello, world!")
obj = MySubClass()
obj.my_abstract_method() # 输出 "Hello, world!"
在上面的示例中,我们定义了一个名为MyBaseClass的抽象基类,其中包含一个名为my_abstract_method的抽象方法。然后我们定义了一个名为MySubClass的子类,实现了my_abstract_method方法。最后,我们创建了一个MySubClass的实例,并调用了它的my_abstract_method方法,输出结果为"Hello, world!"。
判断类型
当判断一个对象是否存在某个方法时,可以使用内置方法hasattr()
。
class A(object):
def __len__(self):
pass
def demo(self):
pass
if __name__ == "__main__":
a = A()
print("存在__len__方法" if hasattr(a, "__len__") else "没有__len__方法")
print("存在test_demo方法" if hasattr(a, "test_demo") else "没有test_demo方法")
"""输出:
存在__len__方法
没有test_demo方法
"""
但在Python中,使用hasattr()
并非优雅解法,这里建议isinstance()
abc模块中定义了Sized类,利用Sized可以判断一个对象里是否存在__len__
方法,即:可否对这个对象使用len()函数。
from collections.abc import Sized
class A(object):
def __len__(self):
pass
if __name__ == "__main__":
a = A()
print("存在__len__方法" if isinstance(a, Sized) else "没有__len__方法")
"""输出:
存在__len__方法
"""
isinstance实现原理
Sized类的源码
class Sized(metaclass=ABCMeta):
__slots__ = ()
@abstractmethod
def __len__(self):
return 0
@classmethod
def __subclasshook__(cls, C):
if cls is Sized:
return _check_methods(C, "__len__")
return NotImplemented
Sized类改写了__subclasshook__
魔法方法,使其可以通过**isinstance()**判断对象是否含有__len__
方法。同时,这个类必须基于元类abc.ABCMeta
。
可以依葫芦画瓢,实现一个用来判断对象是否存在greet()函数的类,尽管并不严谨:
import abc
class A(metaclass=abc.ABCMeta):
@classmethod
def __subclasshook__(cls, subclass):
# 存在greet()返回True,不存在返回False
if hasattr(subclass, "greet"):
return True
return False
class B(object):
def greet(self): # 定义了greet()方法
pass
class C(object): # 没有greet()方法
pass
class D(B): # 继承自B类,因此继承了greet()方法
pass
if __name__ == "__main__":
b = B()
c = C()
d = D()
print(isinstance(b, A)) # True
print(isinstance(c, A)) # False
print(isinstance(d, A)) # True
"""结果
True
False
True
"""
注意,此时A类可以被实例化,因为它还不是抽象基类。
实现ABC类
C++中利用纯虚函数实现抽象基类,Python中写法如下:
import abc
class A(metaclass=abc.ABCMeta):
@abc.abstractmethod # 利用装饰器修饰greet()
def greet(self):
print("hell world")
if __name__ == "__main__":
a = A()
A
作为基类不能直接被实例化, 运行则会抛出错误
Traceback (most recent call last):
File "C:/Users/Hi/AppData/Roaming/JetBrains/PyCharm2021.3/scratches/test_01.py", line 11, in <module>
a = A()
TypeError: Can't instantiate abstract class A with abstract methods greet
因为A类现在就是一个抽象基类了,不可以被实例化,同时,它的子类还必须实现greet()
方法,否则实例化子类时解释器也要报错:
import abc
class A(metaclass=abc.ABCMeta):
@abc.abstractmethod
def greet(self):
pass
class B(A):
def greet(self):
pass
class C(A):
pass
if __name__ == "__main__":
b = B() # 正常实例化
c = C() # 解释器抛错
# 输出:
# C类中没有定义greet()方法导致的报错
"""
Traceback (most recent call last):
File "C:/Users/Hi/AppData/Roaming/JetBrains/PyCharm2021.3/scratches/test_01.py", line 21, in <module>
c = C() # 解释器抛错
TypeError: Can't instantiate abstract class C with abstract methods greet
"""
其他基类
abc模块中还有实现了其他抽象基类,可以用来判断类型或是继承方法
__all__ = ["Awaitable", "Coroutine",
"AsyncIterable", "AsyncIterator", "AsyncGenerator",
"Hashable", "Iterable", "Iterator", "Generator", "Reversible",
"Sized", "Container", "Callable", "Collection",
"Set", "MutableSet",
"Mapping", "MutableMapping",
"MappingView", "KeysView", "ItemsView", "ValuesView",
"Sequence", "MutableSequence",
"ByteString",
]