# Python入门 **Repository Path**: huaiyuechusan/getting-started-with-python ## Basic Information - **Project Name**: Python入门 - **Description**: Python入门代码练习。函数和内存分析,控制语句,面向对象,序列。 更多学习笔记,欢迎访问我的个人博客(https://huaiyuechusan.gitee.io) - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: https://huaiyuechusan.gitee.io - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-11-19 - **Last Updated**: 2023-12-13 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Python入门 ## 编程基本概念 ### Python程序的构成 1. 连接符 \ 一行代码过长,变成多行时使用 2. 强制使用缩进,四个空格表示一个缩进 ### 对象的基本组成和内存示意图 ![image-20231110201745676](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102017825.png)![image-20231110201950773](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102019862.png)![image-20231110202237621](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102022690.png) ### 引用的本质-栈内存和堆内存-内存示意图 ![image-20231110202507724](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102025811.png)![image-20231110202618382](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102026455.png) **==Python是动态类型语言==:变量不需要显式声明类型。根据变量引用的对象,Python解释器自动确定数据类型** ### 标识符-帮助系统简单使用-命名规则 ![image-20231110203017623](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102030659.png)![image-20231110203126470](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102031531.png)![image-20231110203446167](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102034233.png) ### 变量的声明-初始化-垃圾回收机制 ![image-20231110204145297](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102041345.png) ### 常量-链式赋值-系列解包赋值 ![image-20231110204256608](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102042669.png) ![image-20231110204725378](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102047432.png) ![image-20231110205028675](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102050729.png) **使用系列解包复制可以轻松实现变量值交换** ### 内置数据类型-基本算术运算符 ![image-20231110210320597](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102103657.png)![image-20231110210508378](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102105449.png) ### 整数-不同进制-其他类型转成整数 ==**Python的整数可以无限大,任意大**== ![image-20231110210750024](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102107064.png)![image-20231110210944115](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102109168.png)![image-20231110211017712](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102110784.png) ### 浮点数-自动转换强制转换-增强赋值运算符 ![image-20231110211408060](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102114124.png) ==**round(value))**可以返回**四舍五入**的值==。但**不会改变原有值**,而是产生新的值 ![image-20231110211636512](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102116576.png) ### 时间的表示-unix时间点-毫秒微妙-time模块 ![image-20231110212157494](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102121561.png)![image-20231110212401113](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102124175.png) ### 多点坐标-绘制折线图-计算两点距离 ### 布尔值本质-逻辑运算符-位运算符-比较运算符-短路问题![image-20231110213752664](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102153527.png)![image-20231110213917617](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102153409.png) ![image-20231110214203652](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102154007.png) ![image-20231110214628683](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311102154153.png) **关系运算符可以连用 如: `210") ``` ### 多分支选择结构 ![image-20231114164837210](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759581.png) ![image-20231114165601161](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759582.png) ![image-20231114170614275](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759583.png) ```python ''' ### 多分支选择结构 ''' score = input("输入学生分数:") grade = '' if 0 <= int(score) < 60: grade = "不及格" elif int(score) < 80: # 60<=score<=80 多分支之间具有逻辑关系 grade = "及格" elif int(score) < 90: grade = "良好" elif int(score) <= 100: grade = "优秀" else: grade = "成绩输入错误" print("分数是{0},等级是{1}".format(score, grade)) ``` ### 选择结构的嵌套 ![image-20231114170713573](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759584.png) ```python score = int(input("输入一个0-100之间的数字:")) grade = '' if score > 100 or score < 0: score = int(input("输入错误,请重新输入0-100之间的数字:")) else: if score >= 90: grade = 'A' elif score >= 80: grade = 'B' elif score >= 70: grade = 'C' elif score >= 60: grade = 'D' else: grade = 'E' print("分数是{0},等级是{1}".format(score, grade)) ``` ```python # 更少的代码方法 score = int(input("输入一个0-100之间的数字:")) grade = 'ABCDE' if score > 100 or score < 0: score = int(input("输入错误,请重新输入0-100之间的数字:")) else: num = score // 10 if num < 6: num = 5 print("分数是{0},等级是{1}".format(score, grade[9-num])) ``` ### while循环结构-死循环处理 ![image-20231114211935339](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759585.png) ### for循环结构-遍历各种可迭代对象-range对象 ![image-20231115235425967](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759586.png) ```python for x in (20, 30, 50): print(x*3) for temp in "weqwewe": print(x) ``` ```python d = {"name": "haha", "age": 12, "sex": "male"} for x in d: # 遍历字典所以key print(x) for x in d.keys(): # 遍历字典所有的key print(x) for x in d.values(): # 遍历字典所有的value print(x) for x in d.items(): # 遍历字典所有键值对 print(x) ``` ![image-20231116154729368](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759587.png) ```python for x in range(3, 10, 2): # start=3 end=10 step=2 包头不包尾 print(x, end='\t') # 计算1-100累加和,奇数累加和,偶数累加和 sum_all = 0 sum_even = 0 sum_odd = 0 for x in range(101): sum_all += x if x % 2 == 0: sum_even += x else: sum_odd += x print("1-100累加和{0},奇数累加和{1},偶数累加和{2}".format(sum_all, sum_odd, sum_even)) ``` ### 嵌套循环 ![image-20231116160125551](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759588.png) ![image-20231116160157632](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759589.png) ```python for x in range(5): for y in range(5): print(x, end="\t") print() ``` ### 嵌套循环练习-九九乘法表-打印表格数据 ![image-20231116160523325](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759590.png) ```python # 九九乘法表 for m in range(1, 10): for n in range(1, m+1): print("{0}*{1}={2}".format(m, n, m*n), end="\t") print() ''' 1*1=1 2*1=2 2*2=4 3*1=3 3*2=6 3*3=9 4*1=4 4*2=8 4*3=12 4*4=16 5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81 ''' ``` ```python # 用列表和字典存储下表信息,并打印出表中工资高于15000的数据 r1 = dict(name="hycs", age=18, salary=10000, city="shanghai") r2 = dict(name="sdaw", age=28, salary=20000, city="beijing") r3 = dict(name="e1qe", age=38, salary=30000, city="nanjing") tb = [r1, r2, r3] for x in tb: if x.get("salary")>15000: print(x) ``` ### break语句 ![image-20231116161623031](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759591.png) ### continue语句 ![image-20231116161924814](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759592.png) ```python # 要求输入员工的薪资,若薪资小于0则重新输入。最后打印出录入员王的数量和薪资明细,以及平均薪资 empNum = 0 salarySum = 0 salarys = [] while True: s = input("请输入员工薪资(按Q或q退出)") if s.upper() == "Q": break if float(s) < 0: print("无效录入,请重新录入") continue print("录入成功") empNum += 1 salarySum += float(s) salarys.append(float(s)) print("员工数", format(empNum)) print("录入薪资:", salarys) print("总薪资:", salarySum) print("平均薪资", salarySum / empNum) ``` ### 循环中的else子句 ![image-20231116163319060](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759593.png) ```python salarySum = 0 salarys = [] for i in range(4): s = input("请输入员工薪资(按Q或q退出)") if s.upper() == "Q": break if float(s) < 0: print("无效录入,请重新录入") continue print("录入成功") salarySum += float(s) salarys.append(float(s)) else: print("全部录入成功") print("录入薪资:", salarys) print("总薪资:", salarySum) print("平均薪资", salarySum / 4) """ 请输入员工薪资(按Q或q退出)40 录入成功 请输入员工薪资(按Q或q退出)50 录入成功 请输入员工薪资(按Q或q退出)60 录入成功 请输入员工薪资(按Q或q退出)70 录入成功 全部录入成功 录入薪资: [40.0, 50.0, 60.0, 70.0] 总薪资: 220.0 平均薪资 55.0 """ ``` ![image-20231116164105716](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759594.png) ### 循环代码优化技巧 ![image-20231116164149547](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759595.png) **其他优化手段** 1. ==连接多个字符串或列表,使用join()或append()而不使用+== +会创建新的字符串,join不会 2. ==列表进行元素插入和删除,尽量在列表尾部操作== ### zip()并行送代多个序列 ![image-20231116164902092](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759596.png) ![image-20231116165017217](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759597.png) ```python names = ("haohao", "hehe", "huaiyue") ages = (18, 19, 20) jobs = ("IT", "TEACHER", "POLICE") # 使用zip并行迭代多个序列 for name, age, job in zip(names, ages, jobs): print("{0}--{1}--{2}".format(name, age, job)) # 不使用zip也可以并使迭代多个序列 for i in range(min(len(names), len(ages), len(jobs))): print("{0}--{1}--{2}".format(names[i], ages[i], jobs[i])) ``` ### 推导式创建序列-列表推导式-字典推导式-集合推导式-生成器推导式 ![image-20231116165915358](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759598.png) #### 列表推导式 ![image-20231116170101409](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759599.png) ```python a = [x for x in range(1, 10) if x % 2 == 0] print(a) cells = [(row, column) for row, column in zip(range(1, 10), range(101, 110))] print(cells) ''' [2, 4, 6, 8] [(1, 101), (2, 102), (3, 103), (4, 104), (5, 105), (6, 106), (7, 107), (8, 108), (9, 109)] ''' ``` #### 字典推导式 ![image-20231116171102217](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759600.png) ```python # 统计字数 my_text = 'i love python, me too' char_count = {c: my_text.count(c) for c in my_text} print(char_count) ''' {'i': 1, ' ': 4, 'l': 1, 'o': 4, 'v': 1, 'e': 2, 'p': 1, 'y': 1, 't': 2, 'h': 1, 'n': 1, ',': 1, 'm': 1} ''' ``` #### 集合推导式 ![image-20231116171619541](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759601.png) #### 生成器推导式(不直接生成元组) ![image-20231116171724496](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759602.png) ### 综合练习-绘制不同颜色的同心圆-绘制棋盘 ![image-20231116173335354](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311161759604.png) ```python # 绘制不同颜色的同心圆 import turtle p = turtle.Pen() # 画笔对象 radius = [x * 10 for x in range(1, 11)] my_color = ["red", "yellow", "black", "green"] p.width(4) for r, i in zip(radius, range(len(radius))): # (10,0),(20,1) p.penup() p.goto(0, -r) p.pendown() p.color(my_color[i % len(my_color)]) p.circle(r) turtle.done() # 程序执行完毕,窗口还在 ``` ```python # 绘制棋盘 ``` ## 函数和内存分析 ### 函数的基本概念-内存分析-函数分类-定义和调用 ![image-20231117163429235](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943309.png) ![image-20231117163611378](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943310.png) ![image-20231117163711793](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943311.png) ![image-20231117163948916](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943312.png) ![image-20231117164127321](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943313.png) ![image-20231117164425172](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943314.png) ### 形参和实参-文档字符串-函数的注释 ![image-20231117164624930](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943315.png) ![image-20231117165253398](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943316.png) ![image-20231117165341145](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943317.png) ```python def printMax(a, b): """ 求两个数较大的数 :param a: 一个参数 :param b: 一个参数 :return: 返回较大的数 """ help(printMax) print("print(printMax.__doc__) ", printMax.__doc__) """ Help on function printMax in module __main__: printMax(a, b) 求两个数较大的数 :param a: 一个参数 :param b: 一个参数 :return: 返回较大的数 """ """ print(printMax.__doc__) 求两个数较大的数 :param a: 一个参数 :param b: 一个参数 :return: 返回较大的数 """ ``` ### 返回值详解 ![image-20231117165838027](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943318.png) ```python def printShape(n): """ 返回一个列表 :param n: :return: """ s1 = "#" * n s2 = "$" * n return [s1, s2] s = printShape(5) print(s) """ ['#####', '$$$$$'] """ ``` ### 函数也是对象-内存分析 ==Python中,”一切都是对象”==。实际上,执行def定义函数后,系统就创建了相应的==函数对象==。 ![image-20231117170506090](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943319.png) ![image-20231117170556762](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943320.png) ![image-20231117170656317](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943321.png) ### 变量的作用域 #### 全局变量-局部变量-栈帧内存分析讲解 ![image-20231117170903135](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943322.png) ![image-20231117171022107](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943323.png) ```python a = 100 def f1(a, b, c): print(a, b, c) print(locals()) # 打印输出的局部变量 print("#" * 20) print(globals()) # 打印输出的全局变量 f1(2, 3, 4) """ 2 3 4 {'a': 2, 'b': 3, 'c': 4} #################### {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000023373C06CD0>, '__spec__': None, '__annotations__': {}, '__builtins__': , '__file__': 'D:\\2022百战Python\\Python基础\\函数和内存分析\\practice.py', '__cached__': None, 'a': 100, 'f1': } """ ``` #### 局部变量和全局变量-效率测试 ==局部变量的查询和访问速度比全局变量快,优先考虑使用,尤其是在循环的时候。== ==在特别强调效率的地方或者循环次数较多的地方,可以通过将全局变量转为局部变量提高运行速度。== ### 参数的传递 ![image-20231117172204514](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943324.png) ![image-20231117172401914](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943325.png) - **对“可变对象"进行“写操作”,直接作用于原对象本身。** - **对“不可变对象”进行“写操作”,会产生一个新的“对象空间”,并用新的值填充这块空间。** #### 传递可变对象-内存分析 #### 传递不可变对象-内存分析 ![image-20231117173137743](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943326.png) #### 浅拷贝和深拷贝-内存分析 ![image-20231117173444372](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943327.png) ①**浅拷贝**:==拷贝对象,但不拷贝子对象的内容,只是拷贝子对象的引用。== ②**深拷贝**:==拷贝对象,并且会连子对象的内存也全部(递归)拷贝一份,对子对象的修改不会影响源对象== #### 不可变对象含可变子对象-内存分析 ![image-20231117174312493](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943328.png) ### 参数的类型 ![image-20231117174934453](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943329.png) #### 位置参数-默认值参数-命名参数 ![image-20231117181032226](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943330.png) ![image-20231117181134330](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943331.png) ![image-20231117181207414](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943332.png) #### 可变参数-强制命名参数 ![image-20231117181448367](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943333.png) ![image-20231117181534034](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943334.png) ![image-20231117181557596](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943335.png) ![image-20231117181931376](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943336.png) ### lambda表达式和匿名函数 ![image-20231117182235722](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943337.png) ![image-20231117182308842](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943338.png) ```python f = lambda a, b, c: a + b + c print(f) print(id(f)) print(type(f)) print(f(1, 2, 3)) """ at 0x000001AF907F7E50> 1853555179088 6 """ g = [lambda a: a * 2, lambda b: b * 4, lambda c: c * 8] print(g[0](1), g[1](2), g[2](3)) """ 2 8 24 """ ``` ### eval()函数的用法和注入安全隐患问题 ![image-20231117183923056](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943339.png) ![image-20231117183958772](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943340.png) ```python s = "print('abcd')" eval(s) # eval会将字符串当成语句来执行 """abcd""" a = 10 b = 20 c = eval("a+b") print(c) """30""" dict1 = dict(a=100, b=200) d = eval("a+b", dict1) print(d) """300""" ``` ### 递归函数 ![image-20231117184610770](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943341.png) ![image-20231117184633540](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943342.png) #### 函数调用内存分析-栈帧的创建 ![image-20231117185027038](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943343.png) #### 阶乘计算案例 ```python def factorial(n): if n == 1: return 1 else: return n * factorial(n - 1) print(factorial(5)) ``` ![image-20231117185357907](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943344.png) ### 嵌套函数(内部函数)-数据隐藏 **嵌套函数:在函数内部定义的函数** 一般在什么情况下使用嵌套函数? 1. 封装-数据隐藏 :外部无法访问“嵌套函数”。 2. 贯彻DRY(Don't Repeat Yourself)原则 3. 嵌套函数,可以让我们在函数内部避免重复代码。 4. 闭包(后面会讲解) ### nonlocal和global关键字 ![image-20231117190204820](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943345.png) ### LEGB规则 ![image-20231117194110266](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311171943346.png) ## 面向对象 ### 面向对象和面向过程的区别-设计者思维-执行者思维 ![image-20231117194403836](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219980.png) ![image-20231117194445222](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219981.png) > **面向过程和面向对象的区别** > > 面向过程和面向对象都是对软件分析、设计和开发的一种思想,它指导着人们以不同的方式去分析、设计和开发软件。 > > ==C语言是一种典型的面向过程语言,Java是一种典型的面向对象语言。== ![image-20231117194720826](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219982.png) ![image-20231117194758398](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219983.png) > 面向对象可以帮助我们从宏观上把握、从整体上分析整个系统。但是,具体到实现部分的微观操作(就是一个个方法),仍然需要面向过程的思路去处理。 > > 我们干万不要把面向过程和面向对象对立起来。他们是相辅相成的。==面向对象离不开面向过程!== **面向对象和面向过程总结** ①都是解决问题的思维方式,都是代码组织的方式。 ②==面向过程是一种“执行者思维”==,解决简单问题可以使用面向过程 ③==面向对象是一种“设计者思维”==,解决复杂、需要协作的问题可以使用面向对象 > 面向对象离不开面向过程: > ·宏观上:通过面向对象进行整体设计 > ·微观上:执行和处理数据,仍然是面向过程 ### 对象进化的小故事 ![image-20231117195725002](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219984.png) ### 类的定义-类和对象的关系-对象的内存模型 ![image-20231117195854311](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219985.png) > 类:我们叫做`class`。对象:我们叫做`object`,`instance`(实例)。以后我们说某个类的对象,某个类的实例。是一样的意思。 ![image-20231117200128085](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219986.png) > 从一个类创建对象时,每个对象会共享这个类的行为(类中定义的方法),但会有自己的属性值(不共享状态)。更具体一点:“方法代码是共享的,属性数据不共享”。 > Python中,"一切皆对象"。类也称为“==类对像==",类的实例也称为“实例对象”。 ![image-20231117200451070](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219987.png) 要点如下: ①类名必须符合“标识符"的规则;一般规定,==首字母大写,多个单词使用“驼峰原则”==。 ②类体中我们可以定义属性和方法 ③属性用来描述数据,方法(即函数)用来描述这些数据相关的操作 ```python class Student: def __init__(self, name, score): # self参数是必须有的 self.name = name # 实例属性 self.score = score # 实例属性 def say_score(self): print("{0}的分数是{1}".format(self.name, self.score)) s1 = Student("john", 61) # 自动调用__init__()方法 print(s1.name, s1.score) s1.say_score() """ john 61 john的分数是61 """ ``` ![image-20231117201324006](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219988.png) ### 构造函数-init和new方法 ![image-20231117201604950](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219989.png) `__init__()` **构造函数**的要点如下: 1. 名称固定,必须为:`__init__()` 2. 第一个参数固定,必须为:`self`。`self`指的就是刚刚创建好的实例对象 3. 构造函数通常用来初始化实例对象的实例属性,如下代码就是初始化实例属性:`name`和`score` ```python def __init__(self, name, score): # self参数是必须有的 self.name = name # 实例属性 self.score = score # 实例属性 ``` 4. 通过“类名(参数列表)”来调用构造函数。调用后,将创建好的对象返回给相应的变量。比如:`s1=Student("张三",80)` 5. `__init__()`方法:==初始化创建好的对象==,**初始化指的是:“给实例属性赋值”** 6. `__new__()`方法:==用于创建对象==,但我们一般无需重定义该方法 7. 如果我们不定义`__init__`方法,系统会提供一个默认的`__init__`方法。如果我们定义了带参的`__init__`方法,系统不创建默认的`__init__`方法 > Python中的`self`相当于C++中的`self指针`,JAVA和C#中的`this`关键字。Python中`self`必须为构造函数的第一个参数,名字可以任意修改。但一般惯例,都叫做`self` ![image-20231117204441814](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219990.png) ### 实例属性-内存分析 **实例属性** 实例属性是从属于实例对象的属性,也称为“实例变量”。他的使用有如下几个要点: 1. 实例属性一般在`__init__()`方法中通过如下代码定义: `self.实例属性名 = 初始值` 2. 在本类的其他实例方法中,也是通过`self`进行访问: `self.实例属性名` 3. 创建实例对象后,通过实例对象访问: `obj01=类名()` #创建和初始化对象,调用`__init__()`初始化属性 `obj01.实例属性名 = 值` #可以给已有属性赋值,也可以新加属性 ### 实例方法-内存分析方法调用过程-dir()-isinstance **实例方法** 实例方法是从属于实例对象的方法。实例方法的定义格式如下: ```python def 方法名(self [, 形参列表]): 函数体 ``` 方法的调用格式如下: ​ `对象.方法名([实参列表])` 要点: 1. 定义实例方法时,第一个参数必须为`self`。和前面一样,`self`指当前的实例对象。 2. 调用实例方法时,不需要也不能给`self`传参。`self`由解释器自动传参 > **函数和方法的区别** > > ①都是用来完成一个功能的语句块,本质一样。 > > ②方法调用时,通过对象来调用。方法从属于特定实例对象,普通函数没有这个特点 > ③直观上看,方法定义时需要传递`self`,函数不需要 ![image-20231117210158863](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219991.png) **其他操作** 1. `dir(obj)`可以获得对象的所有属性、方法 2. `obj.__dict__`对象的属性字典 3. `pass`空语句 4. `isinstance(对象,类型)`判断对象"是不是"指定类型” ### 类对象 **类对象** 我们在前面讲的类定义格式中, `class 类名:`。实际上,当解释器执行`class`语句时,就会创建一个类对象 ### 类属性-内存分析(创建类和对象的底层) 类属性是从属于“类对象"的属性,也称为“类变量”。由于,类属性从属于类对象,可以被所有实例对象共享。 类属性的定义方式: ```python class 类名: 类名变量 = 初始值 ``` 在==类中或者类的外面==,我们可以通过:`类名.类变量名` 来读写 ```python class Student: school = "HNU" # 类属性 count = 0 # 类属性 def __init__(self, name, score): self.name = name # 实例属性 self.score = score # 实例属性 Student.count = Student.count + 1 def say_score(self): # 实例方法 print("我的学校是:", Student.school) print("{0}的分数是{1}".format(self.name, self.score)) s1 = Student("john", 61) # s1是实例对象,自动调用__init__()方法 s2 = Student("jack", 91) s1.say_score() print("一共创建了{0}个Student对象".format(Student.count)) """ 我的学校是: HNU john的分数是61 一共创建了2个Student对象 """ ``` ![image-20231117212505195](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219992.png) ![image-20231117212336356](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311172219993.png) ### 类方法 ==类方法是从属于“类对象"的方法。==类方法通过装饰器`@classmethod`来定义,格式如下: ```python @classmethod def 类方法名(cls [, 形参列表]): 方法体 ``` 要点如下: 1. `@classmethod`必须位于方法上面一行 2. 第一个`cls`必须有;==`cls`指的就是“类对象"本身== 3. 调用类方法格式:`类名.类方法名(参数列表)`。参数列表中,不需要也不能给`cls`传值 4. **类方法中访问实例属性和实例方法会导致错误** 5. 子类继承父类方法时,传入cls是子类对象,而非父类对象(讲完继承再说) ```python class Student: school = "HNU" # 类属性 @classmethod def printSchool(cls): print(cls.school) Student.printSchool() ``` ### 静态方法 Python中允许定义与"类对象"无关的方法,称为“静态方法”。 “静态方法”和在模块中定义普通函数没有区别,只不过“静态方法”放到了“类的名字空间里面”,需要通过“类调用”。 静态方法通过装饰器`@staticmethod`来定义,格式如下: ```python @staticmethod def 静态方法名([形参列表]): 方法体 ``` 要点如下: 1. `@staticmethod`必须位于方法上面一行 2. 调用静态方法格式:`类名.静态方法名(参数列表)` 3. 静态方法中访问实例属性和实例方法会导致错误 ```python class Student: school = "HNU" # 类属性 @staticmethod def add(a, b): # 静态方法 print("{0}+{1}={2}".format(a, b, a + b)) return a+b Student.add(30, 40) ``` ### del析构方法-垃圾回收机制简介 ==Python实现自动的垃圾回收== `__del__()`称为“析构方法”,用于实现对象被销毁时所需的操作。比如:释放对象占用的资源,例如:打开的文件资源、网络连接等。 Python实现自动的垃圾回收,当对象没有被引用时(引用计数为0),由垃圾回收器调用`__del__()`。 我们也可以通过`del语句`删除对象,从而保证调用`__del__()`。 系统会自动提供`__del__`方法,一般不需要自定义析构方法。 ```python # 析构函数 class Person: def __del__(self): print("销毁对象:{}".format(self)) p1 = Person() p2 = Person() del p2 print("程序结束") """ 销毁对象:<__main__.Person object at 0x0000021E3FE14FA0> 程序结束 销毁对象:<__main__.Person object at 0x0000021E3FE14FD0> """ ``` ### call方法和可调用对象 Python中,凡是可以将`()`直接应用到自身并执行,都称为可调用对象。 可调用对象包括自定义的函数、Python内置函数、以及本节所讲的实例对象。 定义了`__call__()`的对象,称为==“可调用对象”,即该对象可以像函数一样被调用==。 该方法使得实例对象可以像调用普通函数那样,以`"对象名()"`的形式使用。 ```python def f1(): print("f1") f1() # 本质也是调用了__call__()方法 """f1""" class Car: def __call__(self, age, money): print("call方法") print("车龄{0},金额{1}".format(age, money)) c = Car() c(3, 20000) """ call方法 车龄3,金额20000 """ ``` ### 方法没有重载-方法的动态性 ![image-20231117221944194](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753886.png) 如果我们在类体中定义了多个重名的方法,只有最后一个方法有效。 建议:==不要使用重名的方法!Python中方法没有重载。== > 在其他一些语言(比如:Java)中,可以定义多个重名的方法,只要保证方法签名唯一即可。方法签名包含3个部分:方法名、参数数量、参数类型。 > Python中,方法的的参数没有声明类型(调用时确定参数的类型),参数的数量也可以由可变参数控制。因此,Python中是没有方法的重载的。 ![image-20231118140905523](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753888.png) ### 私有属性和私有方法 ![image-20231118141259542](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753889.png) Pytho对于类的成员没有严格的访问控制限制,这与其他面向对象语言有区别。关于私有属性和私有方法,有如下要点: ①通常我们约定,**两个下划线开头的属性是私有的(private)**。其他为公 共的(public)。 ②类内部可以访问私有属性(方法) ③类外部不能直接访问私有属性(方法) ④类外部可以通过 `_类名__私有属性(方法)名` 访问私有属性(方法) > 【注】==方法本质上也是属性!==只不过是可以通过()执行而已。 > > 所以,此处讲的私有属性和公有属性,也同时讲解了私有方法和公有方法的用法。 > > 如下测试中,同时也包含了私有方法和公有方法的例子。 ```python class Employee: __company = "Ailibab" # 私有属性,解释器运行时把__company转化成_Employee__company def __init__(self, name, age): self.name = name self.__age = age # 私有属性 def say_company(self): print("我的公司名字是:", Employee.__company) print("我的年龄是:", self.__age) def __work(self): # 私有方法 print("好好工作") print(Employee._Employee__company) a = Employee("haha", 20) a.say_company() print(a._Employee__age) # 调用私有属性 a._Employee__work() # 调用私有方法 """ Ailibab 我的公司名字是: Ailibab 我的年龄是: 20 20 好好工作 """ ``` ### @property装饰器 `@property`可以将一个**方法的调用方式变成“属性调用”**。 `@property`主要用于帮助我们**处理属性的读操作、写操作**。对于某一个属性,我们可以直接通过: ​ `emp1.salary= 30000` 如上的操作读操作、写操作。但是,这种做法不安全。比如,我需要限制薪水必须为`1-10000`的数字。这时候,我们就需要通过使用装饰器`@property`来处理。 ```python class Employee: def __init__(self, name, salary): self.name = name self.__salary = salary @property # 只能读 print(emp1.salary) 相当于属性的调用 def salary(self): print("薪资是:", self.__salary) return self.__salary @salary.setter # 修改使用这个函数 emp1.salary = 50000 def salary(self, salary): if 0 < salary < 100000: self.__salary = salary else: print("薪资录入错误!只能在0-100000之间") emp1 = Employee("john", 20000) emp1.salary = 50000 print(emp1.salary) emp1.salary = 100000000 print(emp1.salary) """ 薪资是: 50000 50000 薪资录入错误!只能在0-100000之间 薪资是: 50000 50000 """ ``` ### 属性和类的命名规则总结 **属性和方法命名总结** `_xxx`:保护成员,不能用`from module import *` 导入,只有类对象和子类对象能访问这些成员。 `__xxx__`:系统定义的特殊成员 `__xxx`:类中的私有成员,只有类对象自己能访问,子类对象也不能访问。(但,在类外部可以通过 `对像名._类名__xxx` 这种特殊方式访问。Python不存在严格意义的私有成员) > :warning:再次强调,方法和属性都遵循上面的规则。 **类编码风格** 1. **类名首字母大写,多个单词之间采用驼峰原则。** 2. **实例名、模块名采用小写,多个单词之间采用下划线隔开** 3. 每个类,应紧跟“文档字符串”,说明这个类的作用 4. 可以用空行组织代码,但不能滥用。在类中,使用一个空行隔开方法;模块中,使用两个空行隔开多个类 ### None对象的特殊性 **None是什么?** 1. 与C和JAVA不同,Pythont中是没有`NULL`的,取而代之的是`None`。 2. `None`是一个特殊的常量,表示变量没有指向任何对象。 3. 在Python中,`None`本身实际上**也是对象**,有自己的类型`NoneType`。 4. 你可以将`None`赋值给任何变量,但我们不能创建`NoneType`类型的对象

