Python抽象类
在Python中抽象类只能被继承,不能被实例化。
并且,抽象类中只有抽象方法和普通方法。
定义抽象类和抽象方法
Python的抽象类的定义需要abc
模块。(= =...)
# 导入抽象类需要用到的库
from abc import ABCMeta, abstractmethod
class Person(metaclass=ABCMeta):
"""使用元类(模板类)"""
pname = "这是Person抽象类" # 可以定义属性
# 定义抽象方法,抽象方法不需要方法体
# 需要在方法前加@abstractmethod装饰器
@abstractmethod
def run(self):
pass # 不需要写方法体
@classmethod # 可以定义类方法
def eat(cls):
print("在吃东西啊")
@staticmethod
def drink(): # 可以定义静态方法
print("在喝东西啊")
def sleep(self): # 可以定义普通方法
print("在睡觉啊")
抽象方法不需要写方法体,但并不是不能写。可以写,但是没有用。
抽象类中可以定义属性、类方法、静态方法
使用抽象类和抽象方法
class Student(Person):
sno = 123 # 学号
# 不会提示要要重写方法
def run(self):
print("跑")
# 不重写抽象方法就无法实例化对象,并且抛异常
s = Student()
s.run() # 跑
s.eat() # 在吃东西啊
s.sleep() # 在睡觉啊
print(s.pname) # 这是Person抽象类
s.drink() # 在喝东西啊
# p = Person() 运行时抛异常
继承了抽象类,就必须重写抽象类中的抽象方法,否则无法实例化对象,并抛异常。
应该尽量避免多继承
如果出现多继承,那么对于同名抽象方法,哪个继承类靠前就是实现的哪个类的抽象方法。
这对于抽象方法来说没什么意义。
但是!
由于抽象类不止能写抽象方法,还可以写属性、类方法、静态方法、普通方法,这些方法如果同名,就是按继承顺序来继承和重写的了。
class A(metaclass=ABCMeta):
num = 10
def __init__(self):
print("这是A的构造方法")
@abstractmethod
def show(self):
pass
@classmethod # 可以定义类方法
def eat(cls):
print("吃 1")
@staticmethod
def drink(): # 可以定义静态方法
print("喝 1")
def sleep(self): # 可以定义普通方法
print("睡 1")
class B(metaclass=ABCMeta):
num = 20
def __init__(self):
print("这是B的构造方法")
@abstractmethod
def show2(self):
pass
@classmethod # 可以定义类方法
def eat(cls):
print("吃 2")
@staticmethod
def drink(): # 可以定义静态方法
print("喝 2")
def sleep(self): # 可以定义普通方法
print("睡 2")
class C(B, A):
def show(self):
print("showA")
def show2(self):
print("showB")
c = C()
# 在打印20前,先打印了 这是B的构造方法 但是没有打印 这是A的构造方法
print(c.num) # 20
c.show() # showA
c.eat() # 吃 2
c.drink() # 喝 2
c.sleep() # 睡 2
c.show2() # showB
抽象类中可以写构造方法,但是子类继承时只会调用第一个继承类的构造方法。
抽象类方法和抽象静态方法
class A和class B中增添这两个方法
@classmethod
@abstractmethod
def swim(cls): # 抽象 类方法
pass
@staticmethod
@abstractmethod
def run(): # 抽象 静态方法
pass
与抽象方法一样,方法体可以不写,能写,但没必要。
子类必须实现这两个方法才能实例化。
不过!
子类实现后,就是普通方法了。与普通抽象方法的实现没啥区别。
class C(B, A):
def show(self):
print("show")
def swim(self):
print("swim")
def run(self):
print("run")
# 不能这样永类名调用
C.swim()
C.run()
所以还要在实现的方法上加对应的装饰器。
@classmethod
def swim(cls):
print("swim")
@staticmethod
def run():
print("run")
然而这时写反装饰器(不按抽象类中的来),也不会有啥事。
所以说抽象类只保证了抽象类本身不能实例化,和子类必须实现与抽象的方法(抽象方法、抽象类方法、抽象静态方法)同名的方法。
但是不保证子类的实现方法和原抽象的方法是同种类型。
简单来说,只要子类有同名的方法就行,管它是不是为了实现抽象的方法的,还是自己本身独有的。
抽象“属性”
Python中有个个特殊的装饰器@property
。用了它,方法就可以当属性一样用(调用时不需要加(),直接.属性名
)
@property
@abstractmethod
def age(self): # 抽象属性
pass
但是在实现时也必须要加上@property
,否则也就当成普通方法了。
@property
def age(self):
print("age")
return 22
print(c.age) # 先打印 22 再打印 age
print(type(c.age)) # <class 'int'>
c.age = 30 # 这样会抛异常,只读不能修改
print(c.age)
这个属性是只读的,不能修改。
当然可以配合@X.setter
和@X.deleter
来使得该“抽象”属性可读可写可删除。(其中X
是@property
修饰的方法的方法名)
这里不演示了,有兴趣的可以去查看@property
/@X.setter
/@X.deleter
三个装饰器的使用情况和方法。
也一定要注意子类中实现时,对应也要加上装饰器修饰,否则会被当成普通方法。
使用抽象类的好处
对于一些有相似属性和方法类,可以统一把这些属性和方法抽出来放在一个类中。
这样可以很好的理清类直接的继承关系、方法属性的意义、做到了解耦合。
比如说,猫和狗都是动物,都有年龄、性别等属性、都有吃喝拉撒睡等动作(假设这些共有动作两者是不同的)。那么就可以把这些属性和动作抽出来放在一个动物类中(class Animal)。让猫类(class Cat)和狗类(class Dog)都继承这个动物类,然后根据自身特点实现这些属性和动作即可。
其实如果动作相同的话就没必要写成抽象方法,而是写成普通方法即可,这样不需要重写方法,直接调用就行。
抽象就是一种规范,让继承了 抽象类 后的子类必须实现抽象方法才能够实例化。
接口?
Python中没有接口的概念,但是鉴于Java的接口理念,Python可以用abc模块
像实现抽象类那样,来实现接口类。
只要在类中只写抽象的方法、static和final属性(用@property
等和@staticmethod
配合着使用),那么就可以。
当然肯定没有Java中那么严格的检验,不过也勉强能做到。
但最终还是用看开发的需求来决定是否要这样去写,不能为了炫技而去写一些只能自己才能看懂的代码啊,那就失去了编程的初衷了。