Python面向对象之三大特征-多态

发布时间 2024-01-12 20:34:01作者: Lea4ning

多态性

【一】概要

  • 多态是面向对象编程中的一个重要概念,指的是同一种操作作用于不同的对象上时,可以产生不同的行为。多态性允许同样的方法名在不同的对象上有不同的实现,这样可以提高代码的灵活性和可扩展性。
  • 在多态的情况下,程序可以根据对象的类型调用相应的方法,而不需要知道具体对象的类型。这样,不同的类可以实现相同的接口或继承相同的父类,并提供各自的实现,使得代码更容易扩展和维护。

【二】常用方法

  • 例:只要是动物就会有叫这个方法
  • 使用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】继承与抽象

  1. 继承(Inheritance):
    • 定义: 继承是一种机制,允许一个类(子类或派生类)获取另一个类(父类或基类)的属性和方法。
    • 特点: 子类继承了父类的特征,包括属性和方法。这使得子类可以重用父类的代码,并在此基础上进行扩展或修改。
    • 目的: 提高代码的重用性、可维护性和可扩展性。通过继承,可以建立类之间的层次结构,实现通用性和特定性的分离。
  2. 抽象(Abstraction):
    • 定义: 抽象是一种思维方式,通过忽略或隐藏与问题无关的细节,突出问题的关键特征,从而提高问题的理解和解决效率。
    • 特点: 抽象关注于问题的本质,将复杂的现实世界简化为关键的概念或模型。在编程中,抽象可以通过类和接口来实现,将对象的属性和行为抽象出来。
    • 目的: 提高代码的理解和设计水平,降低复杂性,使代码更加清晰和易于维护。
  • 继承:实现个性化

img

  • 抽象:将相似的特征抽象成类

img

【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.2】具体案例

  • 定义一个工具类,类下的对象是文件类型,都必须有读和写的功能
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

【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__方法

image-20240112185705445