Python面向对象之三大特征-多态
多态性
【一】概要
- 多态是面向对象编程中的一个重要概念,指的是同一种操作作用于不同的对象上时,可以产生不同的行为。多态性允许同样的方法名在不同的对象上有不同的实现,这样可以提高代码的灵活性和可扩展性。
- 在多态的情况下,程序可以根据对象的类型调用相应的方法,而不需要知道具体对象的类型。这样,不同的类可以实现相同的接口或继承相同的父类,并提供各自的实现,使得代码更容易扩展和维护。
【二】常用方法
- 例:只要是动物就会有叫这个方法
- 使用abc模块,也叫抽象类,强制要求继承的子类必须包含父类的属性
import abc # abstract class
class Animal(metaclass = abc.ABCMeta): # 通过继承metaclass声明Animal是抽象类
@abc.abstractmethod
def speak(self): # 定义抽象类方法 # 也就是继承Animal类的子类中必须有speak这个方法
pass
class Person(Animal):
def speak(self):
pass # 哪怕没有内容,也需要写上,否则会报错
# 错误
Animal() # 抽象类是不可以被实例化的,只能被继承
- 不做强制限制,但需要自觉的遵从一些规范,如果你属于这个类,那么这个类共有的属性最好就加上
class Animal(object):
pass
class Person(Animal):
def speak(self): # 继承动物类的子类,自觉的将speak方法添加到自己的方法中
pass
class Cat(Animal):
# def speak(self):
# pass
def miaow(self): # 如果不写也不报错,但是最好遵循规范
pass
【三】详解
【1】继承与抽象
- 继承(Inheritance):
- 定义: 继承是一种机制,允许一个类(子类或派生类)获取另一个类(父类或基类)的属性和方法。
- 特点: 子类继承了父类的特征,包括属性和方法。这使得子类可以重用父类的代码,并在此基础上进行扩展或修改。
- 目的: 提高代码的重用性、可维护性和可扩展性。通过继承,可以建立类之间的层次结构,实现通用性和特定性的分离。
- 抽象(Abstraction):
- 定义: 抽象是一种思维方式,通过忽略或隐藏与问题无关的细节,突出问题的关键特征,从而提高问题的理解和解决效率。
- 特点: 抽象关注于问题的本质,将复杂的现实世界简化为关键的概念或模型。在编程中,抽象可以通过类和接口来实现,将对象的属性和行为抽象出来。
- 目的: 提高代码的理解和设计水平,降低复杂性,使代码更加清晰和易于维护。
- 继承:实现个性化
- 抽象:将相似的特征抽象成类
【2】抽象类
- 抽象类是一种不能被实例化的类,其目的是为了被子类继承,并要求子类必须实现特定的方法或属性。抽象类通常包含抽象方法,这些方法在抽象类中被声明但没有提供具体的实现,而是由具体的子类来实现。抽象类可以提供一些共同的接口和结构,以确保子类具有一致的行为。
- 抽象类通过
ABC
模块来实现,使用@abstractmethod
装饰器声明抽象方法。子类必须实现所有抽象方法才能被实例化,否则会引发TypeError
。抽象类的主要作用是定义规范,强制要求子类实现特定的行为。
import abc
class Person(metaclass=abc.ABCMeta):
'''【类Person】作为抽象类,只能被继承,不能被实例化'''
@abc.abstractmethod
def eat(self): # 声明抽象方法 吃
pass
Person() # TypeError: Can't instantiate abstract class Person with abstract method eat
【2.1】abc模块(abstarct class)
import abc
class Person(metaclass=abc.ABCMeta):
'''【类Person】作为抽象类,只能被继承,不能被实例化'''
'''通过模块abc实现,凡继承【类Person】的类,都需要有与抽象类同样的方法'''
@abc.abstractmethod
def eat(self): # 声明抽象方法 吃
pass
@abc.abstractmethod
def drink(self): # 声明抽象方法 喝
pass
class Chinese(Person):
def eat(self):
pass
def drink(self):
pass
# 错误
class English(Person):
'''如果继承了定义类,但是不重写继承的方法,将会报错'''
def eat(self):
pass
c = Chinese()
e = English() # TypeError: Can't instantiate abstract class English with abstract method drink
【2.1.1】具体案例
- 定义一个工具类,类下的对象是文件类型,都必须有读和写的功能
import abc
class Tools(metaclass=abc.ABCMeta):
'''通过模块abc实现,凡继承【类Tools】的类,都需要有与抽象类同样的方法'''
@abc.abstractmethod
def read(self, *args, **kwargs): # 读数据
pass
@abc.abstractmethod
def save(self, *args, **kwargs): # 保存数据
pass
# Json文件
class Json(Tools):
def read(self, path, mode='r'):
import json
with open(file=path, mode=mode) as f:
data = json.load(f)
return data
def save(self, data, path, mode='w'):
import json
with open(file=path, mode=mode) as f1:
json.dump(data, f1)
return "保存成功"
def json_own(self):
pass
# Pickle文件
class Pickle(Tools):
def read(self, path):
import pickle
data = pickle.load(path)
return data
def save(self, data, path):
import pickle
pickle.dump(data, path)
return "保存成功"
def pickle_own(self):
pass
【2.2】通过抛出异常限制子类
class Animal(object):
def speak(self):
raise Exception("请先实现speak功能!")
class Dog(Animal):
def speak(self): # 继承动物类的子类,自觉的将speak方法添加到自己的方法中
print("汪!汪!")
class Cat(Animal):
pass
d = Dog()
d.speak() # 汪!汪!
c = Cat()
c.speak() # 当子类中没有该功能时,将自动去父类中查找,而执行父类将抛出异常
【3】多态
- 多态指的是一类事物有多种形态
- 水:气态水、液态水、固态水
- 动物:人、狗、猪、等都是动物的一种形态
【3.1】鸭子类型(duck-typing)
-
在Python中,上述使用abc模块的方法其实并没有过多的使用,更加崇尚的是使用“鸭子类型”的这种编程风格
-
鸭子类型(Duck Typing)是一种动态类型的概念,关注的是对象的行为而非其类型。该概念源于一个谚语:“如果它走起路来像鸭子,叫起来像鸭子,那么它就是鸭子。”
-
在鸭子类型中,一个对象的适用性不是基于它的类(或类型),而是基于它是否具有特定的方法、属性或行为。这种方法不依赖于继承或接口的实现,而是依赖于对象的实际状态和行为。
-
例如,如果一个对象具有像鸭子一样的
quack
方法和swim
方法,那么它就可以被视为“鸭子”,即使它并没有继承自特定的鸭子类或接口。这种方式使得代码更加灵活,可以接受多种不同类型的对象,只要它们满足所需的行为。
class Duck():
def swim(self):
# 会游泳
pass
def quack(self):
# 会“嘎嘎”叫
print("嘎嘎")
class DonaldDuck():
'''唐老鸭'''
def swim(self):
# 会游泳
pass
def quack(self):
# 会“嘎嘎”叫
print("嘎嘎")
class Goose():
'''鹅'''
def swim(self):
# 会游泳
pass
def quack(self):
# 会“嘎嘎”叫
print("嘎嘎")
# 鹅和唐老鸭都具有,会游泳和会嘎嘎叫的属性,所有鹅和唐老鸭都可以称为鸭子
# 并不依赖于继承,只要有同样的属性,就都可以称他们为鸭子类型
"""鸭子类型就是更多的关注的是对象的行为,而不是对象的类型"""
【3.2】多态性的具体应用场景
- 我们可以根据多态性来进行优化代码,实现代码重用
(1)抽象类
'''拿上述的Tools举例'''
import abc
class Tools(metaclass=abc.ABCMeta):
'''通过模块abc实现,凡继承【类Tools】的类,都需要有与抽象类同样的方法'''
@abc.abstractmethod
def read(self, *args, **kwargs): # 读数据
pass
@abc.abstractmethod
def save(self, *args, **kwargs): # 保存数据
pass
# Json文件
class Json(Tools):
def read(self, path, mode='r'):
import json
with open(file=path, mode=mode) as f:
data = json.load(f)
return data
def save(self, data, path, mode='w'):
import json
with open(file=path, mode=mode) as f1:
json.dump(data, f1)
return "保存成功"
def json_own(self):
pass
# Pickle文件
class Pickle(Tools):
def read(self, path):
import pickle
with open(path, 'rb') as fp:
data = pickle.load(fp)
return data
def save(self, data, path):
import pickle
with open(path, 'wb') as fp:
pickle.dump(data, fp)
return "保存成功"
def pickle_own(self):
pass
- 当我们需要调用工具时,可以通过定义一个函数来实现调用
def file_handler(tag, file_obj, path, data=None):
if tag == 'read':
return file_obj.read(path)
elif tag == 'save':
return file_obj.save(data, path)
json = Json()
pickle = Pickle()
# json文件
file_handler('save', json, '1.json', data='json')
file_handler('read', json, '1.json')
# pickle文件
file_handler('save', pickle, 'test', data='pickle')
file_handler('read', pickle, 'test')
(2)多态性
class Animal(object):
pass
class Dog(Animal):
def speak(self): # 继承动物类的子类,自觉的将speak方法添加到自己的方法中
print("汪!汪!")
class Cat(Animal):
def speak(self):
print("喵喵~")
def speak_animal(animal):
animal.speak()
cat = Cat()
dog = Dog()
speak_animal(cat) # 都是调用speak_animal函数,打印结果不相同
speak_animal(dog) # 这就表现了多态性
# 喵喵~
# 汪!汪!
- Python中的内置函数,也能体现多态,比如
len()
- 可惜看不到底层的源码
len()
方法,是许多数据类型都可以调用的方法
str1 = 'str'
list1 = [1, 2, 3]
dict1 = {1: 1, 2: 2}
set1 = {1, 2, 3}
tuple1 = (1, 2, 3)
print(len(str1)) # 3
print(len(list1)) # 3
print(len(dict1)) # 2
print(len(set1)) # 3
print(len(tuple1)) # 3
'''其实就相当于一个函数'''
def my_len(obj):
# obj对象中都定义了一个__len__方法
return obj.__len__()
print(my_len(str1)) # 3
print(my_len(list1)) # 3
print(my_len(dict1)) # 2
print(my_len(set1)) # 3
print(my_len(tuple1)) # 3
<class 'list'>
等以上可以使用len()
方法的数据类型中,均有__len__
方法