python学习day07

4. 继承/派生

  • 什么是继承/派生

    • 继承是从已有的类中派生出新的类,新类具有原类的数据属性和行为,并能扩展新的能力。

    • 派生类就是从一个已有类中衍生出新类,在新的类上可以添加新的属性和行为

  • 为什么继承/派生

    • 继承的目的是延续旧的类的功能

    • 派生的目地是在旧类的基础上添加新的功能

  • 继承/派生的作用

    • 用继承派生机制,可以将一些共有功能加在基类中。实现代码的共享。

    • 在不改变基类的代码的基础上改变原有类的功能

  • 继承/派生名词:

    • 基类(base class)/超类(super class)/父类(father class)

    • 派生类(derived class)/子类(child class)

4.1 单继承

  • 单继承的语法:

    class 类名(基类名):
        语句块

    单继承说明:单继承是指派生类由一个基类衍生出来的

 

4.2 多继承

Python支持多继承形式。多继承的类定义形如下例:

class DerivedClassName(Base1, Base2, Base3):
    <statement-1>
    .
    .
    .
    <statement-N>

需要注意圆括号中父类的顺序,若是父类中有相同的方法名,而在子类使用时未指定,python从左至右搜索 即方法在子类中未找到时,从左到右查找父类中是否包含方法。

4.3 覆盖 override

  • 覆盖是指在有继承关系的类中,子类中实现了与基类同名的方法,在子类的实例调用该方法时,实际调用的是子类中的覆盖版本,这种现象叫覆盖

  • 作用:

    • 实现和父类同名,但功能不同的方法

  • v
  • 5. 封装 enclosure

    封装(Encapsulation) 是一种将数据(属性)和操作数据的方法(方法)绑定在一起的机制。封装的主要目的是隐藏对象的内部状态,并对外部提供有限的访问接口。通过封装,可以防止外部代码直接访问对象的内部数据,从而提高代码的安全性和可维护性。

    在 Python 中,封装主要通过以下几种方式实现:

    1. 私有属性(Private Attributes)

    在 Python 中,没有严格的私有属性,但可以通过命名约定来模拟私有属性。通常,使用单下划线 _ 或双下划线 __ 来表示私有属性。

    单下划线 _

    单下划线前缀表示属性是“受保护的”,建议外部代码不要直接访问。

    单下划线前缀 _ 是一种约定,而不是语言层面的强制规则。这种约定并没有强制性,外部代码仍然可以直接访问这些属性或方法。

  • 示例中虽然_protected_attr被标记为受保护的属性,但还是可以直接访问和修改。

    双下划线 __

    双下划线前缀表示属性是“私有的”,Python 会对其进行名称改写(name mangling),使其在类外部难以直接访问。

    示例运行会报错:

    AttributeError: 'MyClass' object has no attribute '__private_attr'

    这是因为私有变量被python修改了名称,名称改写的规则是将属性名前面加上类名和一个下划线 _。假设类 MyClass,其中有一个私有属性 __private_attr,那么 Python 会将这个属性名改写为 _MyClass__private_attr

    ​​​​​​​

    2. 属性访问器(Getter 和 Setter)

    单下划线和双下划线的变量虽然可以直接或间接被访问,但python不建议这么做。

    如果需要访问或修改这些属性,应该通过类提供的公共方法gettersetter ,来控制对属性的访问和修改。

    ​​​​​​​
  • 3. 使用 @property 装饰器

    Python 提供了 @property 装饰器,可以更简洁地实现属性的访问和修改。

    @property是 Python 中的一个装饰器,用于将类的方法转换为属性。@property 装饰器用于定义一个只读属性。通过使用 @property,可以将方法的调用方式转换为属性的访问方式,从而使代码更加简洁和易读。

    如果需要设置属性的值,可以使用 @property.setter 装饰器定义一个可写属性

    @property 通常与 @<property_name>.setter 一起使用,以实现属性的读取和写入操作。

    ​​​​​​​
  • 6. 多态 polymorphic

    多态(Polymorphism)是指同一个方法在不同的类中有不同的实现。多态可以分为两种主要类型:静态多态(Static Polymorphism)和动态多态(Dynamic Polymorphism)。

    6.1 静态多态

    静态多态也称为编译时多态(Compile-time Polymorphism),主要通过以下两种方式实现:

    6.1.1 方法重载

    方法重载是指在同一个类中定义多个同名但参数不同的方法。编译器根据调用时传递的参数类型和数量来决定调用哪个方法。

    ​​​​​​​
  • 6.1.2 运算符重载

    运算符重载(Operator Overloading) 是指通过定义类的魔术方法(Magic Methods),使类的对象支持 Python 的内置运算符(如 +-*/ 等)。通过运算符重载,我们可以让自定义类的对象像内置类型(如整数、字符串、列表等)一样使用运算符。

    ​​​​​​​
  • 6.2 动态多态

    动态多态也称为运行时多态(Runtime Polymorphism),主要通过方法重写(Method Overriding)实现。

    6.2.1 方法重写

    方法重写是指子类重新定义父类中已有的方法,从而在运行时根据对象的实际类型来调用相应的方法。

    ​​​​​​​
  • 6.2.2 鸭子类型

    Python 是一种动态类型语言,支持鸭子类型(Duck Typing)。鸭子类型的核心思想是“如果它走起来像鸭子,叫起来像鸭子,那么它就是鸭子”。这意味着只要对象实现了所需的方法,就可以在代码中使用它,而不需要显式地继承某个基类。

    6.2.3 抽象基类

    Python 提供了 abc 模块,用于定义抽象基类。抽象基类不能直接实例化,而是作为其他类的模板。子类必须实现抽象基类中定义的抽象方法,否则会引发错误。

    使用 abc 模块的基本步骤

  • 导入模块:导入 abc 模块中的 ABCabstractmethod

  • 定义抽象基类:通过继承 ABC 类来定义抽象基类。

  • 定义抽象方法:使用 @abstractmethod 装饰器标记抽象方法。

  • 实现子类:子类必须实现抽象基类中定义的所有抽象方法,否则无法被实例化。

