01. 封装

1.1 封装的基本理解

  • 所谓封装,就是隐藏一切可以隐藏的实现细节,只对外暴露简单的接口即可。
  • 在类中定义的方法就可以理解为封装。在类中定义方法使得我们在创建对象之后,只需要通过对象.方法名(参数)的形式就可以执行一些定义好的操作。这个时候其实我们只需要直到方法的名字和参数即可,不需要知道其内部是怎么实现的。
  • 比如list_obj.append(data)可以实现向列表list_obj的末尾添加数据data,但我们只知道这个方法可以实现添加数据的操作,并不知道它具体是怎么添加数据,这就是所谓的封装。

    1.2 可见性

    1.2.1 可见性与属性/方法私有化

  • 在很多编程语言中,对象的属性或方法通常都有三个描述关键字:private(私有的)、public(公共的)、protected(受保护的)。

  • 其中,被private和protected修饰的属性或方法就无法被直接访问了。
  • Python中虽然没有private、public、protected这些关键字,但是Python中依然有这三档不同程度的可见性。
  • Python中不同可见性的实现:

    • 在默认情况下,一个类的属性或方法默认就是公共的,不管在哪个位置,只要可以创建对象就可以访问。
    • 若要将属性或方法设置成受保护protected的,只需要在属性名或方法名前添加一个下划线,如:_name
    • 若要将属性或方法设置成私有private的,只需要在属性名或方法名前添加两个下划线,如:__age。 ```python class Student: def init(self, sid, name, age): self.sid = sid # public属性 self._name = name # protected属性 self.__age = age # private属性

      私有化方法

      def __print_name(self): print(self._name)

stu = Student(1001, “小明”, 18) print(stu.sid) # 1001 print(stu.name) # 无法访问,报错:AttributeError: ‘Student’ object has no attribute ‘name’ print(stu.age) # 无法访问,报错:AttributeError: ‘Student’ object has no attribute ‘age’ stu.print_name() # 无法访问,报错:AttributeError: ‘Student’ object has no attribute ‘print_name’

  1. - 通过运行结果可以看出,将属性或方法私有化之后,就无法通过`对象.属性/方法()`的形式进行访问了。
  2. <a name="IisTl"></a>
  3. #### 1.2.2 私有化的应用场景
  4. - 私有化属性:
  5. - 一但确定值就不再改变的属性,是不允许被外界修改值的,这类属性通常会被私有化。
  6. - 比如一个人的性别在自然情况下是从出生那一刻起就确定了的,不会再修改,那么gender属性就可以私有化成`__gender`
  7. - 这种属性一般就会进行访问,不会进行改值,因此提供get()方法即可,不需要提供set()方法(但也可以提供)。
  8. - 属性的值不能被外界随便修改,这种情况下也要对属性进行私有化。
  9. - 比如一个人的姓名一般情况是不会轻易修改的,因此name属性可以私有化成`__name`。但是通过个人向相关户籍单位申请,那也是可以修改的。
  10. - 这种属性既可以进行访问,也可以进行改值,因此需要提供get/set方法。
  11. - 属性值的修改需要满足一定的条件时,也可以将属性进行私有化。
  12. - 如电脑要加内存,最好的情况就是组双通道,若不将内存属性私有化,那么内存就可以被随便赋值了。
  13. - 此时可以将属性私有化,然后在set中指定逻辑,比如若不满足双通道,就不扩容,只有满足双通道时才扩容成功。
  14. - 私有化方法:当某个方法是为了辅助类中其他方法完成计算才被定义出来,且该方法提供给对象使用无意义的情况下,会将这个方法私有化。
  15. <a name="EDXmX"></a>
  16. #### 1.2.3 get/set方法
  17. - 设置为private/protected的属性外界是无法直接访问的(包括取值与修改),因此可以针对于一个属性提供get/set方法来访问私有化的属性。
  18. - `get_属性名()`:用于获取私有化属性的值。
  19. - `set_属性名`:用于修改私有化属性的值。
  20. - 注意:get/set方法必须是public的。
  21. - 示例:将Student类的nameage属性私有化,并为它们提供对应的get/set方法。
  22. ```python
  23. class Student:
  24. def __init__(self, name, age):
  25. self.__name = name
  26. self.__age = age
  27. def get_name(self):
  28. return self.__name
  29. def set_name(self, name):
  30. self.__name = name
  31. def get_age(self):
  32. return self.__age
  33. def set_age(self, age):
  34. self.__age = age
  35. # 实例化并访问私有化属性
  36. stu = Student("小明", 18)
  37. print(stu.get_name(), stu.get_age()) # 小明 18
  38. # 修改私有化属性
  39. stu.set_name("小黑")
  40. stu.set_age(25)
  41. print(stu.get_name(), stu.get_age()) 小黑 25

