我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈
入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈
虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈
PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈
Oracle数据库教程:👉👉 Oracle数据库文章合集 👈👈
优 质 资 源 下 载 :👉👉 资源下载合集 👈👈
面向对象_综合案例
- 面向对象_综合案例
- 涉及知识点
- 案例需求
- 案例代码
面向对象_综合案例
涉及知识点
- 面向对象
- 装饰器
- 描述器
案例需求
- 设计一款计算器,实现一些基本的操作,加减乘除运算并打印计算结果操作
案例代码
- 1、使用面向过程的思想编程
def jia(n1, n2): return n1 + n2 def jian(n1, n2): return n1 - n2 def cheng(n1, n2): return n1 * n2 def chu(n1, n2): return n1 / n2 # 调用方法进行计算 res1 = jia(2, 5) res2 = jian(20, 8) res3 = cheng(3, 6) print(res1) print(res2) print(res3)
- 用这种方式计算一个连续运算式:
(3 + 6 - 4) * 8
def jia(n1, n2): return n1 + n2 def jian(n1, n2): return n1 - n2 def cheng(n1, n2): return n1 * n2 def chu(n1, n2): return n1 / n2 res1 = jia(3, 6) res2 = jian(res1, 4) res3 = cheng(res2, 8) # res3 = cheng(jian(jia(3, 6), 4), 8) print(res3)
- 问题点: 计算一个连续运算表达式的时候比较麻烦,需要通过多个变量保存中间每一步计算的结果,然后下一次运算再把保存的中间结果当做参数传递进去与另一个数进行运算
- 2、优化:其实这个中间结果仅仅只是运算的时候用到了,用户并不需要。所以我们可以定义一个参数来保存这个中间结果,用户只需要选择运算方式,和参与运算的一个数值,然后再把用户输入的数值与这个中间结果进行运算
result = 0 def first_num(n): """接收第一个数字,不用计算""" global result result = n def jia(n): # 在方法内使用全局变量需要使用global关键字指定 global result result += n def jian(n): global result result -= n def cheng(n): global result result *= n def chu(n): global result result /= n first_num(3) jia(6) jian(4) cheng(8) print(result)
- 优化之后,不再需要定义变量来存储中间结果了,可以直接写运算方式直接计算了。
- 问题点:
- 1、用于存储中间结果的变量
result
是一个全局变量,在任何地方都可以直接修改,很不安全 - 2、代码比较凌乱,每一个函数都是一个单独的个体
- 1、用于存储中间结果的变量
- 3、为了解决这写问题点,那就需要使用面向对象的编程思想了
class Calculator: __result = 0 def first_num(self, n): """接收第一个数字,不用计算""" Calculator.__result = n def jia(self, n): Calculator.__result += n def jian(self, n): Calculator.__result -= n def cheng(self, n): Calculator.__result *= n def chu(self, n): Calculator.__result /= n def show(self): print(Calculator.__result) c = Calculator() c.first_num(3) c.jia(6) c.jian(4) c.cheng(8) c.show()
- 我们已经通过面向对象的思想对案例进行了优化
- 1、使用私有属性
__result
解决了全局变量不安全的问题 - 2、使用类对所有方法进行了封装
- 1、使用私有属性
- 但是我们可以看一下,类里面的所有方法都传递了一个self的参数,但是在方法内部根本没用到,在方法内部都用到了类对象Calculator,所以我们可以继续优化,将类里面的所有方法都改成类方法
- 我们已经通过面向对象的思想对案例进行了优化
- 4、优化代码示例
class Calculator: __result = 0 @classmethod def first_num(cls, n): """接收第一个数字,不用计算""" cls.__result = n @classmethod def jia(cls, n): cls.__result += n @classmethod def jian(cls, n): cls.__result -= n @classmethod def cheng(cls, n): cls.__result *= n @classmethod def chu(cls, n): cls.__result /= n @classmethod def show(cls): print('计算的结果是:', cls.__result) Calculator.first_num(3) Calculator.jia(6) Calculator.jian(4) Calculator.cheng(8) Calculator.show()
- 使用类方法,让方法在调用的时候,自动将类作为参数传递进去,再通过这个类获取到类属性
- 问题点
__result
是类属性,是依赖于类对象的,所以无法同时进行多个表达式的运算
- 解决方法
- 将
__result
定义成实例属性,通过实例化对象,让每一个表达式的运算都相互独立
- 将
- 5、优化代码示例
class Calculator: def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n def jia(self, n): self.__result += n def jian(self, n): self.__result -= n def cheng(self, n): self.__result *= n def chu(self, n): self.__result /= n def show(self): print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia(6) c1.jian(4) c1.cheng(8) c1.show()
- 上面的优化基本已经满足了我们的需求,但是并没有做容错处理,如果用户输入的是一个非数字,那么整个程序就会报错,那么我们需要给这个程序做一个容错处理
- 6、优化代码示例
class Calculator: def n_check(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') def __init__(self, n): """接收第一个数字,不用计算""" self.n_check(n) self.__result = n def jia(self, n): self.n_check(n) self.__result += n def jian(self, n): self.n_check(n) self.__result -= n def cheng(self, n): self.n_check(n) self.__result *= n def chu(self, n): self.n_check(n) self.__result /= n def show(self): print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia('1') c1.jian(4) c1.cheng(8) c1.show()
- 这段代码实现了容错处理,而且看上起比较简洁,但是,却让类里面的所有方法违反的单一原则,所有的运算方法除了运算功能还做了数据类型判断功能,并且我们破坏了原有方法的代码结构
- 为了更好的解决输入数据验证的问题,我们可以使用我们前面学习的装饰器
- 7、优化代码示例
class Calculator: def n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @n_check def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @n_check def jia(self, n): self.__result += n @n_check def jian(self, n): self.__result -= n @n_check def cheng(self, n): self.__result *= n @n_check def chu(self, n): self.__result /= n def show(self): print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia('1') c1.jian(4) c1.cheng(8) c1.show()
- 这里虽然优化了,但是还有一点问题,就是当前我们定义的这个装饰器
n_check()
在外部能被访问到,而且会报错! - 解决办法:将这个方法定义成私有方法
- 这里虽然优化了,但是还有一点问题,就是当前我们定义的这个装饰器
- 8、优化代码示例
class Calculator: def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @__n_check def jia(self, n): self.__result += n @__n_check def jian(self, n): self.__result -= n @__n_check def cheng(self, n): self.__result *= n @__n_check def chu(self, n): self.__result /= n def show(self): print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia('1') c1.jian(4) c1.cheng(8) c1.show()
- 我们将装饰器函数私有化之后,在外界就无法使用了,当前就比较完善了
- 但是,客户有了新的需求:用户输入数字之后,需要增加语音播报
- 补充:语音播报模块
win32com.client
(仅支持windows系统) - 安装库
# 使用pip命令安装(20231129安装失败) pip install pywin32 # 配置文件安装 PyCharm —— File —— Settings —— Project:项目名 —— Python Interpreter —— + —— pywin32 # 注意这里搜索的包名称是:pywin32
- 使用步骤
import win32com.client # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # SAPI.SpVoice 这是固定的字符串 # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak('我的名字是失心疯')
- 9、优化代码示例
import win32com.client class Calculator: def __say(self, word): # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak(word) def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check def __init__(self, n): """接收第一个数字,不用计算""" self.__say(n) self.__result = n @__n_check def jia(self, n): self.__say(n) self.__result += n @__n_check def jian(self, n): self.__say(n) self.__result -= n @__n_check def cheng(self, n): self.__say(n) self.__result *= n @__n_check def chu(self, n): self.__say(n) self.__result /= n def show(self): self.__say(f'计算的结果是:{self.__result}') print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia(6) c1.jian(4) c1.cheng(8) c1.show()
- 这里,我们是通过定义私有方法,然后在所有的方法中添加了这个私有方法,这就跟刚开始我们添加输入值的验证一样,让所有的方法都违反了单一性和破坏了原代码结构
- 同样的,我们可以通过装饰器来解决了
- 10、优化代码示例
import win32com.client class Calculator: def __say(func): def inner(self, n): # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak(n) return func(self, n) return inner def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check @__say def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @__n_check @__say def jia(self, n): self.__result += n @__n_check @__say def jian(self, n): self.__result -= n @__n_check @__say def cheng(self, n): self.__result *= n @__n_check @__say def chu(self, n): self.__result /= n def show(self): # self.__say(f'计算的结果是:{self.__result}') print('计算的结果是:', self.__result) c1 = Calculator('3') c1.jia(6) c1.jian(4) c1.cheng(8) c1.show()
-
在这里需要注意的是,我们是需要先验证输入的内容是否满足需求,满足需求之后才会进行播报,所以装饰器的顺序也是
@__n_check
在上面,@__say
在下面,就是优先执行@__n_check
装饰器进行验证,然后再执行@__say
装饰器进行播报 -
嵌套装饰器执行原理
def jia(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') speaker = win32com.client.Dispatch('SAPI.SpVoice') speaker.Speak(n) self.__result += n return self.__result
-
这里出现了一个新的问题点:我们将之前的私有方法
__say()
改变成了装饰器,但是我们的show()
方法结构与上面的运算方法不一致,导致show()
方便并不能直接使用装饰器进行装饰,导致无法直接进行语音播报 -
解决方法:我们将执行语音播报部分单独封装成一个方法,然后
__say()
装饰器中再调用这个方法
-
- 11、优化代码示例
import win32com.client class Calculator: def __say(self, word): # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak(word) def __say_zsq(func): def inner(self, n): self.__say(n) return func(self, n) return inner def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check @__say_zsq def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @__n_check @__say_zsq def jia(self, n): self.__result += n @__n_check @__say_zsq def jian(self, n): self.__result -= n @__n_check @__say_zsq def cheng(self, n): self.__result *= n @__n_check @__say_zsq def chu(self, n): self.__result /= n def show(self): self.__say(f'计算的结果是:{self.__result}') print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia(6) c1.jian(4) c1.cheng(8) c1.show()
- 这里看上去优化挺好的,但是播报的内容并不是理想的效果,这里仅仅只是播报了数字,并没有播报运算方法
- 那么,我们可以再优化一下
- 12、优化代码示例
import win32com.client class Calculator: def __say(self, word): # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak(word) def __say_zsq_init(func): def inner(self, n): self.__say(n) return func(self, n) return inner def __say_zsq_jia(func): def inner(self, n): self.__say(f'加{n}') func(self, n) return inner def __say_zsq_jian(func): def inner(self, n): self.__say(f'减去{n}') func(self, n) return inner def __say_zsq_cheng(func): def inner(self, n): self.__say(f'乘以{n}') func(self, n) return inner def __say_zsq_chu(func): def inner(self, n): self.__say(f'除以{n}') func(self, n) return inner def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check @__say_zsq_init def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @__n_check @__say_zsq_jia def jia(self, n): self.__result += n @__n_check @__say_zsq_jian def jian(self, n): self.__result -= n @__n_check @__say_zsq_cheng def cheng(self, n): self.__result *= n @__n_check @__say_zsq_chu def chu(self, n): self.__result /= n def show(self): self.__say(f'计算的结果是:{self.__result}') print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia(6) c1.jian(4) c1.cheng(8) c1.show()
- 播报效果是实现了,但是创建了多个装饰器,每个装饰器仅仅只是播报的内容不同而已,出现了太多的冗余代码。
- 解决办法:我们可以定义一个方法,用来创建装饰器(函数返回一个装饰器),这个装饰器的播报内容由这个方法传递进来
- 13、优化代码示例
import win32com.client class Calculator: def __say(self, word): # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak(word) def __create_zsq(word=""): def __say_zsq(func): def inner(self, n): self.__say(word + str(n)) return func(self, n) return inner return __say_zsq def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check @__create_zsq() def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @__n_check @__create_zsq("加") def jia(self, n): self.__result += n @__n_check @__create_zsq("减去") def jian(self, n): self.__result -= n @__n_check @__create_zsq("乘以") def cheng(self, n): self.__result *= n @__n_check @__create_zsq("除以") def chu(self, n): self.__result /= n def show(self): self.__say(f'计算的结果是:{self.__result}') print('计算的结果是:', self.__result) c1 = Calculator(3) c1.jia(6) c1.jian(4) c1.cheng(8) c1.show()
- 这里需要理解的是带参数的装饰器
@__create_zsq("加")
- 可以这么简单的理解:
- 1、
@__create_zsq("加")
拆分成@
和__create_zsq("加")
- 2、
__create_zsq("加")
这个就相当于是调用执行这个方法,执行这个方法的返回值就是__say_zsq
- 3、那么
@__create_zsq("加")
就相当于是@__say_zsq
,这就和前面的装饰器效果是一样了
- 1、
- 解决了这个播报的需求,客户又有了新的需求了:希望能够拿到最后的计算结果,方便对结果进行其他操作
- 解决方案:
- 由于存储结果的
__result
是私有属性,不能在外界直接访问,那么我们可以定义一个方法来读取这个属性值 - 如果是直接定义方法,那么在获取这个结果值的时候就得加
()
执行方法调用,为了实现对象.属性
的方式获取结果值,我们可以通过@property
装饰这个方法
- 由于存储结果的
- 这里需要理解的是带参数的装饰器
- 14、优化示例代码
import win32com.client class Calculator: def __say(self, word): # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak(word) def __create_zsq(word=""): def __say_zsq(func): def inner(self, n): self.__say(word + str(n)) return func(self, n) return inner return __say_zsq def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check @__create_zsq() def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @__n_check @__create_zsq("加") def jia(self, n): self.__result += n @__n_check @__create_zsq("减去") def jian(self, n): self.__result -= n @__n_check @__create_zsq("乘以") def cheng(self, n): self.__result *= n @__n_check @__create_zsq("除以") def chu(self, n): self.__result /= n def show(self): self.__say(f'计算的结果是:{self.__result}') print('计算的结果是:', self.__result) @property def result(self): return self.__result c1 = Calculator(3) c1.jia(6) c1.jian(4) c1.cheng(8) c1.show()
- 整个类的实现已经非常完美了,但是用户在使用的时候非常不爽啊,每次执行一个运算的时候都需要通过
c1.
去调用,挺麻烦的。 - 假设,如果我们的
c1.jia(6)
返回的是c1
本身,那么后面的c1.jian(4)
,是不是就可以写成c1.jia(6).jian(4)
了?以此类推…
- 整个类的实现已经非常完美了,但是用户在使用的时候非常不爽啊,每次执行一个运算的时候都需要通过
- 14、优化示例代码
import win32com.client class Calculator: def __say(self, word): # 1、创建一个播报器对象 speaker = win32com.client.Dispatch('SAPI.SpVoice') # 2、通过这个播报器对象直接播放相对于的语音字符串 speaker.Speak(word) def __create_zsq(word=""): def __say_zsq(func): def inner(self, n): self.__say(word + str(n)) return func(self, n) return inner return __say_zsq def __n_check(func): def inner(self, n): if not isinstance(n, int): raise TypeError('输入的数据类型错误,应该输入int类型!') return func(self, n) return inner @__n_check @__create_zsq() def __init__(self, n): """接收第一个数字,不用计算""" self.__result = n @__n_check @__create_zsq("加") def jia(self, n): self.__result += n return self @__n_check @__create_zsq("减去") def jian(self, n): self.__result -= n return self @__n_check @__create_zsq("乘以") def cheng(self, n): self.__result *= n return self @__n_check @__create_zsq("除以") def chu(self, n): self.__result /= n return self def show(self): self.__say(f'计算的结果是:{self.__result}') print('计算的结果是:', self.__result) return self @property def result(self): return self.__result c1 = Calculator(3) c1.jia(6).jian(4).cheng(8).show() c2 = Calculator(5) print(c2.jia(8).jian(3).cheng(6).show().cheng(3).result)
c1.jia(6).jian(4).cheng(8).show()
这个就是链式编程