Python抽象类
Python抽象类
python没有接口类型,因为python是动态类型的语言,像接口这种轻耦合的东西在python中随处都是,例如内置的魔法方法等,甚至可以说在python这种天马行空的语言中接口显的反而有点清秀。
不过Python还是提供了像java中那样的抽象类定义方法,某些时候还是有用的,顾名思义抽象类就是对一些实体类的抽象定义,其出现的主要目的就是为了描述某一类拥有相同方法的类。
抽象类是介于普通的类继承和接口之间的一种中庸之道,他与接口之间的差别不大,甚至可以说能用抽象类处理的实际上都可以用接口处理。
用更加通俗的语言来描述抽象类:
- 抽象类当中的抽象方法可以没有具体实现,只需要定义函数名和输入输出即可(python中输入输入亦可省)
- 抽象类不能被直接实例化,只有实现了其抽象方法的子类才能实例化
- 抽象类的子类必须实现抽象方法
为什么需要抽象类?
1.我们想要为多个实体类定义一个父类,但是我们不想让父类直接实例化,因为父类的实例化没有意义。举个例子,我们定义一个动物类,他有一个sing()方法,但是每种动物的叫声都不一样,直接实例化Animal没有任何意义,我们只是想让他抽象出sing方法来:
class Animal(object):
def __init__(self, name):
self.name = name
def sing(self):
pass
def say_name(self):
print(self.name)
这样的普通Animal类可以实例化,但是之后呢,其sing方法完全是失效的,同时继承的子类并不强制要求实现sing方法,要求太宽松了。
class AnimalBaseClass(abc.ABC):
@abc.abstractmethod
def sing(self):
pass
def say_name(self):
print(self.name)
抽象类就不一样了,他直接禁止你实例化AnimalBaseClass,同时要求子类必须实现sing方法,哪怕你在子类的sing中直接pass也行,但必须得有,这样我们可以确保子类的sing方法有意义(如果在子类中继续定义sing() pass那是你子类的错和我抽象类有什么关系~~)。
当然对于say_name方法直接使用就可以,这点和普通父类继承一样,所以可以看到抽象类也可以定义普通方法(非抽象方法),甚至可以这么说:除了抽象方法外,抽象类其他的定义就和普通父类一样即可,可以定义__init__()方法,子类初始化时也可以直接使用super().__init__来初始化一部分属性。
2.所以我们为什么需要抽象类?
答案是当然非必须啊,你要喜欢继承普通父类并且随意的重载和增加方法都随意,但是定义抽象类可以使代码更加有逻辑,在规则的驱使下大型项目可以更加便于协作的和后期维护,这个多接触一些项目就明白了。
使用抽象类或接口的另一大好处是,当在其他地方需要统一处理某一大类对象时,我们可以直接使用抽象类型or接口类型作为输入,此时使用任意子类作为输入都合法,这样可以避免繁复的子类类型判断或编写多个函数。
在Python中我们需要借助内置的抽象基类帮助定义抽象类:
from abc import ABC, abstractmethod
class MyBaseClass(ABC):
@abstractmethod
def my_abstract_method(self):
pass
def my_regular_method(self):
return "Hello from MyBaseClass"
class MyDerivedClass(MyBaseClass):
def my_abstract_method(self):
return "Hello from MyDerivedClass"
my_derived_instance = MyDerivedClass()
print(my_derived_instance.my_abstract_method()) # Output: Hello from MyDerivedClass
print(my_derived_instance.my_regular_method()) # Output: Hello from MyBaseClass
其中@abstractmethod
用于标识一个必须被子类实现的抽象方法,不同于java当中对抽象类定义和继承的各种限制,在python中我们只要继承ABC类即可定义出一个抽象类,其他的地方随你怎么写。
通常情况的,在python中你没有必要定义抽象类,因为抽象类相当于规则的制定者,一般这种角色在开源库或标准包中较为常见,例如xxxBaseClass,当你看到base class这种命名时可以额外注意下,普通父类一般不会这么命名,只有作为其他实体类的抽象基类时才会约定命名为此。