1.3 动态属性

1.3.1 对象动态增加属性

  • Python是一门动态的语言,所谓动态语言,就是指程序在运行的时候,可以改变数据结构的语言。
  • 补充知识点:
    • 构造对象的时候,解释器为对象分配一个字典,来存储对象的特征信息(属性与属性值的键值对)。
    • 可以通过对象.__dict__获取该字典。
  • 在Python中,对象可以动态的增加属性:对象.属性名 = 值 ```python class Computer: def init(self, brand, memory, hard_disk):

    1. """
    2. :param brand: 品牌
    3. :param memory: 内存
    4. :param hard_disk: 硬盘
    5. """
    6. self.__brand = brand
    7. self.__memory = memory
    8. self.__hard_disk = hard_disk

    def repr(self):

    1. return f"Computer(brand={self.__brand}, memory={self.__memory}, hard_disk={self.__hard_disk})"

macbook = Computer(“苹果”, “8G”, “256G”) print(macbook) # Computer(brand=苹果, memory=8G, harddisk=256G) print(macbook._dict) # {‘_Computerbrand’: ‘苹果’, ‘_Computermemory’: ‘8G’, ‘_Computer__hard_disk’: ‘256G’}

print(macbook.brand) # 试图访问私有化属性品牌,发现会报错

macbook.brand = “小米” # 试图修改品牌属性,发现并没有报错。 print(macbook) # Computer(brand=苹果, memory=8G, harddisk=256G),但self.brand依旧是苹果。 print(macbook.brand) # 小米,再访问品牌属性,发现并没有报错,并且值是小米。 print(macbook.dict_) # {‘_Computerbrand’: ‘苹果’, ‘_Computermemory’: ‘8G’, ‘_Computer__hard_disk’: ‘256G’, ‘brand’: ‘小米’},但字典中增加了键值对’brand’: ‘小米’

使用动态属性还可以再增加其他属性

macbook.externalscreen = “AOC” macbook.mouse = “logitech” print(macbook._dict) # {‘_Computerbrand’: ‘苹果’, ‘_Computermemory’: ‘8G’, ‘_Computer__hard_disk’: ‘256G’, ‘brand’: ‘小米’, ‘external_screen’: ‘AOC’, ‘mouse’: ‘logitech’}

  1. - 注意:动态属性是针对于具体对象而言的,如现在再创建一个新的Computer对象thinkpad,发现在thinkpad`__dict__`里并没有macbook动态增加的brandexternal_screenmouse这些属性。
  2. ```python
  3. thinkpad = Computer("联想", "32G", "1T")
  4. print(thinkpad.__dict__) # {'_Computer__brand': '联想', '_Computer__memory': '32G', '_Computer__hard_disk': '1T'}

