Python升级之路( Lv7 ) 面向对象深入

Python系列文章目录

第一章 Python 入门
第二章 Python基本概念
第三章 序列
第四章 控制语句
第五章 函数
第六章 面向对象基础
第七章 面向对象深入


前言

在本章我们首先学习了面向对象的三大特征, 继承, 封装和多态. 主要讲述了继承和多态:
继承某个父类, 可以实现并重写父类方法, 并且所有类都继承父类Object, 因此可以使用很多Object提供的特殊方法和特殊属性;
而多态指的的是不同子对象在调用父类方法时会有不同体现, 核心是: 继承和方法重写. 多态的出现也符合面向对象的"开闭原则"
封装之所以没讲是因为我们经常会用到, 因为在我们日常编程中, 将一些具有特定功能的代码块封装成方法/函数这种行为正体现了封装思想
然后学习了深拷贝和浅拷贝: 浅拷贝后对象的地址改变, 但子对象地址不改变. 深拷贝: 拷贝对象的地址改变, 子对象地址也改变
再然后我们了解编程中常用的工厂模式, 单例模式以及他们的组合, 最后通过实操来回顾下本章学习的内容


一、面向对象三大特征

Python是面向对象的语言
面向对象编程具有三大特性:继承、封装(隐藏)、多态

具体含义如下

  • 继承: 继承可以让子类具有父类的特性,提高了代码的重用性
    从设计上是一种增量进化,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法
  • 封装(隐藏): 隐藏对象的属性和实现细节,只对外提供必要的方法
    相当于将“细节封装起来”,只对外暴露“相关调用方法”
    通过前面学习的“私有属性、私有方法”的方式,实现“封装”.
    Python追求简洁的语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现
  • 多态是指同一个方法调用由于对象不同会产生不同的行为
    例如: 同样是休息方法,人不同休息方法不同: 张三休息是睡觉,李四休息是玩游戏,程序员休息是“敲几行代码”

继承

继承是面向对象编程的三大特征之一. 继承让我们更加容易实现类的扩展. 实现代码的重用,不用再重新发明轮子

语法格式

class  子类类名(父类1[,父类2...]):
 类体

注意
如果在类定义中没有指定父类,则默认父类是 object类.
也就是说 object 是所有类的父类,里面定义了一些所有类共有的默认实现,比如: __new__()

关于构造函数:

  • 子类不重写 __init__ 时,实例化子类,会自动调用父类定义的 __init__
  • 子类重写了 __init__ 时,实例化子类,就不会调用父类已经定义的 __init__
  • 如果重写了 __init__ 时,要使用父类的构造方法,可以使用 super 关键字,也可以使用如下格式调用: 父类名.__init__(self, 参数列表)

实操代码

import copy

print("======================测试继承===========================")


class Person:
    def __init__(self, name, age):
        print("Person构造方法")
        self.name = name
        self.age = age

    def print_age(self):
        print("姓名是: {0}, 年龄是: {1}".format(self.name, self.age))


# 这种方式相当于继承, 这里指的是Student继承Person
class Student(Person):
    """
    关于构造函数:
    子类不重写 __init__ ,实例化子类时,会自动调用父类定义的 __init__ 。
    子类重写了 __init__ 时,实例化子类,就不会调用父类已经定义的 __init__
    如果重写了 __init__ 时,要使用父类的构造方法,可以使用 super 关键字,也可以使用如下格式调用: 父类名.__init__(self, 参数列表)
    """

    def __init__(self, name, age, score):
        print("Student的构造方法")
        # 方式一: 通过类名.__init__重写父类构造
        Person.__init__(self, name, age)
        # 方式二: 通过super重写父类构造
        # super(Student, self).__init__(name, age)
        # self.name = name
        # self.age = age
        self.score = score


# s1 = Student("TimePause", 18)   # 子类没有重写构造方法可以这样调用
s1 = Student("TimePause", 18, 149)
s1.print_age()
print(dir(s1))

super()获得父类定义

在子类中,如果想要获得父类的方法时,我们可以通过 super() 来做. super() 代表父类的定义,不是父类对象
调用父类的构造方法: super(子类名称,self).__init__(参数列表)

print("=====================super()获得父类定义==============================")


