抽象基类
接口(interface)和抽象基类(abstract base class)是非常相似的,Python中并未提供真正意义上的接口,但是提供了抽象基类的使用。
它们的作用在于:
- 约束子类实现,子类必须实现抽象基类中的某一个方法才能被实例化,否则将不可被实例化
举个例子,羊(Sheep)和狼(Wolf)都属于哺乳类(Breastfeeding),那么它们都至少具有1个方法名为哺乳(feeding),如果没有该方法,则该类不能被实例化。
其实说白了,抽象基类的作用就是规范子类实现,所以:
- 抽象基类仅用于被继承,不必对其进行实例化
- 抽象基类内部方法不必实现
abc
Python中用abc模块定义抽象基类,只需要将抽象基类的metaclass=abc.ABCMeta即可。
除此之外,它还提供了3个装饰器:
- abc.abstractclassmethod:抽象类方法
- abc.abstractstaticmethod:抽象静态方法
- abc.abstractmethod:抽象实例方法
代码示例如下:
import abc
class Breastfeeding(metaclass=abc.ABCMeta):
@abc.abstractmethod
def feeding(self):
pass
class Sheep(Breastfeeding):
def feeding(self):
return "sheep feeding"
class Wolf(Breastfeeding):
pass
if __name__ == "__main__":
sheepInstance = Sheep()
print("sheep class instance success!")
wolfInstance = Wolf()
# sheep class instance success!
# TypeError: Can't instantiate abstract class Wolf with abstract methods feeding
这个例子中,羊实现了哺乳的方法,所以实例化成功了,而狼则没有实现哺乳方法,故实例化失败了。
如果有1个狗(Dog)类继承了狼类,那么狗类能否受到抽象基类的影响呢?
答案是不能,抽象基类只能影响其下一代继承类的实现,不能影响其孙系类。
如下所示:
import abc
class Breastfeeding(metaclass=abc.ABCMeta):
@abc.abstractmethod
def feeding(self):
pass
class Sheep(Breastfeeding):
def feeding(self):
return "sheep feeding"
class Wolf(Breastfeeding):
def feeding(self):
return "wolf fedding"
class Dog(Wolf):
pass
if __name__ == "__main__":
sheepInstance = Sheep()
print("sheep class instance success!")
wolfInstance = Wolf()
print("wolf class instance success!")
dogInstance = Dog()
print("dog class instance success!")
# sheep class instance success!
# wolf class instance success!
# dog class instance success!
继承约束
通过继承,也能进行子类的行为约束,但是常规手段不能禁止其实例化。
如下所示,狼类没有实现哺乳方法,依然实例化成功了,但是不能调用哺乳方法:
class Breastfeeding:
def feeding(self):
raise AssertionError("%s must implement method feeding" %
(self.__class__.__name__))
class Sheep(Breastfeeding):
def feeding(self):
return "sheep feeding"
class Wolf(Breastfeeding):
pass
if __name__ == "__main__":
sheepInstance = Sheep()
print("sheep class instance success!")
print(sheepInstance.feeding())
wolfInstance = Wolf()
print("wolf class instance success!")
print(wolfInstance.feeding())
# sheep class instance success!
# sheep feeding
# wolf class instance success!
# AssertionError: Wolf must implement method feeding
上面这种方式在很多框架中也被经常使用到,但如果你想实现和ABCMeta同样的效果,可以覆写基类的__new__()方法,如下所示:
class Breastfeeding:
def __new__(cls) -> object:
for attr in Breastfeeding.__dict__:
if attr.startswith("__"):
continue
if attr not in cls.__dict__:
raise TypeError("Can't instantiate abstract class %s with abstract methods %s" %
(cls.__name__, attr))
return super(Breastfeeding, cls).__new__(cls)
def feeding(self):
pass
class Sheep(Breastfeeding):
def feeding(self):
return "sheep feeding"
class Wolf(Breastfeeding):
pass
if __name__ == "__main__":
sheepInstance = Sheep()
print("sheep class instance success!")
print(sheepInstance.feeding())
wolfInstance = Wolf()
print("wolf class instance success!")
print(wolfInstance.feeding())
# sheep class instance success!
# sheep feeding
# TypeError: Can't instantiate abstract class Wolf with abstract methods feeding