1.3.2 私有属性并非将属性私有化

  • 从1.3.1的程序中的前两次print(macbook.__dict__)可以看出,第19行被注释掉的代码print(macbook.brand)尝试访问brand属性会报错是因为macbook.__dict__中并没有'brand': '苹果'这样的键值对。
  • 而Computer类中私有化的brand属性实际上是_Computer__brand属性,尝试访问这个属性,发现获取到的值真的是“苹果”。

    • 另外两个私有化属性也是一样的道理:self.memory体现为_Computermemory、self.hard_disk体现为_Computerhard_disk。
    • 这些属性都是可以直接访问的。
      1. print(macbook._Computer__brand) # 苹果
  • 由此可以得出,Python在语法上没有严格的私有化属性,只是给私有化的属性和方法换了个名字(原属性/方法名 => 类名属性/方法名),因此用原来的名字就无法访问到指定的结构了。

  • 总结:Python并没有像Java那样的强私有化,只要知道了Python私有化是怎么改变量名的,就还是可以访问到被私有化的结构,因此私有化在Python中实际上不常用。

    • 扩展:Python的设计者认为大家都是成年人了,自己写的代码应该由自己负责,而不是由Python语法来强制约束什么东西。
    • 因此如果要私有化变量,就要自己遵守私有化的规则,哪怕语法上有捷径可寻,也不要写不对的代码。

      1.3.3 限制动态属性

  • 在类列结构中直接声明__slots__可以限制动态属性的实现,变量__slots__的值就是由所有属性名组成的元组。

    1. class Computer:
    2. __slots__ = ("__brand", "__memory", "__hard_disk")
    3. def __init__(self, brand, memory, hard_disk):
    4. """
    5. :param brand: 品牌
    6. :param memory: 内存
    7. :param hard_disk: 硬盘
    8. """
    9. self.__brand = brand
    10. self.__memory = memory
    11. self.__hard_disk = hard_disk
    12. def __repr__(self):
    13. return f"Computer(brand={self.__brand}, memory={self.__memory}, hard_disk={self.__hard_disk})"
  • 声明__slots__后动态属性就不能用了。

    1. macbook = Computer("苹果", "8G", "256G")
    2. macbook.brand = "小米" # 尝试动态属性报错,AttributeError: 'Computer' object has no attribute 'brand'
  • __slots__实际上是在创建对象的时候将分配给对象的字典改成不可变容器(元素),由于元组是不能增加数据的,因此动态属性也就失效了。

    • 同等长度下,元组开辟的内存空间比字典小。
    • 因此声明了__slots__后的内存占用会比声明之前小。
  • 注意:__slots__限制之后__dict__就不能在用了。

    1. print(macbook.__dict__) # 报错:AttributeError: 'Computer' object has no attribute '__dict__'

    1.4 方法属性化

    1.4.1 方法属性化的基本概念

  • 方法属性化是基于私有化,并且提供了get/set方法基础上实现的。

  • 要访问私有化的数据,就需要调用get/set方法,而方法属性化就是要让调用get/set方法看起来像使用一个普通属性一样。
  • 之前访问私有化属性都使用get_file()/set_file(value)方法: ```python class Person: slots = (“name”, “__age”)

    def init(self, name, age):

    1. self.name = name
    2. self.__age = age

    def get_age(self):

    1. return self.__age

    def set_age(self, age):

    1. if age <= 0:
    2. raise ValueError("年龄不能小于0")
    3. else:
    4. self.__age = age

构造对象

lele = Person(“乐乐”, 18)

修改与获取普通属性的值

lele.name = “大乐” name = lele.name print(name) # 大乐

修改与获取私有化属性的值

lele.set_age(21) age = lele.get_age() print(age) # 21

  1. - 而方法属性化就是用`obj.get_file`获取对象的私有化属性(即将get_file()的小括号去掉,让它变成属性的形式)。
  2. - 同理,用`obj.file = value`的方式给私有化属性赋值(代替`obj.set_file(value)`的形式)。
  3. <a name="blaCj"></a>
  4. #### 1.4.2 @property装饰get方法
  5. - 在高阶函数中有提到,`obj.get_file`本质是`get_file`这个函数本身,`get_file()`才是调用这个方法。
  6. ```python
  7. print(lele.get_age) # <bound method Person.get_age of <__main__.Person object at 0x000002074ED9EFD0>>
  8. print(lele.get_age()) # 18
  • 若要让obj.get_file实现obj.get_file()的功能,就要在get方法的定义前使用@property装饰器。 ```python class Person: slots = (“name”, “__age”)

    def init(self, name, age):

    1. self.name = name
    2. self.__age = age

    @property def get_age(self):

    1. return self.__age

    def set_age(self, age):

    1. if age <= 0:
    2. raise ValueError("年龄不能小于0")
    3. else:
    4. self.__age = age

lele = Person(“乐乐”, 18) print(lele.get_age) # 18

print(lele.get_age()) # 报错:TypeError: ‘int’ object is not callable

意味着方法被@property修饰后,就变成一个属性了。

  1. - 为了让其看起来更像是在调用属性,可用把`get_file`这个函数名就改成`file`
  2. ```python
  3. class Person:
  4. __slots__ = ("name", "__age")
  5. def __init__(self, name, age):
  6. self.name = name
  7. self.__age = age
  8. @property
  9. def age(self):
  10. return self.__age
  11. def set_age(self, age):
  12. if age <= 0:
  13. raise ValueError("年龄不能小于0")
  14. else:
  15. self.__age = age
  16. lele = Person("乐乐", 18)
  17. print(lele.age) # 18,看起来更像是在调用属性,但实际是在调用age()方法。