class A:
    def __init__(self):
        print("A的构造方法")

    def say(self):
        print(self)
        print("I said, no one speaks better than me")


class B(A):
    def __init__(self):
        super(B, self).__init__()  # 调用父类的构造方法
        print("B的构造方法")

    def say(self):
        A.say(self)  # 1.调用父类的say方法
        # super().say()  # 2.通过super 调用父类的方法
        print("bb")


b1 = B()
b1.say()

object 父类

object 类是所有类的父类,因此所有的类都有 object 类的属性和方法

dir() 查看对象属性

而内置函数 dir() : 可以让我们方便的看到指定对象所有的属性

【实操】查看对象所有属性以及和 object 进行比对

print("==========================object根类============================")
print("========================dir() 查看对象属性============================")


class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def print_age(self):
        print(self.name, "的年龄是:", self.age)


obj = object()
print(dir(obj))
s2 = Person("时间静止", 18)
print(dir(s2))

从上面代码在控制台的输出我们可以发现这样几个要点:

  1. Person 对象相较于object, 增加了六个属性: __dict__ , __module__ , __weakref__ , age , name , print_age

  2. object 的所有属性, Person 类作为 object 的子类,显然包含了所有的属性

  3. 我们打印 age 、 name 、 print_age ,发现 print_age虽然是方法,实际上也是属性。只不过,这个属性的类型是 method 而已

    age <class 'int'>
    name <class 'str'>
    print_age <class 'method'>
    

mro() 获取类的层次结构

Python支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将“从左向右”按顺序搜索
MRO(Method Resolution Order):方法解析顺序. 我们可以通过 mro() 方法获得“类的层次结构”,方法解析顺序也是按照这个“类的层次结构”寻找的

实操代码

print("============================MRO方法解析顺序=========================")


class A:
    def aa(self):
        print("aa")

    def say(self):
        print("I said, no one speaks better than me")


class B:
    def bb(self):
        print("bb")


class C(A, B):
    def cc(self):
        print("cc")


c1 = C()
"""打印类的层次结构"""
print(C.mro())
""" 解释器寻找方法是“从左到右”的方式寻找,此时会执行B类中的say()"""
c1.say()

运行结果
在这里插入图片描述

特殊方法和特殊属性

除了上面方法,Object 父类还定义了一些特殊方法, 用于对方法, 属性等进行相关操作

常用的特殊方法统计如下:
在这里插入图片描述

运算符重载

在python中, 每个运算符实际上都有Object相对应的方法

最常用的一些统计如下:
在这里插入图片描述
实操代码

print("================特殊方法和运算符重载========================")
"""Python的运算符实际上是通过调用对象的特殊方法实现的"""
a = 20
b = 30
c = a + b
d = a.__add__(b)
print("c=", c)
print("d=", d)
print("================测试运算符的重载========================")


class Person:
    def __init__(self, name):
        self.name = name

    def __add__(self, other):
        if isinstance(other, Person):
            return "{0}--{1}".format(other.name, self.name)
        else:
            return "不是对象不能相加"

    def __mul__(self, other):
        if isinstance(other, int):
            return self.name * other
        else:
            return "不是相同对象不能相乘"


p1 = Person("时间静止")
p2 = Person("TimePause")

print(p1 + p2)
print(p1 * 3)

多重继承

Python支持多重继承,一个子类可以有多个“直接父类”.
这样就具备了“多个父类”的特点. 但是这样会把“类的整体层次”搞的异常复杂,因此尽量避免使用

语法格式

class  子类类名(父类1,父类2[...]):
 类体

实操代码

print("============================多继承========================================")


class A:
    """
    Python支持多重继承,一个子类可以有多个“直接父类”。
    这样,就具备了“多个父类”的特点。但是由于,这样会被“类的整体层次”搞的异常复杂,尽量避免使用
    """

    def aa(self):
        print("aa")


class B:
    def bb(self):
        print("bb")


class C(A, B):	# 类C同时继承了A,B
    def cc(self):
        print("cc")


c1 = C()
c1.cc()
c1.bb()
c1.aa()

组合