None不是False,None不是0,None不是空字符串。None和任何其他的数据类型比较永远返回False。

**空列表、空字符串、0之间的比较** 1. if语句判断时,空列表[]、空字典{}、空元组()、空字符串、0、None等一系列代表空和无的对象会被转换成False 2. `==`和`is`判断时,空列表、空字符串不会自动转成`False` ### 面向对象的三大特征说明(封装、继承、多态) ![image-20231118165047726](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753890.png) ==Python是面向对象的语言,支持面向对象编程的三大特性:继承、封装(隐藏)、多态。== **封装(隐藏)** > 隐藏对象的属性和实现细节,只对外提供必要的方法。相当于将“细节封装起来”,只对外暴露”相关调用方法”。 > > 通过前面学习的“==私有属性、私有方法"的方式,实现封装==”。Pythoni追求简洁的语法,没有严格的语法级别的“访问控制符”,更多的是依靠程序员自觉实现。 **继承** > 继承可以让子类具有父类的特性,提高了代码的重用性。 > > 从设计上是一种==增量进化==,原有父类设计不变的情况下,可以增加新的功能,或者改进已有的算法。 **多态** > 多态是指==同一个方法调用由于对象不同会产生不同的行为==。 > > 生活中这样的例子比比皆是:同样是休息方法,人不同休息方法不同。张三休息是睡觉,李四休息是玩游戏,程序员休息是“敲几行代码”。 ### 继承详解 ![image-20231118165634687](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753891.png) 继承是面向对象编程的三大特征之一。继承让我们更加容易实现类的扩展。实现代码的重用,不用再重新发明轮子(don't reinvent wheels)。 如果**一个新类继承自一个设计好的类,就直接具备了已有类的特征**,就大大降低了工作难度。已有的类,我们称为“父类或者基类”。新的类,我们称为“子类或者派生类”。 ![image-20231118165736909](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753892.png) **语法格式** Python支持==多重继承,一个子类可以继承多个父类==。继承的语法格式如下: ```python class 子类类名(父类1[, 父类2, ...]): 类体 ``` > 如果在类定义中没有指定父类,则默认父类是`object类`。也就是说,`object`是所有类的父类,里面定义了一些所有类共有的默认实现,比如:`__new__()` **关于构造函数:** 1. 子类不重写`__init__`,实例化子类时,会自动调用父类定义的`__init__`。 ```python class Person: def __init__(self, name, age): print("创建Person") self.name = name self.age = age def say_age(self): print("{0}的年龄是{1}".format(self.name, self.age)) class Student(Person): pass s1 = Student("haha", 20) s1.say_age() print(dir(s1)) """ 创建Person haha的年龄是20 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age'] """ ``` 2. 子类重写了`__init__`时,实例化子类,就不会调用父类已经定义的`__init__` ```python class Person: def __init__(self, name, age): print("创建Person") self.name = name self.age = age def say_age(self): print("{0}的年龄是{1}".format(self.name, self.age)) class Student(Person): def __init__(self, name, age, score): # 两种调用父类的构造方法 # 1. # Person.__init__(self, name, age) # 2. super(Student, self).__init__(name, age) print("创建Student") self.score = score def say_score(self): print("我的分数:", self.score) s1 = Student("haha", 20, 90) s1.say_score() print(dir(s1)) """ 创建Person 创建Student 我的分数: 90 ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age', 'say_score', 'score'] """ ``` 3. 如果重写了`__init__`时,要使用父类的构造方法,可以使用`super`关键字,也可以使用如下格式调用: ​ `父类名.__init__(self, 参数列表)` ```python class Person: def __init__(self, name, age): print("创建Person") self.name = name self.age = age def say_age(self): print("{0}的年龄是{1}".format(self.name, self.age)) class Student(Person): def __init__(self, name, age, score): # 两种调用父类的构造方法 # 1. # Person.__init__(self, name, age) # 2. super(Student, self).__init__(name, age) print("创建Student") self.score = score s1 = Student("haha", 20, 90) print(dir(s1)) """ 创建Person 创建Student ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age', 'score'] """ ``` ### 类成员继承和方法的重写 **成员继承**:==子类继承了父类除构造方法(`__init__()`)之外的所有成员。== > :warning:(私有属性、私有方法也被继承) ```python class Person: def __init__(self, name, age): print("创建Person") self.name = name self.__age = age # 私有属性也会被继承 def say_age(self): print("{0}的年龄是{1}".format(self.name, self.__age)) class Student(Person): def __init__(self, name, age, score): # 两种调用父类的构造方法 # 1. # Person.__init__(self, name, age) # 2. super(Student, self).__init__(name, age) print("创建Student") self.score = score def say_score(self): print("我的分数:", self.score) s1 = Student("haha", 20, 90) s1.say_age() s1.say_score() print(dir(s1)) """ 创建Person 创建Student haha的年龄是20 我的分数: 90 ['_Person__age', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'name', 'say_age', 'say_score', 'score'] """ ``` **方法重写**:子类可以重新定义父类中的方法,这样就会覆盖父类的方法,也称为“重写” ```python class Person: def __init__(self, name, age): print("创建Person") self.name = name self.age = age def say_age(self): print("{0}的年龄是{1}".format(self.name, self.age)) def say_name(self): print("我的名字是:", self.name) class Student(Person): def __init__(self, name, age, score): Person.__init__(self, name, age) print("创建Student") self.score = score def say_score(self): print("我的分数:", self.score) def say_name(self): # 重写父类的方法 print("hello ,my name is ", self.name) s1 = Student("haha", 20, 90) s1.say_age() s1.say_score() s1.say_name() print(dir(s1)) """ 创建Person 创建Student haha的年龄是20 我的分数: 90 hello ,my name is haha ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age', 'say_name', 'say_score', 'score'] """ ``` **查看类的继承层次结构** 通过类的方法`mro()`或者类的属性`__mro__`可以输出这个类的继承层次结构。 ```python class A: pass class B(A): pass class C(B): pass print(C.mro()) """ [, , , ] """ ``` ![image-20231118173835027](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753893.png) ### object根类-查看模块结构-dir() ![image-20231118174521095](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753894.png) > ==`object`类是所有类的父类==,因此所有的类都有`object`类的属性和方法。我们显然有必要深入研究一下`object`类的结构。对于我们继续深入学习Python很有好处。 **`dir()`查看对象属性** 为了深入学习对象,先学习内置函数dir(),他可以让我们方便的看到指定对象所有的属性 > :warning:快捷键 Alt+ 7 打开模块结构 ![image-20231118175130469](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753895.png) ```python class Person: def __init__(self, name, age): print("创建Person") self.name = name self.age = age def say_age(self): print("{0}的年龄是{1}".format(self.name, self.age)) # 快捷键 Alt+ 7 打开模块结构 obj = object() print(dir(obj)) s2 = Person("haha", 20) print(dir(s2)) # 方法的本质也是是属性 print(s2.say_age) print(type(s2.say_age)) """ ['__class__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__'] 创建Person ['__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'age', 'name', 'say_age'] > """ ``` **从上面我们可以发现这样几个要点:** 1. Person对象增加了六个属性:`__dict__` `__module__` `__weakref__` `age` `name` `say_age` 2. `object`的所有属性,`Person`类作为`object`的子类,显然包含了所有的属性 3. 我们打印`age`、`name`、`say_age`,发现`say_age`虽然是==方法,实际上也是属性==。只不过这个属性的类型是method而已。 ```python age name say_age ``` > 关于objecti这些属性的详细学习,会后面学习中逐个涉及。在此,没必要展开。 ### 重写`__str__()`方法 1. `object`有一个`__str()__`方法,用于返回一个对于"对象的描述”。内置函数`str(对象)`,调用的就是`__str()__` 2. `__str()__`经常用于`print()`方法,帮助我们查看对象的信息。`__str()__`可以重写 ```python class Person: def __init__(self, name, age): self.name = name self.age = age def __str__(self): """将对象转化成一个字符串描述,一般用于print方法""" print("重写str方法") return "名字是:{0},年龄是{1}".format(self.name, self.age) p = Person("haha", 20) print(p) s = str(p) """ 重写str方法 名字是:haha,年龄是20 重写str方法 """ ``` ### 多重继承 ![image-20231118181906059](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753896.png) > Pytho支持多重继承,一个子类可以有多个“直接父类"。这样,就具备了“多个父类"的特点。但是由于,这样会被“类的整体层次"”搞的异常复杂,==尽量避免使用==。 ### MRO方法解析顺序 Pythor支持多继承,如果父类中有相同名字的方法,在子类没有指定父类名时,解释器将**“从左向右”**按顺序搜索。 MRO(Method Resolution Order):方法解析顺序。我们可以通过`mro()`方法获得"类的层次结构",方法解析顺序也是按照这个“类的层次结构”寻找的。 ### super()获得父类的定义 在子类中,如果想要获得父类的方法时,我们可以通过`super()`来做。 `super()`代表**父类的定义,不是父类对象**。 > 想调用父类的构造方法: > > `super(子类名称, self).__init__(参数列表)` ```python class Person: def __init__(self, name, age): print("创建Person") self.name = name self.__age = age # 私有属性也会被继承 def say_age(self): print("{0}的年龄是{1}".format(self.name, self.__age)) class Student(Person): def __init__(self, name, age, score): # Person.__init__(self) super(Student, self).__init__(name, age) # 调用父类的构造方法 print("创建Student") self.score = score def say_age(self): # Person.say_age(self) super().say_age() # 通过super()调用父类的方法 ``` ### 多态详解 ![image-20231119160951739](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753897.png) **多态(polymorphism)是指同一个方法调用由于对象不同可能会产生不同的行为。** 关于多态要注意以下2点: 1. **多态是方法的多态,属性没有多态**。 2. 多态的存在有==2个必要条件:继承、方法重写== ```python class Animal: def shout(self): print("动物叫了一声") class Dog(Animal): def shout(self): print("小狗叫了一声") class Cat(Animal): def shout(self): print("小猫叫了一声") def animalShout(a): a.shout() # 会产生多态,传入对象不同,则调用方法不同 animalShout(Dog()) animalShout(Cat()) """ 小狗叫了一声 小猫叫了一声 """ ``` ### 特殊方法和运算符重载 ![image-20231119162419296](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753898.png) ![image-20231119162554609](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753899.png) ![image-20231119162639978](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753900.png) ### 特殊属性 ![image-20231119162849422](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753901.png) ### 浅拷贝和深拷贝对象-内存分析 ![image-20231119163101534](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753902.png) ### 继承和组合 ![image-20231119164344071](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753903.png) 除了继承,“组合”也能实现代码的复用!“**组合"核心是“将父类对象作为子类的属性”**。 > `is-a`关系,我们可以使用"**==继承==**”。从而实现子类拥有的父类的方法和属性。`is-a`关系指的是类似这样的关系:狗是动物,dog is animal。.狗类就应该继承动物类。 > > `has-a`关系,我们可以使用"==组合==”,也能实现一个类拥有另一个类的方法和属性。`has-a`关系指的是这样的关系:手机拥有CPU。MobilePhone has a CPU ### 设计模式 设计模式是面向对象语言特有的内容,是我们在面临某一类问题时候固定的做法,设计模式有很多种,比较流行的是:GOF(Goup Of Four)23种设计模式。当然,我们没有必要全部学习,学习几个常用的即可。 对于初学者,我们学习两个最常用的模式:工厂模式和单例模式。 #### 工厂模式实现 工厂模式实现了**创建者和调用者的分离**,使用专门的工厂类将选择实现类、创建对象进行统一的管理和控制。 ![image-20231119165631900](https://gitee.com/huaiyuechusan/picture/raw/master/Typora/202311191753904.png) ```python # 工厂模式实现 class Benz: pass class BMW: pass class BYD: pass class CarFactory: def createCar(self, brand): if brand == "宝马": return BMW() elif brand == "奔驰": return Benz() elif brand == "比亚迪": return BYD() else: return "未知品牌,无法创建" factory = CarFactory() c1 = factory.createCar("奔驰") c2 = factory.createCar("宝马") print(c1) print(c2) """ <__main__.Benz object at 0x0000021C16D12FA0> <__main__.BMW object at 0x0000021C16D12F70> """ ``` #### 单例模式实现 单例模式(Singleton Pattern)的核心作用是确保**一个类只有一个实例**,并且提供**一个访问该实例的全局访问点**。 单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可以产生一个“单例对象”,然后永久驻留内存中,从而极大的降低开销。