1.4.3 @get方法名.setter装饰set方法

  • 普通属性用等号赋值,私有化属性用set方法赋值。

    1. lele.name = "大乐"
    2. lele.set_age(21)
  • set方法属性化后就可以用obj.set_file = value的形式给私有化属性赋值。

  • 同样的,set方法属性化也需要使用装饰器装饰set方法,这个装饰器为:@get方法名.setter。 ```python class Person: slots = (“name”, “__age”)

    def init(self, name, age):

    1. self.name = name
    2. self.__age = age

    @property def age(self):

    1. return self.__age

    @age.setter def set_age(self, age):

    1. if age <= 0:
    2. raise ValueError("年龄不能小于0")
    3. else:
    4. self.__age = age

lele = Person(“乐乐”, 18) lele.set_age = 21 print(lele.age) # 21

  1. - 同理,为了让其看起来更像是在调用属性,可用把`set_file`这个函数名就改成`file`
  2. - 根据装饰器是函数嵌套的本质,因此两个`file`方法实际上并不冲突。
  3. ```python
  4. class Person:
  5. __slots__ = ("name", "__age")
  6. def __init__(self, name, age):
  7. self.name = name
  8. self.__age = age
  9. @property
  10. def age(self):
  11. return self.__age
  12. @age.setter
  13. def age(self, age):
  14. if age <= 0:
  15. raise ValueError("年龄不能小于0")
  16. else:
  17. self.__age = age
  18. lele = Person("乐乐", 18)
  19. lele.age = 21
  20. print(lele.age) # 21
  21. """
  22. 解释器会更具有无等号自动判断调用哪个age方法。
  23. 有等号的就是set,调用@age.setter修饰的age方法。
  24. 没有等号的就是get,调用@property修饰的age方法。
  25. """

02. 继承

2.1 继承的简介

2.1.1 继承的基本概念

  • 继承是指从两个或两个以上的相关普通类(继承中称之为子类、衍生类)中提取出共同的特征和行为到一个通用类(继承中称之为父类、超类、基类)中,并通过指定的声明形式,使得子类可以使用父类中的信息,这种格式就被称之为继承。
  • 子类和父类之间的关系:子类继承自父类,父类派生出子类。
  • 只要子类和父类之间设置好联系,那么子类构造的对象就可以直接继承使用父类中的所有信息。

    2.1.2 继承的基本语法

  • 继承的基本语法: ```python class 通用父类: def init(self): # 用于实现通用属性声明以及其他相关的通用初始化操作

    def 通用方法(self): # 定义那些通用的方法

class 子类(通用父类): def init(self): # 定义子类中特有的属性和初始化操作 通用父类.init(self, 父类属性) # 执行父类中的初始化操作。 子类初始化操作

  1. def 子类中特有的方法(self):
  1. - 示例:定义一个学生类和一个教师类,然后后继承的形式改写这两个类。
  2. - 学生类:属性:学号、姓名、年龄、性别、成绩;行为:吃饭、学习。
  3. - 教师类:属性:教师编号、姓名、年龄、性别、职称;行为:吃饭、教学。
  4. ```python
  5. class Student:
  6. def __init__(self, id, name, age, gender, score):
  7. self.id = id
  8. self.name = name
  9. self.age = age
  10. self.gender = gender
  11. self.score = score
  12. def eat(self):
  13. print(f"{self.name}在吃饭")
  14. def study(self):
  15. print(f"{self.name}在学习")
  16. def __repr__(self):
  17. return f"Student(id={self.id}, name={self.name}, age={self.age}, gender={self.gender}, score={self.score})"
  18. class Teacher:
  19. def __init__(self, id, name, age, gender, title):
  20. self.id = id
  21. self.name = name
  22. self.age = age
  23. self.gender = gender
  24. self.title = title # 职称
  25. def eat(self):
  26. print(f"{self.name}在吃饭")
  27. def teach(self):
  28. print(f"{self.name}在教学")
  29. def __repr__(self):
  30. return f"Teacher(id={self.id}, name={self.name}, age={self.age}, gender={self.gender}, title={self.title})"
  31. student = Student(1001, "小明", 18, "男", 98)
  32. teacher = Teacher(1001, "张老师", 25, "女", "语文老师")
  33. print(student) # Student(id=1001, name=小明, age=18, gender=男, score=98)
  34. print(teacher) # Teacher(id=1001, name=张老师, age=25, gender=女, title=语文老师)
  • 可以发现,属性:id、name、age、gender和eat方法在两个类中都存在,因此可以把这部分内容提取到一个父类Person中,然后让Student、Teacher这两个类继承Person类即可。 ```python class Person: def init(self, id, name, age, gender): self.id = id self.name = name self.age = age self.gender = gender

    def eat(self): print(f”{self.name}在吃饭”)

class Student(Person): def init(self, id, name, age, gender, score): Person.init(self, id, name, age, gender) # 执行父类中的初始化操作 self.score = score

  1. def study(self):
  2. print(f"{self.name}在学习")
  3. def __repr__(self):
  4. return f"Student(id={self.id}, name={self.name}, age={self.age}, gender={self.gender}, score={self.score})"

class Teacher(Person): def init(self, id, name, age, gender, title): Person.init(self, id, name, age, gender) # 执行父类中的初始化操作 self.title = title # 职称

  1. def teach(self):
  2. print(f"{self.name}在教学")
  3. def __repr__(self):
  4. return f"Teacher(id={self.id}, name={self.name}, age={self.age}, gender={self.gender}, title={self.title})"

student = Student(1001, “小明”, 18, “男”, 98) teacher = Teacher(1002, “张老师”, 25, “女”, “语文老师”) print(student) # Student(id=1001, name=小明, age=18, gender=男, score=98) print(teacher) # Teacher(id=1001, name=张老师, age=25, gender=女, title=语文老师)

  1. <a name="U7GGD"></a>
  2. ### 2.2 三种父类初始化的方式
  3. - 子类会自动拥有父类中声明的所有方法,但是无法直接给继承自父类的属性进行赋值,因此需要在子类的__init__()方法中初始化父类。
  4. - 在子类中初始化父类有三种形式:`父类名.__init__()`、`super(子类类名, 子类对象).__init__()`、`super().__init__()`。
  5. <a name="MmGU6"></a>
  6. #### 2.2.1 父类名.__init__()
  7. - 在子类的`__init__()`首行,使用`父类名.__init__()`可以初始化父类的结构(注意这里是用父类名调用__init__(),因此需要手动给self传值指定对象)。
  8. ```python
  9. def __init__(self, id, name, age, gender, score):
  10. Person.__init__(self, id, name, age, gender) # 执行父类中的初始化操作
  11. self.score = score