结婚就是组合。两人组合后,可以复用对方的属性和方法

  • is-a 关系,我们可以使用“继承”. 从而实现子类拥有的父类的方法和属性.
    is-a 关系指的是类似这样的关系:狗是动物,dog is animal. 狗类就应该继承动物类
  • has-a 关系,我们可以使用“组合”,也能实现一个类拥有另一个类的方法和属性
    has-a 关系指的是这样的关系:手机拥有CPU. MobilePhone has a CPU

实操代码

print("===============================组合===============================")


class MobilePhone:
    def __init__(self, cpu, screen):
        self.cpu = cpu
        self.screen = screen


class CPU:
    def calculate(self):
        print("计算,算个12345")


class Screen:
    def show(self):
        print("显示一个好看的画面,亮瞎你的钛合金大眼")


c = CPU()
s = Screen()
m = MobilePhone(c, s) # 在创建对象时进行组合操作
m.cpu.calculate()  # 通过组合,我们也能调用cpu对象的方法。相当于手机对象间接拥有了“cpu的方法”
m.screen.show()  # 通过组合,我们也能调用screen对象的方法。相当于手机对象间接拥有了“screen的方法”

多态

多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为
现实生活中,同一个方法具体实现会完全不同. 比如:同样是调用人“吃饭”的方法,中国人用筷子吃饭,英国人用刀叉吃饭,印度人用手吃饭

注意:

  • 多态是方法的多态,属性没有多态
  • 多态的存在有2个必要条件:继承、方法重写

实操代码

print("=================================多态======================================")


class Animal:
    def shout(self):
        print("动物叫")


class Dog(Animal):
    def shout(self):
        print("小狗叫, 汪汪汪")


class Cat(Animal):
    def shout(self):
        print("小猫叫, 咪咪咪")


def animalShout(a):
    """传入的对象不同,shout方法对应的实际行为也不同"""
    a.shout()


animalShout(Dog())
animalShout(Cat())

二、浅拷贝与深拷贝

  • 浅拷贝: Python拷贝一般都是浅拷贝
    浅拷贝:拷贝时,拷贝源对象,但对象包含的子对象内容不拷贝
  • 深拷贝: 使用 copy 模块的 deepcopy 函数,递归拷贝对象中包含的子对象
    深拷贝:拷贝时,拷贝源对象,也递归拷贝对象中包含的子对象

如图所示:
在这里插入图片描述
实操代码

print("=======================对象的浅拷贝和深拷贝================================")


class MobilePhone:
    def __init__(self, cpu):
        self.cpu = cpu


class CPU:
    pass


c = CPU()
m = MobilePhone(c)
print("----浅拷贝: 拷贝对象的地址改变, 但子对象地址不改变-------")
m2 = copy.copy(m)  # m2是新拷贝的另一个手机对象
print("m:", id(m))
print("m2:", id(m2))
print("m的cpu:", id(m.cpu))
print("m2的cpu:", id(m2.cpu))  # m2和m拥有了一样的cpu对象
print("----深拷贝: 拷贝对象的地址改变, 子对象地址也改变--------")
m3 = copy.deepcopy(m)
print("m:", id(m))
print("m3:", id(m3))
print("m的cpu:", id(m.cpu))
print("m3的cpu:", id(m3.cpu))  # m3和m拥有不一样的cpu对象

三、常用设计模式

设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法
设计模式有很多种,比较流行的是:GOF(GoupOf Four)23种设计模式. 我们没有必要全部学习,只需学习几个常用的即可
对于初学者,我们学习两个最常用的模式:工厂模式和单例模式

工厂模式

工厂模式实现了创建者和调用者的分离,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制

注意事项

  • 工厂模式核心是: 工厂类和工厂方法
  • 工厂方法的核心逻辑是: 根据输入不同的内容, 去创建不同的类

实操代码

print("=======================工厂模式=========================")


class Benz:
    pass


class BMW:
    pass


class BYD:
    pass


class CarFactory:
    def createCar(self, brand):
        if brand == "奔驰":
            return Benz()
        elif brand == "宝马":
            return BMW()
        elif brand == '比亚迪':
            return BYD()
        else:
            return "未知品牌,无法创建"


factory = CarFactory()
c2 = factory.createCar("宝马")
c1 = factory.createCar("奔驰")
print(c2)
print(c1)

单例模式