单例模式有多种实现的方式,我们这里推荐重写__new__()的方法。

```python # 单例模式实现 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("初始化第一个对象...") self.name = name MySingleton.__init_flag = False a = MySingleton("aa") print(a) b = MySingleton("bb") print(b) """ 初始化第一个对象... <__main__.MySingleton object at 0x000001E70B8E2FA0> <__main__.MySingleton object at 0x000001E70B8E2FA0> """ ``` #### 工厂和单例模式结合起来 设计模式称之为“模式”,就是一些固定的套路。我们很容易用到其他场景上,比如前面讲的工厂模式,我们需要将工厂类定义成“单例”,只需要简单的套用即可实现: ```python # 工厂和单例模式结合 class CarFactory: __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): if CarFactory.__init_flag: print("初始化第一个对象...") CarFactory.__init_flag = False def createCar(self, brand): if brand == "宝马": return BMW() elif brand == "奔驰": return Benz() elif brand == "比亚迪": return BYD() else: return "未知品牌,无法创建" class Benz: pass class BMW: pass class BYD: pass factory = CarFactory() c1 = factory.createCar("奔驰") c2 = factory.createCar("宝马") print(c1) print(c2) factory2 = CarFactory() print(factory) print(factory2) """ 初始化第一个对象... <__main__.Benz object at 0x0000018C683F3F70> <__main__.BMW object at 0x0000018C683F3F10> <__main__.CarFactory object at 0x0000018C683F3FA0> <__main__.CarFactory object at 0x0000018C683F3FA0> """ ``` ## Python开发环境搭建 ### PyCharm搭建Python环境-非虚拟环境 ### VSCode搭建Python环境-非虚拟环境 ### 虚拟环境使用 ### PyCharm使用虚拟环境 ### VSCode使用虚拟环境 ### 虚拟环境管理工具