7. 内建函数重写(了解)

在自定义类内添加相应的方法,让自定义类创建的实例像内建对象一样进行内建函数操作。

然而,这样做通常是不推荐的,因为这可能会导致代码的可读性和可维护性降低,并且可能会引发意外的行为。

对象转字符串函数重写

对象转字符串函数重写方法

  • str() 函数的重载方法:

    • def __str__(self)

      • 如果没有 __str__(self) 方法,则返回repr(obj)函数结果代替

str函数重写示例

class MyNumber:
    "此类用于定义一个自定义的类,用于演示str函数重写"
    def __init__(self, value):
        "构造函数,初始化MyNumber对象"
        self.data = value
    def __str__(self):
        "转换为普通字符串"
        return "%s" % self.data
​
n1 = MyNumber("一只猫")
n2 = MyNumber("一只狗")
print("str(n2) ===>", str(n2))
内建函数重写
  • __abs__ abs(obj) 函数调用

  • __len__ len(obj) 函数调用

  • __reversed__ reversed(obj) 函数调用

  • __round__ round(obj) 函数调用

  • 内建函数 重写示例

    # file : len_overwrite.py
    class MyList:
        def __init__(self, iterable=()):
            self.data = [x for x in iterable]
        def __repr__(self):
            return "MyList(%s)" % self.data
        def __len__(self):
            print("__len__(self) 被调用!")
            return len(self.data)
        def __abs__(self):
            print("__len__(self) 被调用!")
            return MyList((abs(x) for x in self.data))
    ​
    myl = MyList([1, -2, 3, -4])
    print(len(myl))
    print(abs(myl))

8. super函数

8.1 MRO查找顺序

MRO(Method Resolution Order,方法解析顺序)描述了 Python 在多重继承的情况下,如何查找类的方法和属性。MRO 决定了当一个类继承自多个父类时,Python 如何确定方法和属性的查找顺序。

MRO 的计算规则

Python 使用 C3 线性化算法来计算 MRO。C3 线性化算法确保了以下几点:

子类优先于父类:子类的方法或属性会优先于父类被查找。

父类的顺序保持不变:在定义类时,父类的顺序会影响 MRO。在多重继承中,按照继承列表的顺序从左到右查找。

单调性:如果一个类出现在另一个类的 MRO 中,那么它的父类也应该出现在这个类的 MRO 中。

MRO 的计算方法

MRO 的计算方法可以通过 __mro__ 属性或 mro() 方法来查看。

    8.2 super()

    super() 函数是用于调用父类(超类)的一个方法。

    super() 是用来解决多重继承问题的,直接用类名调用父类方法在使用单继承的时候没问题,但是如果使用多继承,会涉及到查找顺序(MRO)、重复调用(钻石继承)等种种问题。

    super() 是一个用于调用父类方法的函数,它依赖于 Python 的 MRO 机制。因此,super() 本身并不能解决 MRO 冲突,而是依赖于已经确定的 MRO。

    super() 解决多继承问题的关键在于 MRO(方法解析顺序),即 Python 按照某种顺序(通常是从左到右)依次调用继承链中的方法。super() 总是会调用下一个类的 process(),而不会重复调用同一个类的方法。

    使用 super() 时,Python 会动态查找类继承链中的下一个类。通过 MRO,避免了手动调用时可能发生的重复调用。

      评论
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

      当前余额3.43前往充值 >
      需支付:10.00
      成就一亿技术人!
      领取后你会自动成为博主和红包主的粉丝 规则
      hope_wisdom
      发出的红包
      实付
      使用余额支付
      点击重新获取
      扫码支付
      钱包余额 0

      抵扣说明:

      1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
      2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

      余额充值