单例模式(Singleton Pattern)的核心作用是确保一个类只有一个实例,并且提供一个访问该实例的全局访问点
单例模式只生成一个实例对象,减少了对系统资源的开销. 当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,
可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销
单例模式有多种实现的方式,我们这里推荐重写 __new__() 方法

注意事项

  • 单例模式核心是: 重写 __new__() 方法 ( __new__() 作用是定义并返回实例化的类, 执行顺序: new() -> init())
  • 单例模式的逻辑是:
    新建两个类属性, 用于表示是否创建过对象或者是否进行初始化
    重写 __new__() 方法, 如果没有创建对象, 则通过 object.__new__(cls) 创建该类对象. 否则执行下一步
    重写 __init__() 方法, 用于初始化该类, 并初始化类属性

实操代码

print("==========================单例模式===================================")


class MySingleton:
    __obj = None
    __init_flag = True

    def __new__(cls, *args, **kwargs):
        if cls.__obj is None:
            cls.__obj = object.__new__(cls)
        return cls.__obj

    def __init__(self, name):
        if MySingleton.__init_flag:
            print("init....")
            self.name = name
            MySingleton.__init_flag = False


a = MySingleton("aa")
print(a)
b = MySingleton("bb")
print(b)

运行结果

在这里插入图片描述

工厂和单例模式组合

设计模式称之为“模式”,就是一些固定的套路
我们很容易用到其他场景上,比如前面讲的工厂模式,我们需要将工厂类定义成“单例”,只需要简单的套用即可实现

实操代码

class BenZ:
    pass


class BMW:
    pass


class BYD:
    pass


class CarFactory:
    """定义两个类属性"""
    __obj = None
    __init_flag = True

    """定义工厂方法: 输入对应的商标, 返回对应的对象"""
    def create_car(self, brand):
        if brand == "奔驰":
            return BenZ()
        elif brand == "宝马":
            return BMW()
        elif brand == "比亚迪":
            return BYD()
        else:
            return "未知品牌, 无法创建"

    """定义原型模式方法"""
    def __new__(cls, *args, **kwargs):
        if cls.__obj is None:
            # cls.__obj == object.__new__(cls) # 这里使用的是赋值语句而不是等值语句
            cls.__obj = object.__new__(cls)
        return cls.__obj

    def __init__(self):
        if CarFactory.__init_flag:
            print("初始化工厂模式")
            CarFactory.__init_flag = False


factory = CarFactory()
c1 = factory.create_car("奔驰")
c2 = factory.create_car("比亚迪")
print(c1)
print(c2)
print("这里可以看到根据输入的内容创建了不同的对象")
factory2 = CarFactory()
print(factory)
print(factory2)
print("这里可以看到虽然我们又新建了一个工厂对象, 但是我们使用的仍是同一个工厂对象")

运行结果
在这里插入图片描述