2.2.2 super关键字

  • Python中的super关键字代表着父类,可以利用super创建父类对象,调用父类的__init__()
  • super关键字的语法结构:super(子类类名, 子类对象).__init__()

    • 一个父类可以拥有很多子类,需要标记哪个子类调用了super。
    • 一个子类可以拥有很多对象,需要标记给哪个对象设置参数。
      1. def __init__(self, id, name, age, gender, score):
      2. super(Student, self).__init__(id, name, age, gender)
      3. self.score = score

      2.2.3 省略版的super关键字

  • 用super构造对象时,Python会默认将当前子类以及当前对象对象传递过去。

  • 因此super()中的子类类名和子类对象可以不写。

    1. def __init__(self, id, name, age, gender, score):
    2. super().__init__(id, name, age, gender)
    3. self.score = score
  • 这种方式是在日常开发中最常用的方式。

    2.3 方法重写

    2.3.1 方法重写的概念

  • 方法重写是指子类继承父类后,会获得父类中所有结构(包括方法)。但子类中有些方法可能实现起来与父类中的方法不一样,此时就可以使用方法重写来修改逻辑。

  • 方法重写的两种场景:
    • 子类与父类中方法的实现完全不一致。
    • 子类方法中的实现与父类中部分一致。(在子类方法体中写子类特有的逻辑,然后在相同的部分中调用父类的方法即可)
  • 方法重写的语法格式:

    • 方法的声明需要与父类中完全一致(即方法名、形参列表需要保持完全一样)。
    • 方法的方法体(具体功能的是实现)可以重新编写。
      1. def 父类中的方法名(self, 父类中的):
      2. 方法体:子类中重写的逻辑
  • 当子类重写父类中的方法后,从内存的角度来看,是子类中重写后的内容将从父类中继承过来的内容覆盖了。

  • 因此用子类对象来进行调用的时,调用的是子类中重写的方法,父类中的结构已经不存在了,故无法直接用对象调用父类中未重写的方法,只能通过super().方法名()的形式调用。

    2.3.2 方法重写的应用案例

  • 需求描述:

    • 在愤怒的小鸟游戏中有许多不同颜色的小鸟,不同颜色的小鸟都有攻击的能力,但是攻击的具体表现行为是不一样的。
      • 红鸟:特征【昵称】,行为【攻击(技能为死幢)】。
      • 蓝鸟:特征【昵称】,行为【攻击(技能为分身、死撞)】。
      • 黄鸟:特征【昵称】,行为【攻击(技能为加速、死撞)】。
      • 黑鸟:特征【昵称】,行为【攻击(技能为爆炸)】。
    • 要求以继承和方法重写的形式实现这个需求。
  • 代码实现: ```python

    所有鸟的父类

    class Bird: def init(self, name):

    1. self.name = name

    def attack(self):

    1. print("死撞")

class RedBrid(Bird):

  1. # 因为红鸟中的结构与父类Bird中结构完全一样。
  2. # 因此只需要继承Bird类即可,不需要声明其他东西。
  3. pass

class BlueBird(Bird):

  1. # 蓝鸟的属性与父类Bird中的属性完全一样,因此这里不需要再写__init__()方法。
  2. def attack(self):
  3. print("分身", end=",")
  4. super().attack() # 与父类Bird中的attack()有相同的部分,因此直接调用即可。

class YellowBird(Bird): def attack(self): print(“加速”, end=”,”) super().attack()

class BlackBird(Bird): def attack(self): print(“爆炸”)

  1. # 因为BlackBird子类的attack()与父类Bird中的attack()完全不一样。
  2. # 因此这里只需要重写BlackBird子类的attack()中特有的逻辑即可,不需要调用父类中的方法。

rd = RedBrid(“愤怒红”) rd.attack() # 死撞

bd = BlueBird(“蓝弟弟”) bd.attack() # 分身,死撞

yb = YellowBird(“飞镖黄”) yb.attack() # 加速,死撞

bb = BlackBird(“炸弹黑”) bb.attack() # 爆炸

  1. <a name="TWNy8"></a>
  2. ### 2.4 object类
  3. <a name="RznMw"></a>
  4. #### 2.4.1 __base__父类与mro()继承体系
  5. - `类名.__base__`可以查看到指定类的父类(若一个类有多个直接父类,则使用`类名.__bases__`进行查看)。
  6. - `类名.mro()`可以查看到一个类的继承体系(包括当前类、父类、爷爷类、太爷爷类、……)。
  7. ```python
  8. print(BlackBird.__base__) # <class '__main__.Bird'>
  9. print(BlackBird.mro()) # [<class '__main__.BlackBird'>, <class '__main__.Bird'>, <class 'object'>]

