前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Python升级之路( Lv7 ) 面向对象深入

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

作者头像
时间静止不是简史
发布2022-06-15 19:41:56
4630
发布2022-06-15 19:41:56
举报
文章被收录于专栏:Java探索之路

Python系列文章目录

第一章 Python 入门

第二章 Python基本概念

第三章 序列

第四章 控制语句

第五章 函数

第六章 面向对象基础

第七章 面向对象深入


面向对象深入

前言

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


一、面向对象三大特征

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

具体含义如下

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

继承

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

语法格式

代码语言:javascript
复制
class  子类类名(父类1[,父类2,...]):
 类体

注意

如果在类定义中没有指定父类,则默认父类是 object类.

也就是说 object 是所有类的父类,里面定义了一些所有类共有的默认实现,比如: __new__()

关于构造函数:

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

实操代码

代码语言:javascript
复制
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__(参数列表)

代码语言:javascript
复制
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 进行比对

代码语言:javascript
复制
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))

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

mro() 获取类的层次结构

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

实操代码

代码语言:javascript
复制
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相对应的方法

最常用的一些统计如下:

实操代码

代码语言:javascript
复制
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支持多重继承,一个子类可以有多个“直接父类”. 这样就具备了“多个父类”的特点. 但是这样会把“类的整体层次”搞的异常复杂,因此尽量避免使用

语法格式

代码语言:javascript
复制
class  子类类名(父类1,父类2[,...]):
 类体

实操代码

代码语言:javascript
复制
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

实操代码

代码语言:javascript
复制
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个必要条件:继承、方法重写

实操代码

代码语言:javascript
复制
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 函数,递归拷贝对象中包含的子对象 深拷贝:拷贝时,拷贝源对象,也递归拷贝对象中包含的子对象

如图所示:

实操代码

代码语言:javascript
复制
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种设计模式. 我们没有必要全部学习,只需学习几个常用的即可 对于初学者,我们学习两个最常用的模式:工厂模式和单例模式

工厂模式

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

注意事项

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

实操代码

代码语言:javascript
复制
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__() 方法, 用于初始化该类, 并初始化类属性

实操代码

代码语言:javascript
复制
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)

运行结果

工厂和单例模式组合

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

实操代码

代码语言:javascript
复制
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("这里可以看到虽然我们又新建了一个工厂对象, 但是我们使用的仍是同一个工厂对象")

运行结果


四、实操作业

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

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

也需要调用底盘类Chassis的work()方法

设计模式选择依据:

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

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

实操代码

代码语言:javascript
复制
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()

运行结果

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

(1) 电脑工厂类ComputerFactory用于生产电脑Computer。工厂类使用单例模式,也就是说只能有一个工厂对象

(2) 工厂类中可以生产各种品牌的电脑:联想、华硕、神舟

(3) 各种品牌的电脑使用继承实现:

(4) 父类是Computer类,定义了calculate方法

(5) 各品牌电脑类需要重写父类的calculate方法

实操代码

代码语言:javascript
复制
# 定义父类
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)

执行结果

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

(1) 属性有:id、name、salary

(2) 运算符重载+:实现两个对象相加时,默认返回他们的薪水和

(3) 构造方法要求:输入name、salary,不输入id. id采用自增的方式,从1000开始自增,第一个新增对象是1001,第二个新增对象是1002

(4) 根据salary属性,使用@property设置属性的get和set方法。set方法要求输入:1000-50000范围的数字

实操代码

代码语言:javascript
复制
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.sette
    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

执行结果

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-06-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Python系列文章目录
    • 面向对象深入
    • 前言
    • 一、面向对象三大特征
      • 继承
        • super()获得父类定义
        • object 父类
        • 多重继承
        • 组合
      • 多态
        • 工厂模式
        • 单例模式
        • 工厂和单例模式组合
    • 二、浅拷贝与深拷贝
    • 三、常用设计模式
    • 四、实操作业
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档