四、实操作业

  1. 定义发动机类Motor、底盘类Chassis、座椅类Seat,车辆外壳类Shell,并使用组合关系定义汽车类. 其他要求如下:

    定义汽车的run()方法,里面需要调用Motor类的work()方法,需要调用座椅类Seat的work()方法,
    也需要调用底盘类Chassis的work()方法

    设计模式选择依据:

    • 多态实现: 因为这些汽车组件都有一个run() 方法. has-a + 继承 可以使用多态实现

    • 工厂模式: 因为有多个组件, 可以通过工厂模式进行创建

    实操代码

    print("=====================方式一:多态实现======================")
    
    
    class Component:
        def work(self):
            print("汽车组件工作")
    
    
    class Motor(Component):
        def work(self):
            print("发动机工作")
    
    
    class Chassis(Component):
        def work(self):
            print("底盘工作")
    
    
    class Seat(Component):
        def work(self):
            print("座椅工作")
    
    
    class Shell(Component):
        def work(self):
            print("外壳工作")
    
    
    def run(part):
        part.work()
    
    
    run(Motor())
    run(Seat())
    run(Chassis())
    print("====================方式二: 工厂模式实现=======================")
    
    
    class CarFactory:
        """定义类属性"""
        __init_flag = True
        __obj = None
        """定义工厂方法"""
    
        def run(self, component):
            if component == "发动机":
                return Motor()
            elif component == "底盘":
                return Chassis()
            elif component == "座椅":
                return Seat()
            elif component == "外壳":
                return Shell()
    
        """定义原型模式方法"""
        def __new__(cls, *args, **kwargs):
            if cls.__obj is None:
                cls.__obj = object.__new__(cls)
            return cls.__obj
    
        def __init__(self):
            if CarFactory.__init_flag:
                print("初始化汽车工厂")
                CarFactory.__init_flag = False
    
    
    car1 = CarFactory()
    car1.run("发动机").work()
    car1.run("座椅").work()
    car1.run("底盘").work()
    

    运行结果
    在这里插入图片描述

  2. 使用工厂模式、单例模式实现如下需求:

    (1) 电脑工厂类ComputerFactory用于生产电脑Computer。工厂类使用单例模式,也就是说只能有一个工厂对象
    (2) 工厂类中可以生产各种品牌的电脑:联想、华硕、神舟
    (3) 各种品牌的电脑使用继承实现:
    (4) 父类是Computer类,定义了calculate方法
    (5) 各品牌电脑类需要重写父类的calculate方法

    实操代码

    # 定义父类
    class Computer:
        def calculate(self):
            print("计算方法, 用于被重写")
    
    
    class Lenovo(Computer):
        def calculate(self):
            print("联想进行电脑计算")
    
    
    class ASUS(Computer):
        def calculate(self):
            print("华硕电脑进行计算")
    
    
    class ShenZhou(Computer):
        def calculate(self):
            print("神州电脑进行计算")
    
    
    class ComputerFactory:
        """类属性"""
        __obj = None
        __init_flag = True
    
        """工厂方法"""
    
        def productionComputer(self, brand):
            if brand == "联想":
                return Lenovo()
            elif brand == "华硕":
                return ASUS()
            elif brand == "神州":
                return ShenZhou()
            else:
                return "输入错误, 无法创建"
    
        """单例模式方法"""
    
        def __new__(cls, *args, **kwargs):
            if cls.__obj is None:
                return object.__new__(cls)
    
        def __init__(self):
            if ComputerFactory.__init_flag:
                print("开始初始化")
                ComputerFactory.__init_flag = False
    
    
    computer1 = ComputerFactory()
    print("测试工厂方法")
    computer1.productionComputer("联想").calculate()
    computer1.productionComputer("华硕").calculate()
    computer1.productionComputer("神州").calculate()
    print("测试单例方法")
    computer2 = ComputerFactory()
    print(computer1)
    print(computer2)
    

    执行结果

    在这里插入图片描述

  3. 定义一个Employee雇员类,要求如下:

    (1) 属性有:id、name、salary
    (2) 运算符重载+:实现两个对象相加时,默认返回他们的薪水和
    (3) 构造方法要求:输入name、salary,不输入id. id采用自增的方式,从1000开始自增,第一个新增对象是1001,第二个新增对象是1002
    (4) 根据salary属性,使用@property设置属性的get和set方法。set方法要求输入:1000-50000范围的数字

    实操代码

    class Employee:
        __id = 1000
    
        def __init__(self, name, salary):
            """利用类属性实现id自增"""
            self.name = name
            self.__salary = salary  # 因为salary要在多个方法中使用, 因此要设置成类变量而不是设置成局部变量/方法内变量
            self.id = Employee.__id + 1
            Employee.__id = self.id
    
        def __add__(self, other):
            if isinstance(other, Employee):
                return print("{0}的薪水为{1}, {2}的薪水为{3}, 两人薪水一共:{4}"
                             .format(self.name, self.__salary, other.name, other.__salary, self.__salary + other.__salary))
    
        @property
        def salary(self):
            print("月薪为", self.__salary)
            return self.__salary
    
        @salary.setter
        def salary(self, currentSalary):
            if 1000 < currentSalary < 50000:
                self.__salary = currentSalary
                print("月薪设置成功")
            else:
                print("薪水录入错误! 只能在1000-50000之间")
    
    
    e1 = Employee("时间静止", 5555)
    e2 = Employee("静止时间", 4444)
    Employee.__add__(e1, e2)
    e1.salary = 4444
    print(e1.salary)
    e1.salary = 666
    

    执行结果
    在这里插入图片描述


猜你喜欢

转载自blog.csdn.net/qq_43371556/article/details/124804219