2.4.2 object根父类

  • object类是所有类的根类(也被称为顶级父类),即Python中的所有类都直接或间接继承自object类。
  • 当定义一个类并且没有声明继承结构时,它默认就是继承自object类的。 ```python class Test: pass

print(Test.base) #

  1. - `__eq__``__init__``__dict__`等这些常用的方式都是定义在object类中的,因此Python所有的对象都可以直接使用这些方法,也可以根据实际需求重写这些方法。
  2. <a name="MCk6M"></a>
  3. ### 2.5 多继承
  4. - Python是支持多继承的,即一个子类可以有多个直接父类,子类会拥有多有父类中的所有结构。
  5. - 注意:多继承在子类的`__init__()`方法中初始化父类中的结构时只能使用`父类名.__init__()`这种方式。
  6. - 因为`super(子类类名, 子类对象).__init__()`这种方式只会将值传给第一个父类,即这里的A;第二个父类B拿不到值,无法进行初始化。
  7. ```python
  8. class A(object):
  9. def __init__(self, a):
  10. self.a = a
  11. def show_a(self):
  12. print(f"a={self.a}")
  13. class B(object):
  14. def __init__(self, b):
  15. self.b = b
  16. def show_b(self):
  17. print(f"b={self.b}")
  18. class C(A, B): # 继承类A和类B
  19. def __init__(self, a, b, c):
  20. A.__init__(self, a) # 初始化第一个父类A的结构
  21. B.__init__(self, b) # 初始化第二个父类B的结构
  22. self.c = c # 初始化自己特有的结构
  23. def show_c(self):
  24. print(f"c={self.c}")
  25. c = C(10, 20, 30)
  26. c.show_a() # a=10
  27. c.show_b() # b=20
  28. c.show_c() # c=30
  • 使用__bases__可以查看所有的直接父类。

    1. print(C.__bases__) # (<class '__main__.A'>, <class '__main__.B'>)

    03. 多态

  • 多态是指事物根据不同的赋值呈现出不同的形态。

  • Python本身就是一个动态类型语言,给变量赋予什么值,变量就是什么类型的,这就是多态。
  • 因此由于Python的语言特性,多态在Python中其实并没有什么特殊的点。