Python-2020-fall
跳到导航
跳到搜索
Python程序设计课程主页(2020年秋季学期)
Teacher: 李正华
Teaching Assistant: 周厚全、刘泽洋、周仕林
上课时间和地点、QQ群
周一13:30-15:20卫校301; 周五13:30-15:20理工楼153 周二13:30-15:20理工楼238、247(上机); python-2020学习交流QQ群:893402501
教材的名字:零基础学习并理解Python 特点:1)把重点放到理解上,建立基本概念;2)重视英语学习,尽可能多的给出英语术语翻译;3)多画流程图、多给出图例,考虑latex画图。
考试安排
三次正式考试
- 第一次考试:11.11(周三),考试内容:基础、分支、列表、循环 (考试成绩)
- 第二次考试:12月9日(周三)晚上:元组 字符串 字典 集合
- 第三次考试(期末):1月
- 考试形式:上机考试
- 选择题
- 编程题:每一道题对应一个函数(给定了函数名,不能随便修改),所有函数放到一个.py文件中
2020.11.3 模拟考试
- 考试时间:2020.11.3 13:45-15:00
- 考试题型:5道选择题(不会没关系,快速做完)、3道编程题
- 考试内容:2020-11-03-模拟考试试题(zip包的解压缩密码是:Py202011031230)
- 模拟考试总结
常见问题: a. 语法错误:程序中出现语法问题将会导致判为0分: 1. if中出现逗号,例如: if a0, a1, a2, a3, a4, a5==0: 2. else后面带表达式:此处应该用elif 3. 程序不完整(unexpected EOF):例如用if...else...时,else直接一个冒号就结束了,如果想空掉某些位置请使用pass 4. if后没有冒号 5. 小于等于运算符写错:应该是<=而不是=< 6. 重复def 7. 使用了中文的括号 8. 缩进问题 9. 使用未赋值的变量,尤其是在if判断的时候会出现这样的问题,例如: if x>0: a = 1 print(a) b. 变量使用问题:在使用一个变量时,应该首先确保这个变量已经被赋值,同上。 c. 在代码中使用input导致超时:我们调用你的程序会遇到input()导致无限等待。 d. 误用中括号,例如: return x, [.....]/5 e. 其他审题要清楚,注意特殊情况下的结果是否与题目给出的样例一致。 注意:考试时,提交的.py中不能有input() print()。
2020.11.10模拟考试
- 考试时间:2020.11.10 13:40-15:00
- 考试题型:1道选择题、3道编程题
- 考试内容:2020-11-10-模拟考试试题
Mooc混合式教学
中国大学慕课 Python程序设计 18新利体育 朱晓旭等
请同学们同步学习这个网课。
理论课上,我会留一些时间,来针对网课的内容进行讲解、答疑。
参考资料
实验课安排(有问题多主动问,Python不是教会的,而是不断动手、思考学会的)
实验报告说明
实验报告由两部分组成:
- 实验报告:学号-exp-x.pdf (x表示第几次实验)
- 注意文件命名格式,必须用pdf文件
- 包含题目、流程图、解题思路、运行结果截图、遇到什么问题及如何解决的、总结有哪些收获、对老师的建议
- 每次会提供一个基本的实验模板,学生按照自己的方式去安排报告的内容
- 选择三道较难的题目,先画流程图,然后写代码。可以手画流程图然后拍照,也可以用画图软件去画。其他题目不用画流程图。
- 代码:学号-exp-x.py
- 把所有题目的代码放到一个py文件中,不要压缩,在提交的时候点击附件,上传相应实验的.py文件。
- 注意可读性,方便老师批改,可以写一些必须的注释
注意事项:
- 抄袭会严惩!当次实验成绩清0!
- 实验报告、代码,要保留好,不要删除,期末的时候可能要统一放到一个文件夹中,交给老师。
- 除了基本的题目,还可以额外做老师在课堂上提出的思考题、扩展题。
- 根据认真程度、完成的质量、可读性等,来综合评分
- 每次作业批改后,会在理论课或上机课上进行讲解,重点是讲大家常见的错误。
- 如果系统中没有给出分数,则可以撤销提交并重新提交。
往年的习题集:
扩展题目(选做,可以放到实验报告中)
顺序结构题目: 1)输入三个变量的值,然后按小到大顺序输出。 2)BMI指数(身体质量指数,Body Mass Index)是目前国际上常用的衡量人体胖瘦程度及是否健康的一个标准。 体质指数(BMI)=体重(kg)÷身高^2(m) 3)编写程序实现华氏温度到摄氏转换 转换公式: 摄氏度C=(华氏度-32)乘以(5/9) 华氏度F=32+(9/5) 乘以 摄氏度C 4)从键盘输入一个三位整数n,输出其逆序数m。例如,输入n=127,则m=721。 ----- 分支结构题目: (1)判断年份year是否为闰年。 (2)判断ch是否为小写字母。 (3)判断m能否被n整除。 (4)判断ch既不是字母也不是数字字符。 (5)输入年月,求该月的天数。 (6)输入一个时间(小时:分钟:秒),输出该时间经过5分30秒后的时间 (7)根据bmi情况,输出不同的信息: if bmi<18.5: print("过轻") elif bmi<25: print("正常") elif bmi<28: print("过重") elif bmi<32: print("肥胖") else: print("非常肥胖") ----- 循环结构题目: 从m到n数字求和 判断一个数字是否为素数(质数) 判断一个字符串是否为回文('abccba', 'aba') 求一个浮点数(>0)的根号,可以有多种方法。例如:平方根迭代公式x1=1/2*(x0+a/x0) 求两个整数a与b的最大公约数、最小公倍数 输出[100,1000)以内的全部完数(因子之的和等于自己,如6=1+2+3) 用字符'*'输出不同的形状,如等边三角形,等腰三角形,直角三角形 打印9*9乘法表 角谷猜想: 是指对于任意一个正整数 如果是奇数,则乘3加1 如果是偶数,则除以2 得到的结果再按照上述规则重复处理 最终总能够得到1 欧几里得算法(辗转相除法)求最大公约数 num1,num2=eval(input("请输入两个正整数")) if num1<num2: num1,num2=num2,num1 #保证num1大 while num1%num2!=0: temp=num1%num2 num1=num2 num2=temp print("最大公约数是:",num2) 哥德巴赫猜想 哥德巴赫->欧拉 任一大于2的偶数都可表示为两个素数之和 1+1 陈景润 任何一个充分大的偶数都可以表示成一个素数和一个不超过两个素数的乘积之和 1+2 猜数字 计算机产生一个1-10000之间的随机数 人去猜 计算机提示 偏大 偏小 猜对 显示猜的人所猜的次数 ----- 列表扩展题目: 例:给定一个数字列表,将每个元素求修改为其绝对值,然后返回该列表 l9 = [1,-3.39,7,-999] 例:从键盘输入数字到列表(输入非数字则停止),并对列表进行排序(从小到大),然后返回列表 例:给定一个列表,返回某元素在列表中的所有下标,作为一个列表返回 l1 = [1,'a',2,3] value = 1 例:实现reverse功能 例:二分查找(一定要亲手实现一下) 例:排序算法 例:矩阵乘法实现:随机产生矩阵中的数字,用嵌套列表来存储
- 2020.10.26:输出当前时间,新输出的时间刷新旧时间。(提示,利用退格符,PyCharm可以,用cmd也可以,IDLE不行)
- 2020.10.23:求平方根,是否还有其他更快的解法(之前讲的是二分查找)
- 2020.10.15:把求平方根的代码,扩展为x为任何大于0的浮点数。注意一定要先画流程图。
思考题目
- 有100只一模一样的苹果,编号1-100。其中99个是正常的,一个被注射了毒药。只要老鼠咬一口毒苹果,一天后则死亡。现在,你有7只老鼠和一天的时间,如何检验出哪个号码的苹果被注射了毒药?
- 蒙特卡洛法求圆周率 pi。正方形面积、圆的面积。当然,圆的面积公式又是怎么来的呢?
实验课常见问题
2020.10.20:
- input()函数从用户得到的输入类型是字符串,不能直接进行数值的运算,需要类型转换。
- 希望一次性input多个数字,可以用字符串split方法:
- lst = list(map(int, input('please input:').split())) #一次性输入多个数字,按空格隔开;并存储到一个列表中
- x, y, z = eval(input('please input two int, using comma as the delimiter: ')) # 输入 3, 5, -4
- lst = eval(input('please input a python list: ')) # 输入 [3, 5, -4],中括号必须有,作为列表对象存储
- a_tuple = eval(input('please input several int, using comma as the delimiter: ')) # 输入 3, 5, -4,作为元组对象存储
- 注意变量 x 与字符串 "x" 之间的区别。
第一次实验报告 2020.10.20 python语言基础
- 截止时间:11月4日20:00前
- 报告模板(含基本题目),见csteaching
第二次实验报告 2020.11.3 顺序结构程序设计
- 截止时间:11月15日20:00前
- 报告模板
第三次实验报告 2020.11.10 选择结构程序设计
- 截止时间:11月28日20:00前
- 报告模板
- 学生建议:
建议拓展题目不要有包含关系或同类题目,最好少而难。 希望老师能在实验报告后对较难的题目给出较为简便高效的代码,以便于学习。 希望老师上课多讲点知识,有条理一些。 每节理论课可以在群里提前说一下下节课内容,有所准备的话,在课上可以更好的理解。 希望老师能够对稍微大型些的程序做些分析和讲解。
第四次实验报告 2020.11.17 列表
- 截止时间:12月01日20:00前
- 报告模板
- 学生建议:
希望课程主页上能多更新一点课外拓展的题目。另外,希望老师能提供一点选择题的资料或者练习之类,或者以往几届考试的选择题和题库都可以。 希望老师可以讲讲多个循环嵌套的例子。 建议老师在每次实验报告截止日期后发一份标准答案,并在上机课上讲解较难的题目。 对老师的建议是希望可以把魔方阵简单说一下。 建议老师上课把重点函数点出来,过一遍基本没人能记住,有些不常用的看一眼就行了 希望老师讲快一点
第五次实验报告 2020.11.24 循环结构程序设计
- 截止时间:12月08日20:00前
- 报告模板
第六次实验报告 2020.12.01 字符串与正则表达式
- 截止时间:12月31日20:00前
- 报告模板
第七次实验报告 2020.12.08 元组、字典、集合
- 截止时间:12月25日20:00前
- 报告模板
- 题目订正
9. 使用random模块生成一个整数类型的随机数集合:从1到10(包括10)中随机选择一个数n作为采样次数,从[0,1000]范围内采样n个随机数,并将这些数字组成集合A。同理,按此方法生成集合B。在此基础上实现以下功能: a) 显示A和B的结果。要求每行最多显示10个数,每个数占5列,右对齐; b) 要求用户输入 A | B 和 A & B 的结果,并告诉用户他(或她)的答案是否正确。如果用户回答错误,允许他(或她)修改解决方案,然后重新验证用户输入的答案。如果用户三次提交的答案均不正确,程序将显示正确结果。
第八次实验报告 2020.12.15 函数
- 截止时间:2021年1月3日20:00前
- 报告模板
第九次实验报告 2020.12.22 文件操作
- 截止时间:2021年1月5日20:00前
- 报告模板
讲给同学们的话
大学生活、学习的一些建议
- 计算机英语很重要(1000左右单词),有助于理解和记忆;
- 逻辑思维能力很重要,从流程图(或伪代码)锻炼起;
- 多动手、多练习、多思考、多尝试,才能学好编程;
- 编程只是计算机科学与技术这门学科的最基本能力。要想成为顶尖的编程高手,必须对计算机的硬件、操作系统、数据结构、算法等基础理论理解透彻,所以要长期坚持,不断提高自己的计算机素养和基础。
2021.1.1 元旦,少一次课 2020.10.23 少一次课。希望大家利用各种资源,抓紧学习。我的课主要以理解为主,把知识串起来。要学好python,要不断写代码、调试。要靠自己 第5周周五少了一次课
讲义记录
TODO
正则表达式 函数 模块 文件 异常 6次课:11 14 18 21 25 28 需要澄清的概念: 转义字符 特殊字符 backslash反斜杠字符\ 标量Scalar(integer, floating-point number, boolean) 矢量Non-Scalar(字符串string等) python的特点 high-level(python > c++ > c > 汇编) C语言能做的,Python都能做?感觉不一定 解释性(interpretative) 面向对象(object) 跨平台、移植性高 2.0和3.0不兼容,语法有一些小的变化
学习朱晓旭老师的PPT
in和not in 测试(成员测试,列表、字符串等) isinstance(3, int) 用单引号或双引号括起来的字符串不可跨行 用三引号括起来的字符串可以是多行的 格式字符串:% format() str.format() 原始字符串:如果不希望字符串转义,前面加r r”\n” 如果转义后没有意义,那么就是两个字符,如'\A' 转义字符:以反斜杠“\”开头,后跟一个或多个字符(反斜杠称为转义字符,表示的字符称为特殊字符?怎么说比较清晰呢,得看看英文文献,确认一下?) 转义字符具有特定的含义 不同于字符原有的意义,故称转义字符, 主要用来表示那些用一般字符不便于表示的控制代码 eval()函数 其调用格式为: eval(字符串) 作用是把字符串的内容作为对应的Python语句(表达式?)来执行 表达式: 单个任何类型的对象或常数 使用运算符连接的变量和常量 函数调用的任意组合 什么是表达式?eval('x=3') vs. x=eval('3')
- Python代码规范
缩进: 类定义、函数定义、选择结构、循环结构,行尾的冒号表示缩进的开始 python程序是依靠代码块的缩进来体现代码之间的逻辑关系的,缩进结束就表示一个代码块结束了。 同一个级别的代码块的缩进量必须相同。 一般而言,以4个空格为基本缩进单位,可以通过下面的方法进行代码块的缩进和反缩进: 注释: 一个好的、可读性强的程序一般包含30%以上的注释。常用的注释方式主要有两种: 以#开始,表示本行#之后的内容为注释 包含在一对三引号'''...'''或"""..."""之间且不属于任何语句的内容将被解释器认为是注释 每个import只导入一个模块 如果一行语句太长,可以在行尾加上\来换行分成多行,但是更建议使用括号来包含多行内容。 必要的空格与空行: 运算符两侧、函数参数之间、逗号两侧建议使用空格分开。 不同功能的代码块之间、不同的函数定义之间建议增加一个空行以增加可读性。 适当使用异常处理结构进行容错,后面将详细讲解。 软件应具有较强的可测试性,测试与开发齐头并进。
python对中文的支持 encode decode(刘泽洋整理,还不清楚,后面要继续琢磨)
Python-2020-fall-python_character_set
异常
python通过异常机制来管理错误。Exception 编译错误(语法错误,compiling syntax): 运行时错误(run-time) 报错类型: syntaxError:语法错误 AssertionError:例如assert(0>1) NameError:变量名错误,变量名未定义 ZeroDivisionError:0作为除数报错 IndexError:index越界 KeyError:key不存在 FileNotFoundError:打开不存在的文件 ModuleNotFoundError:找不到import的模块 ValueError: Try-Catch(expect)-Throw(raise) try: normal-code-block except: error-handling-code-block float('abc') import math math.import(-2) try: x = 1/0 f = open('aaa') except: print('something wrong') try: x = 1/0 f = open('aaa') except Exception: print('something wrong') try: x = 1/0 f = open('aaa') except Exception as e: print('something wrong') raise e raise Exception('hi') try: x = 1/0 f = open('aaa') except ZeroDivisionError as e: print('something wrong, e') try: x = 1/arg1 f = open('aaa') except ZeroDivisionError as e: print('something wrong, e') except FileNotFoundError as e: print('file open error, e') else: # 如果上面语句块没有发生异常,执行 print('no error found') finally: # 不管有没有异常,都会执行,如清理行为(关闭文件等) print('cleanup') ----- 抛出异常: raise(Exception('abc')) 可以通过类继承,自定义异常类型 ----- with open('abc','w') as f: #不管发不发生异常,最后都会把f关掉 print('hi') ----- 断言assertion(防御型编程,非常好的习惯) assert expression 等价于: if not expression: raise AssertionError assert expression [, arguments] 等价于 if not expression: raise AssertionError(arguments)
模块
import prime100 import python2.prime100 (package,通过文件目录结构来组织代码(层次化的)) python20.prime100.is_prime(10) 使用上层目录中的模块,如: python20/abc/ import sys sys.path.append("..") ----- import时到底发生了什么? from mod import (x,foo) 把名字引入到当前命名空间 可以认为,相当于你在当前程序中x=3,def foo() import 进来的名字和本来已有的名字有冲突时,两者合二为一,是同一个名字 可以起别名:from mod import (x as xx,foo as foo)。此时x的别名为xx,foo的别名为ffoo 将模块中对应的命名空间及里面的名字引入到当前环境中。 命名空间(namespace)解释:(1)通过什么路径找到一个名字;(2)名字在什么空间 ----- 多次import,只会执行一次 程序运行过程中,第一次import一个模块,会加载并执行此模块。 这个可以试一下。a b c三个模块,内部互相import。如果循环了会怎么样? 模块的一个特殊数据属性:__name__ 1.假如当前模块是主模块(也就是调用其他模块的模块),那么此模块名字就是__main__ 2.假如此模块是被import的,则此模块名字为import进来的模块名 from 模块名 import 对象名[ as 别名] #可以减少查询次数,提高执行速度 from math import * # 谨慎使用,相当于名字空间,可能导致盖被子情况 >>> from math import sin >>> sin(3) 0.1411200080598672 >>> from math import sin as f #别名 >>> f(3) 0.141120008059867 ----- 内嵌模块,预加载 dir(builtins): import builtins 也可以使用sys.modules.items()显示所有预加载模块的相关信息。 ----- DIY一个模块:已经讲过了。 random模块的学习和使用 什么是模块?py文件,文件夹组织时,如何import?【后期还会专门学习一下】 help(dir) dir(math) help(random) dir(random) help(time) dir(time) help(time.ctime) Python默认安装仅包含部分基本或核心模块,但用户可以安装大量的扩展模块,pip是管理模块的重要工具。 在Python启动时,仅加载了很少的一部分模块,在需要时由程序员显式地加载(可能需要先安装)其他模块。 减小运行的压力,仅加载真正需要的模块和功能,且具有很强的可扩展性。 可以使用sys.modules.items()显示所有预加载模块的相关信息。 import 模块名 >>>import math >>>math.sin(0.5) #求0.5的正弦 >>>import random >>>x=random.random( ) #获得[0,1) 内的随机小数 >>>y=random.random( ) >>>n=random.randint(1,100) #获得[1,100]上的随机整数 可以使用dir函数查看任意模块中所有的对象列表,如果调用不带参数的dir()函数,则返回当前脚本的所有名字列表。 可以使用help函数查看任意模块或函数的使用帮助。 如何自己写一个模块。模块和引用py文件的路径问题? time模块
文件
----- 重要注意事项:由于历史的原因,不同操作系统、语言处理\n\r的方式不同,包括一些隐式处理,所以要注意! https://www.runoob.com/python3/python3-file-methods.html 标准输入输出 文件输入输出设备 import sys print('hi',file=sys.stderr) # 往屏幕标准错误输出 ----- 打开和关闭文件,遍历文件 f = open('a.txt') for i in f: i = i.rstrip() # 把右边空白符去掉 print(i) f.close() ----- open函数 mode text/binary模式 t 文本模式 (默认) b 二进制模式 + 打开一个文件进行更新(可读可写),不能单独使用,必须结合rwa r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 r+ 打开一个文件用于读写。文件指针将会放在文件的开头。 rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 w和w+的区别,是否可以读(都会清空) r和r+的区别:是否可以写 r+(文件必须存在,不清空) vs. w+(文件不存在则新建,存在则清空) 默认为文本模式,如果要以二进制模式打开,加上b。 ----- 文本文件 vs. 二进制文件 文本文件:以字符串写入内容 二进制文件:内容对应整数和浮点数,图片 音频 视频 其他数据 文本文件是否可以用二进制方式读?可以 二进制文件是否可以以文本方式读?可以 f = open('test-file.bin', 'wb+') f.write(struct.pack('B', 97)) f.close() f = open('test-file.bin', 'r') f.readline() # 'a' f.close() ----- flush,写文件,要实时看到结果,需要flush ----- 字节流和字符串之间的互相转化: bytes.decode() str.encode() ----- 二进制读写:struct和pickle模块 struct模块:将int/float等数据转化为字节流、或反之。比较底层。 https://blog.csdn.net/qq_30638831/article/details/80421019 f = open('x.bin', 'wb') f.write(struct.pack('B', 97)) f.write(struct.pack('i', 97)) f.write(struct.pack('f', 97)) f.write(struct.pack('d', 97)) f.close() f = open('x.bin', 'rb') struct.unpack('B', f.read(1)) struct.unpack('i', f.read(r)) struct.unpack('f', f.read(r)) struct.unpack('d', f.read(8)) f.close() c char string of length 1 1 b signed char integer 1 B unsigned char integer 1 h short integer 2 H unsigned short integer 2 i int integer 4 I(大写的i) unsigned int integer 4 q long long long 8 f float float 4 d double float 8 s char[] string p char[] string pickle模块:序列化和反序列化Python对象,更方便 ----- 文件对象、方法: 读:返回读出来的内容 readline readlines read ----- 文件写: write: 返回成功写入的字符数 writelines() print ----- seek/tell https://www.runoob.com/python3/python3-file-seek.html fileObject.seek(offset[, whence]) offset:开始的偏移量,也就是代表需要移动偏移的字节数,如果是负数表示从倒数第几位开始。 whence:可选,默认值为 0。给 offset 定义一个参数,表示要从哪个位置开始偏移;0 代表从文件开头开始算起,1 代表从当前位置开始算起,2 代表从文件末尾算起。 ----- 文件读写混合 边读边写的操作文件,实时来看:) tell seek write read ----- 程序、进程 当前目录 进程 程序启动后产生进程。进程是动态的。 进程初始的当前工作目录是发起此进程的命令所在的目录。 import os os.getcwd() # 返回当前工作目录 os.chdir('..') # 改变当前工作目录到指定的路径
函数
----- 函数定义(语法):函数名、参数列表、返回值 def functionname([param]...)函数可以有0个或者任意多个参数([]表示可有可无) body return obj is_prime return 从函数中退出,并返回“对象”(注意理解:返回的是对象,不做名字绑定,无法继续处理) 没有返回对象的话,默认返回None exit() 内置函数、从程序中退出 >>> print(exit) Use exit() or Ctrl-D (i.e. EOF) to exit >>> type(exit) <class '_sitebuiltins.Quitter'> 循环相关: break 从循环中退出 continue 从循环的当前迭代中退出,进入下一次迭代 ----- 为什么需要函数: is_prime,一个是把列表中的prime相加,一个是把另一个列表中的非Prime保留,并排序 模块化、代码重用 ----- 函数对象 名字 类型(微信图片) ----- 匿名函数: foo = lambda x, y: x if x > y else y def foo(x,y) return x if x > y else y 函数可以作为函数的参数 例子1:排序 l1 = [['a',1],['b',20],[12,5]] sorted(l1,key=lambda i: i[1]) # 将第一个值作为key排序 例子2: def op_list(l, f): print(f(l)) op_list([1,2,3,4], min) 例子3:了解reduce map等函数 ----- 定义函数的注意事项: 1)不要和已有名字重名 x = 3 def x(): pass 关键字在任何语言中都是受保护的,会报错。关键字不能作为变量名也不可以用函数名。内置的类和我们自己定义的类是一样的,并没有像关键字那样严格受保护。 2)函数必须先定义再调用 和变量名一样,可以del,也可以重新绑定 ----- 函数的执行步骤: 参数名绑定:被调用函数有自己局部的环境。(很重要!) 顺序执行函数中的语句。 遇到return退出,并返回包含结果的obj或执行完所有语句退出默认返回None。 返回调用。 [https://note.youdao.com/ynoteshare1/index.html?id=65e5043c1e116e6471ed1a6738ba4f59&type=note 函数的执行过程参见课程视频] ----- 名字的生命域: 函数内部定义的名字 函数内部定义的x,什么时候死亡? ----- 全局环境 vs. 局部环境 全局名字 vs. 局部名字 def bar(m): print('x in bar() = %d' % x) def foo(n): x = 7 bar(x) print('x = %d' % x) x = 9 foo(x) print('x in global = %d' % x) # 输出 x = 7 # x in bar() = 9 # x在局部环境bar()中没找到,则在全局环境中找 # x in global = 9 函数内部可以引用,也可以定义全局变量 global x x = 3 总结: 全局环境指主程序所在的环境。局部环境指函数被调用时所在的环境。 全局环境中的变量名称为全局名字,名字前可能需要加上模块名来访问。 局部环境中的变量名称为局部名字。局部名字的生命域很短,只能在函数内部使用。 函数内部“引用”一个名字时,如局部环境找不到该名字,会尝试在全局环境找(隐式)。 函数内部的赋值语句会创建局部变量(如果不存在该局部名字,无论是否有同名的全局名字)。 函数中修改一个名字的绑定或者修改绑定的对象时,会认定这个变量为局部名字,而不会在全局环境中找出该名字进而做修改。为什么要这样呢?当然不允许了! 不允许在函数中隐式地修改全局变量。如果需要修改全局变量,需要用global关键字显式说明。 参数名字绑定时,会为每一个参数定义相应的局部名字。 ----- 调用函数时传递参数(传参)的两种方式 positional call keyword call def foo(x, y): print('x in foo() = %d' % x) print('y in foo() = %d' % y) foo('a', 3) # positional call foo(y = 'a', x = 3) # keyword call ----- 函数定义时:默认值参数default argument。规则:有默认值的参数必须放在无默认值的参数之后 # 指数函数 def mypow(x, p = 2): # 有 ret = 1 for i in range(p): ret *= x return ret print(mypow(5, 5)) ----- 规则:函数调用时,关键字调用的参数必须放在位置调用的参数后面(需要理解) Python 3.8: "/"的前面只能固定位置传递参数 "*"的后面只能关键字传递参数 更清晰、更明确 def fun(x,y,/,z,*,m,n): print(x,y,z,m,n) fun(1,2,z=3,m=4,n=5) ----- 函数定义:变长参数 variable-length arguments def max(*left): m = -math.inf for i in left: m = m if m > i else i return m print(value,...,end = '\n',flush = False) # flush = true 实时输出到屏幕上 ----- 函数定义:变长关键词参数 keyword variable arguments def func(x, *left, y, **kw): print(x, y, left, kw) ----- 函数定义时的参数顺序:(正常参数,变长参数,默认值参数,变长关键词函数) def func(x, *left, y='*', **kw): print(x, y, left, kw) func(1,2,3,4,5, l=1, m=2, n=3, y='#') func(1,2,3,4,5, l=1, y='#', m=2, n=3) func(1,2,3,4,5, y='#', l=1, m=2, n=3) def func2(x, y='#', *left, **kw): # 这个默认参数没啥意义 print(x, y, left, kw) # func2(1, 2,3,4,5, y='#', l=1, m=2, n=3) # 提示错误:y多次赋值 func2(1, 2,3,4,5, l=1, m=2, n=3) ----- 函数嵌套(内部函数) def foo(x, y): print('x, y in foo() = ', x, y) def bar(x, y): x, y = y, x print('x,y in bar() = ',(x, y)) bar(x, y) print('x, y in foo() = ', x, y) foo(5, 7) foo.bar(5, 7) ----- 递归函数:对递归函数的理解,要求对函数调用的理解非常深。 | -- | -- | -- | -- | [https://note.youdao.com/ynoteshare1/index.html?id=dd959db426f13f16ed89c7422f26dff8&type=note 2017年笔记] 用递归函数实现1+2+3...+n 用递归函数得到斐波那契数列 # n = 1 2 3 4 5... # f(n) = 1 1 2 3 5 8... # f(n) = f(n-1) + f(n-2) 例:用循环得到斐波那契数列(递归调用很慢,而且没法重用) 例:用递归判断是否是回文 例:用循环判断是否是回文 递归通常可以用循环代替,但是有些问题,用循环会很难做,比如Hanoi Tower问题。 -----
2020.12.28 文件,模块,异常
文件 文件open()的模式(mode):分为text和binary两种模式。 文件打开之后会有一个文件指针,用于读写文件。 打开方式: t 文本模式 (默认) b 二进制模式 + 打开一个文件进行更新(可读可写),不能单独使用,必须结合rwa r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等。 r+ 打开一个文件用于读写。文件指针将会放在文件的开头。 rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。 a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入。 w和w+的区别,是否可以读(都会清空) r和r+的区别:是否可以写 r+(文件必须存在,不清空) vs. w+(文件不存在则新建,存在则清空) 默认为文本模式,如果要以二进制模式打开,加上b。 使用样例:f = open(file_path, 'r', encoding='utf-8') 其中,file_path是一个相对于你当前工作目录下的一个路径,找到你需要打开的文件。 'r' 就是打开的方法,上述对每种打开的方式已经进行讲解。 encoding参数是用什么编码方式打开这个文件,针对以文本打开的文件。 注:用os.getcwd()可以查询当前工作目录,进程默认是python解释器shell的目录。 '.' 表示着当前目录;'..' 表示着上一级工作目录。 打开的文件指针在文件的最开头,追加模式a在末尾。 文本文件:以字符串表示来存储。 二进制文件:图片(RGB像素点) 音频 视频(每帧是一张图片) 其他数据(一堆整数、浮点数等) 补充作业:将一个浮点数列表,存储在一个文件中,然后读出来,重构出之前的列表。(文本文件和二进制文件都可以,都可以做一下) 文本文件优势:字符串存取,可视化很好,但是存取慢,效率低,精度不够。 二进制文件优势:存取麻烦点,需要把结构设置好,存取快,效率高,精度高,可批量。 文件方法: fp.write(),把一个字符串写入fp所指的文件位置。 fp.flush(),把缓存在内存的文件,载入到硬盘上。 注:如果是个列表,写到文本文件需要是字符串,从字符串再转回原列表可以用eval()函数,看runoob,这个函数非常有用! fp.writeline() fp.readline(),把'\r'弄没了,历史原因,不同os处理文本换行的方式不同,要注意'\r'的处理,如果你可以说了算,最好别用'\r'。 fp.readlines(),按照行读回来列表,会读回来\n换行符。 fp.read(),二进制可以按照字节数读取,不输入指定字节数,直接全部读取。 fp.seek(),可以移动文件指针的位置。 struct模块:将int/float等数据转化为字节流(bytes)、或反之,比较底层。 可见:https://blog.csdn.net/qq_30638831/article/details/80421019 用pack和unpack来打包和解包,自己要指定读取的字节数,按照什么方式读,怎么解包。 二进制只能写bytes,不能写字符串。 pickle模块:更高级,不用自己想格式,直接自动序列化和反序列化。不像struct用的那么麻烦。 可以用二进制读文本文件,但是读取回来会读取为bytes类型的数据。可以用bytes类型的方法,.decode()来解码输出。 str有对应的encode()方法,可以编码成bytes,bytes有decode()方法,可以解码成str。 ---------------------- 程序和进程 当前目录 进程 程序启动后产生进程。进程是动态的。 进程初始的当前工作目录是发起此进程的命令所在的目录。 import os os.getcwd() # 返回当前工作目录 os.chdir('..') # 改变当前工作目录到指定的路径 --------------------- 模块 之前讲过,用import来载入模块 import之后,发生了什么事? 执行该模块中的代码(仅在第一次调用时才执行一次,防止多次载入) import的模块会有自己的名字__name__,如果是主程序就是'__main__',如果不是主程序就是调用模块的名字,含路径。 sys.path存储了所有用以搜索模块的路径。 from 模块名 import 对象名 from math import * # 谨慎使用,会覆盖你的命名空间 用as可以给你import的东西取别名。 ----- 内嵌模块,预加载 dir(__builtins__) 也可以使用sys.modules.items()显示所有预加载模块的相关信息。 ------------- 异常(Exception) python通过异常机制来管理错误。Exception 编译错误(语法错误,compiling syntax): 运行时错误(run-time) 报错类型: SyntaxError:语法错误 AssertionError:例如assert(0>1) NameError:变量名错误,变量名未定义 ZeroDivisionError:0作为除数报错 IndexError:index越界 KeyError:key不存在 FileNotFoundError:打开不存在的文件 ModuleNotFoundError:找不到import的模块 ValueError: 异常处理:三步走,Try-Catch(expect)-Throw(raise) 语法:try-except-else-finally 形式: try: normal-code-block except: error-handling-code-block 例如: try: x = 1/0 f = open('aaa') except: print('something wrong') 可以用except来选定不同的错误类型有不一样的处理方式。 ----- 主动抛出异常: raise(Exception('abc')) 可以通过类继承,自定义异常类型 ----- with open('abc','w') as f: #不管发不发生异常,最后都会把f关掉 print('hi') ----- 断言assertion(防御型编程,非常好的习惯) assert expression 等价于: if not expression: raise AssertionError assert expression [, arguments] 等价于 if not expression: raise AssertionError(arguments)
2020.12.25 函数
位置型传参和关键字传参的一些规则: 关键字调用的参数要放在位置型调用的参数的后面。 一个参数一旦关键字调用,后面的参数也都要关键字调用。 例如有这样一个函数def func(x, y, z),用func(2, 3, y=4)来调用就会报错。 python增加一些严格的要求,是因为实际没有这样的需求,而且能增加代码的严格性。 变长参数(variable-length):调用时,可以传0-无穷个参数,函数内会以元组方式来表示。(python解释器会把这些参数打包为一个元组对象,作为一个参数传入) 定义方式:def func(*arg): 例如:func(1, 2, 3),arg的值会为元组(1, 2, 3) 变长关键词参数:调用时,可以传0-无穷个键值对形式的关键字参数(key=value),python解释器会把他打包成一个元组对象,作为一个字典对象,作为一个参数传给函数使用,其中key是以字符串类型存的。 定义方式:def func(**karg): 例如:func(x=1, y=2, z=3),最后karg的值为{'x':1, 'y':2, 'z':3} 函数定义时的参数顺序:(正常参数x,变长参数*arg,有默认值参数y=3,变长关键词函数**karg) 注:要注意,变长参数不能存在两个,因为会搞不清楚他们的长度,老师在板书的时候连续写了两个*u *v,不是要连续定义。 变长参数和有默认值参数可以调换位置,但调换后y就只能用位置传参调用了。 调用时规则:变长参数后的正常参数或者有默认值参数必须关键字调用(keyword-only) def func(x, *left, y='*', **kw): print(x, y, left, kw) func(1,2,3,4,5, l=1, m=2, n=3, y='#') func(1,2,3,4,5, l=1, y='#', m=2, n=3) func(1,2,3,4,5, y='#', l=1, m=2, n=3) def func2(x, y='#', *left, **kw): # 这个默认参数没啥意义 print(x, y, left, kw) # func2(1, 2,3,4,5, y='#', l=1, m=2, n=3) # 提示错误:y多次赋值 func2(1, 2,3,4,5, l=1, m=2, n=3) 函数嵌套定义(子函数): 例如: (((-----))) def foo(x, y): print('x, y in foo() = ', x, y) def bar(x, y): x, y = y, x print('x,y in bar() = ',(x, y)) bar(x, y) print('x, y in foo() = ', x, y) foo(5, 7) foo.bar(5, 7) 遗留问题,子函数可否访问父函数中的名字。 函数递归:很适合解决汉诺塔等问题,但是能用递归解决的问题,通常也能用循环,或者利用栈等数据结构来实现。只不过像汉诺塔问题就太难了。 以经典数列,斐波那契数列举例(兔子繁殖问题)。 首先,斐波那契数列的公式是f(n) = f(n-1) + f(n-2) 代码: def fi(n): if n == 1 or n == 2: return 1 return fi(n-2)+fi(n-1) 但是这个方法优化很差,因为每层的递归调用呈指数级增长,但是写起来很简单,可以用动态规划算法来优化。 递归的优势:自然易懂,但是增加了重复调用函数的开销,而且需要正确使用。 -------------------------------- (((----))) 文件:用sys模块,即import sys sys.stdinput; sys.stdoutput; sys.stderror,永远为进程打开,可以直接调用, flush → 内存中的内容,写到磁盘(屏幕) 内存 → 缓冲 buffer os模块,即import os os.getcwd(),返回当前工作目录(进程) open()函数返还一个文件对象,文件对象是可迭代对象 文件自己学习一下, 文本文件,二进制文件? 怎么在文件里又读又写? seek; tell 方法自己试试。
2020.12.21 函数
----- 一个面向过程的程序 = 数据(数据结构) + 函数(算法) 一个面向对象的程序 = 一堆类, 类就是数据结构和算法。 ----- 函数定义的语法: 三部分:函数名、参数列表、返回值。 形式: def function_name( [argument]... ): # 函数可以有0个或者任意多个参数(parameter、argument),[]表示可选 body return obj return语句会导致函数立刻退出,同时返回一个obj。 如果函数执行结束时,没有return语句,则默认会return None。 为什么要用函数? 1) 重用 2) 模块化 函数使用时的注意事项: return exit() break continue 函数的本质:在python中,函数仍然是个对象,类型(type)为”function”。我们用def就是来创建了一个function对象。 而且这个和普通的名字和对象的绑定不太一样,用def来定义的函数对象是一个”初始绑定”,会赋予一个名字给这个对象。 例如:def func(),会创造一个名字为func的function对象。 再用 x = func,会把x这个名字绑定在func指向的函数对象上,可以正常使用,但是即使x指到了那个function对象上,那个function对象仍然叫”func”。 匿名(anonymous)函数:用关键字lambda来定义函数。 例如:sorted(l1, key=lambda x: x[1]),sorted函数前面已经讲过,这里传入的函数是一个lambda函数,用key传入到这个函数得到的返回值来排序。 注:可以理解为一个轻量级临时函数。 函数必须先定义,再调用。 函数的执行过程(画了两个图,很形象) 函数嵌套调用:函数f()中调用函数g(),g()中调用了h() 函数递归调用:函数f()中调用了自己f() 函数定义和函数调用? 函数调用时,才会执行函数内部的代码 ... 名字的生命域: 局部环境:函数执行时创建的临时环境。函数内部定义的名字(包括参数名字),均为局部名字(除非global)。函数执行结束时,局部环境消失,局部名字也随之消失。 下面代码中的注释是泽洋写的。 def bar(m): # 在foo中,传入了7,并且将名字m绑定在这个对象上 print('x in bar() = %d' % x) # 因为bar中没有定义局部的x,所以这个x找全局的x,所以输出了”x in bar() = 9”(第一次输出) def foo(n): # 传进来的是9,并且用n来绑定了9 x = 7 # 定义了一个局部的x,值为7 bar(x) # 把7传入了bar函数中 print('x = %d' % x) # 有局部的x=7,所以输出局部的x,此时输出”x = 7” (第二次输出) x = 9 # 全局的9 foo(x) # 把9传入foo函数中,并进入foo函数体 print('x in global = %d' % x) # 输出”x in global = 9”(第三次输出) 输出:我在程序中写了注释。并且给print的先后标了序号。简单来说,有局部找局部,没有局部找全局。 ----- 函数内引用名字时:如果该名字不是局部名字(局部环境中不存在),则作为全局名字使用(在全局环境中找)。 注意:当函数嵌套调用时,为什么不在调用该函数的函数的局部环境去找呢?思考一下。视频中有讲。 ----- global语句:在函数内(局部环境中)定义、引用、或修改全局变量。函数结束后,该名字仍然存在。 def func(): global x #如果全局名字x不存在,则定义一个新的全局名字x print(x) x = 9 ----- 全局名字和局部名字必须从一而终(一致)。如果要在函数中修改一个全局名字,必须在第一次访问前用global语句声明。 def func(): x = 7 # 使用赋值语句时,x一定是局部名字 global x x = 9 上面的写法是不允许的。函数中一个名字,要么是局部名字,要么是全局名字。 x = 5 def func(): print(x) # 全局名字 x = 9 # 局部名字 上面的写法也不允许 x = 5 def func(): print(x) global x x = 9 上面的写法也不允许 ----- 全局名字 vs. 局部名字(全局环境 vs. 局部环境)总结: 全局环境指主程序所在的环境。局部环境指函数被调用时所在的环境。 全局环境中的变量名称为全局名字,名字前可能需要加上模块名来访问。 局部环境中的变量名称为局部名字。局部名字的生命域很短,只能在函数内部使用。 函数内部“引用”一个名字时,如局部环境找不到该名字,会尝试在全局环境找(隐式)。 函数内部的赋值语句会创建局部变量(如果不存在该局部名字,无论是否有同名的全局名字)。 函数中修改一个名字的绑定或者修改绑定的对象时,会认定这个变量为局部名字,而不会在全局环境中找出该名字进而做修改。为什么要这样呢?当然不允许了! 不允许在函数中隐式地修改全局变量。如果需要修改全局变量,需要用global关键字显式说明。 参数名字绑定时,会为每一个参数定义相应的局部名字。 ----- 函数调用的两种方式: def func(x, y): pass 1) 位置传参(positional call):简单来说就是按照顺序来传参。 例如:func(3, 4) 2) 关键字传参(keyword call):简单来说就是指定参数名来传参。 例如:func(x=3, y=4)
2020.12.18 正则表达式(part 2)
- 大家可以通过看大量例子,去理解和练习
实验报告不好理解的题目就提出来。 文件 模块 异常 自己先了解。 ----- 定位符(anchoring, 锚):在匹配时可以明确位置,本身不匹配字符。 在我们网页中的蓝色链接,其实就是一种锚文本(anchor text),即超链接(hyper-link)。 ^等价于\A,用来匹配行首(字符串开始), 例如:search(r'^[a-z]', 'Hello')就会匹配不上,和match()方法相同了。 $等价于\Z,用来匹配行尾。 例如:search(r'[a-z]+$', 'Hello, You')会匹配到'ou'。 search(r'[a-z]+$', 'Hello, YOU')就匹配不到了。 \b词边界(word), 首先明确词的定义:由[_A-Za-z0-9]构成的字符串。与标识符(名字)的构成“基本”相同,不考虑中文字符。 每个词有两个边界,形式大概是这样:'\b\w+\b' \B不是词边界,即\b求补集。 例如:search(r'\B.', '##'),匹配到第一个#。 search(r'.\B', '##'),也匹配到第一个#。 search(r'..\B', '##'),能匹配到两个#。 总结:每个非单词字符两边都有\B。 一个连续单词,除了两端,两两之间都有一个\B ----------------- 拼接(concatenation): 形式:E1E2,放一起。 例如:r'ab'; r'cd'; r'abcd' r'[a-d]+'; r'[0-5]+'; r'[a-d]+[0-5]+' r'ab+',同等于r'a(b+)' 注:优先级:圆括号(parenthesis) > 重复修饰符 > 拼接 > alternation(选择)。 英文:花括号 brace;圆括号parenthesis;方括号 square bracket;括号bracket;尖括号 angle bracket。 r'ab|c',同等于r'(ab)|c' 如何找到任意的连续重复字符: 例如:re.search(r'(.)\1+', 'ieeeeee'),就会直接匹配到'eeeeee'。 思考:匹配'a'出现n次,然后后面跟同样次数的n次其它字符。 如果n是一个具体的数字,如10:r'a{10}b{10}' 如果n不确定,做不到了。正则无法记忆长度。 什么时候正则表达式,不能在''前加r? 我要匹配'\b'(backspace)这种转义字符,去除了转义还怎么匹配转义呢。而且这种转义字符你还不能打出来,不像\t你可以手打一个。 例:search(r'\b', '\b\ba'),会匹配到a后面。 search('\\b', '\b\ba'),和上一个一样。 search('\b', '\b\ba'),会匹配到第一个\b。 总结:前面的pattern是正则字符串,但是会先把它当做一个普通的字符串,处理转义字符或者字符串格式化等操作,然后再把处理过的字符串,根据RE的语法,把它给变成一个pattern。 可以想象一下,利用了前几节课的知识,先按照处理字符串的规则,再把处理过的字符串,按照正则的规则再处理。 例:re.search(r'\\M', '\M'),会返匹配到'\\M' re.search(r'\M', '\M'),会报错。 也就是说,在转义字符中,如果\后面的转义没有意义,就会默认把他当成普通的'\',但是在正则中'\'后面没有意义就报错了。 很简单,毕竟写python的,和写python的re模块的可能也不是一批人,写的思路不一样也很正常。 RE如何匹配元字符:通过转义字符 r'\\' → '\' r'\.' → '.' ^$(){}[]?+*,都一样 '-'不是元字符,例如:[-ab^],包含'-'和'^'。 ((((那为啥-不是^是)))) --------------------------------- 其它方法: re.sub(pattern, repl, string, count=0, flags=0) repl 参数是一个函数 re.findall(pattern, string, flags=0) 或 pattern.findall(string[, pos[, endpos]]),如果有分组,会在列表里嵌套元组。 re.finditer(pattern, string, flags=0) (返回迭代器) re.split(pattern, string[, maxsplit=0, flags=0]) 在re.split的pattern中使用分组,会将分组所匹配的字符串也保留: >>> re.split("&", "a_&_b_&_c_&_d") ['a_', '_b_', '_c_', '_d'] >>> re.split("(&)", "a_&_b_&_c_&_d") ['a_', '&', '_b_', '&', '_c_', '&', '_d'] >>> re.split("_((&)(_))", "a_&_b_&_c_&_d") ['a', '&_', '&', '_', 'b', '&_', '&', '_', 'c', '&_', '&', '_', 'd'] >>> re.split("(&)?", "a_&_b_&_c_&_d") ['', None, 'a', None, '_', '&', '', None, '_', None, 'b', None, '_', '&', '', None, '_', None, 'c', None, '_', '&', '', None, '_', None, 'd', None, ''] compile 编译:返回一个RegexObject,可以直接编译好一个字符串为pattern,然后就可以直接调用编译好的对象的方法。会很高效,不用反复编译了。 ----- re.split 和 string.split学习要注意的一个地方: split后,空字符串是否会保留? re.split中,如果正则表达式中有分组,那么分组也会保留到结果中。 ----- 关于贪心: data = 'Thu Feb 15 17:46:04 2007::uzifzf@dpyivihw.gov::1171590364-6-8' re.search('\d+-\d+-\d+', data) re.search('.+\d+-\d+-\d+',data) re.search('.+(\d+-\d+-\d+)', data) re.search('.+?(\d+-\d+-\d+)', data) 正则表达式的不贪心是指:向后不贪心,一旦匹配上,不会再尝试从前面减少从而匹配更少的字符。 (张天夫同学帮助总结) Linux中还要区分:扩展正则表达式 vs. 基本正则表达式语法 (python不涉及)
字符串、进制、raw字符串?正则表达式(备课的笔记,先不删除)
正则表达式
Linux课讲义:文件:Linux-RE.pdf
2017年讲义也有很多例子。
regex模块,对于re.search(r'\M', '\M')不报错。但是这些细节都不重要。最重要是要理解RE解析的原理。 re.finditer(r'\b', string)和re.finditer(r'\B', string)可以帮助理解。 我总结的规则,感觉可以简单这么写: \w\B\w \W\B\W ^\B\W$ \W\b\w\b\W ^\b\w\b$ ----- 定位符anchoring ^表示行首,等价于\A $表示行尾,等价于\Z \b表示单词边界 \B表示非单词边界 ----- 预定义字符集:\s \S \w \W \d \D ----- concatenation EF ab ----- EF+ ab+ 总结一下优先级 concatenation alternation [] group repetition Repetition takes precedence over concatenation, which in turn takes precedence over alternation. A whole expression may be enclosed in parentheses to override these precedence rules and form a subexpression. ----- 如何匹配任意一个小写字母,重复出现多次? 回顾Back References and Subexpressions The back-reference \n, where n is a single digit, matches the substring previously matched by the nth parenthesized subexpression of the regular expression. 只能是1位吗? ----- 什么时候不用raw? 如果我就是要匹配 '\b' 如果我就是要匹配'\t'怎么做? \t \\t 都可以匹配 '\t' python理解正则表达式的三个步骤: 1)字符语法 2)正则语法 3)字符语法 正则表达式字符串的解析步骤: 1)先解析为一个普通字符串,按照字符串语法(主要是转义字符/问题),所以推荐用raw 2)进而解析为正则表达式,按照正则语法 如何匹配'\\'这个字符? \^ \$ \. \* \+ \( \) ...:将正则表达式的元字符,转变为本来的意思(字面意思,逐字的,verbatim, literal) 例如: r'\\'或'\\\\'可以匹配'\\' (r'\'这个写法会报错) r'\^'或'\\^'都可以匹配'^' r'\\^'这个RE的意思:先匹配'\\'这个字符,然后匹配字符串的开始,所以是矛盾的,什么都不会匹配上 r'\$'可以匹配'$' r'\\$'这个RE的意思:先匹配'\\'这个字符,且该字符为字符串结尾 ----- 其他方法 re.sub(pattern, repl, string, count=0, flags=0) repl 参数是一个函数 re.findall(pattern, string, flags=0) 或 pattern.findall(string[, pos[, endpos]]) re.finditer(pattern, string, flags=0) (返回迭代器) re.split(pattern, string[, maxsplit=0, flags=0]) ----- re.RegexObject对象: pattern = re.compile(pattern[, flags]):返回 RegexObject 对象。 compile ----- 关于贪心: data = 'Thu Feb 15 17:46:04 2007::uzifzf@dpyivihw.gov::1171590364-6-8' re.search('\d+-\d+-\d+', data) re.search('.+\d+-\d+-\d+',data) re.search('.+(\d+-\d+-\d+)', data) re.search('.+?(\d+-\d+-\d+)', data) 正则表达式的不贪心是指:向后不贪心,一旦匹配上,不会再尝试从前面减少从而匹配更少的字符。 (张天夫同学帮助总结) Linux中还要区分:扩展正则表达式 vs. 基本正则表达式语法 (python不涉及) ----- re.MatchObject group() 返回被 RE 匹配的字符串。 start() 返回匹配开始的位置 end() 返回匹配结束的位置 span() 返回一个元组包含匹配 (开始,结束) 的位置 group groups(后面专门讲分组的概念,然后再讲groups) match和search早点讲:re.match 只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回 None,而 re.search 匹配整个字符串,直到找到一个匹配。 后面举例的时候,就可以两个切换着用 re.M多行模式啥意思? re.I|re.M这个flag是啥意思?肯定是按位操作。这个好玩:) re.I 忽略大小写 re.L 表示特殊字符集 \w, \W, \b, \B, \s, \S 依赖于当前环境 re.M 多行模式 re.S 即为' . '并且包括换行符在内的任意字符(' . '不包括换行符) re.U 表示特殊字符集 \w, \W, \b, \B, \d, \D, \s, \S 依赖于 Unicode 字符属性数据库 re.X 为了增加可读性,忽略空格和' # '后面的注释 自定义字符集 [ ] 字符集, [^ ]补集 预定义字符集 \w [ [:alphanum:]] \d [ [:digit:]] \s . 任意一个字符,不能匹配\n, re.S可以取消 重复修饰符(默认为贪心greedy匹配最长的字符串) * 表示前面的字符匹配0次到n次 ? 表示可取0个/1个 + 重复一次到无穷多次 {3} 重复3次 {3,5} 3次到5次 {3,} 3次到无穷 *? ?? +? {3,}? 不贪心匹配
字符串 进制 海象运算符 位运算
----- 字符和数字的转化(不同进制)【不同进制的格式化输出】 print(0xff, 0o377, 0b11111111, 255) 默认的字符串表示,其实内存中都是一样的哈 进制转化函数:返回字符串 hex(255) # 十六进制,hexadecimal bin(255) # 二进制: binary oct(255) # octal ----- 整数格式化时的进制转化: x和X:十六进制 o:八进制 b:二进制(%不支持) '%#x' % 255 '{0:b} {0:d} {0:o} {0:x} {0:#X}'.format(255) # '11111111 255 377 ff 0XFF' ----- 汉字的表示(UTF-8/16/32编码的细节不讲),python对字符串默认是采用UTF-8编码。python文件必须是utf-8编码,才可以正常运行?UTF-16编码到底是什么样子的? 如何避免编码问题?不要用中文字符 http://www.asciitable.com/ ascii码表 每个字符都有多种表示:八进制、十六进制 print('\61', '\061', '\x31') # '\X31' '\o31'都不对 ord('a') # 字符对应的ASCII码(American standard code for information interchange) chr(97) # 返回与ASCII码对应的字符 ----- raw字符串 ----- 海象运算符,可在表达式内部做变量赋值(walrus operator) The purpose of the walrus operator is to consolidate an assignment statement and a boolean expression when both assignment and expression would utilize a similar statement. := 在这个示例中,赋值表达式可以避免调用 len() 两次: if (n := len(a)) > 10: print(f"List is too long ({n} elements, expected <= 10)") if结束后,n还活着 函数的地方会讲:变量名字的生命域 C++支持吗? if (int i = len(a) < 10) { } C++支持: for(int i=0; i < 10; i++) { } python: for i in range(10): ... for循环结束后,i还活着 ----- 位运算 & | ^异或 ~ << >> a = 60 # 60 = 0011 1100 b = 13 # 13 = 0000 1101 c = a & b 负数的补码表示(按位取反+1,注意python的int没有上界,补码理解起来就有点微妙)
2020.12.14 正则表达式
- 板书:文件:2020-12-14-1.jpg , 文件:2020-12-14-2.jpg , 文件:2020-12-14-3.jpg , 文件:2020-12-14-4.jpg , 文件:2020-12-14-5.jpg , 文件:2020-12-14-6.jpg
- 视频:第18次理论课
正则表达式(regular expression, RE)是满足正则语言(regular language)语法的一个字符串,作为一个抽象的模式,用以匹配一个、多个甚至无穷多个实际的字符串。 作为一个语言,也有它的语法(grammar)。 在python中,使用模块re,即import re,可以调用。 'abc'只是一个字面上(literal)的字符串。正则字符串是一个模式(pattern)。 re.match('acb', 'abc is me')就会返还一个Match对象,里面有span()方法,可以返回匹配的区间。如果没匹配上返回None。 re.search()和re.match()一样,匹配字符串,返回Match对象,但是match必须要在字符串开头匹配上,search是在全字符串找,匹配首次出现的。 在search和match方法都有flags参数,可传入参数如下: re.I,使匹配大小写不敏感。 re.M,多行匹配。 re.S,使 . 匹配包括换行在内的所有字符。 也可以用re.I|re.M|re.S共同使用,利用了位运算,非常高效的表示方法。 自定义字符集: [abc],匹配一个字符,必须是{a, b, c}这个集合中的某一个。 例如:re.match('[abc]', 'abc'),就会找到a。 re.search(r'[abc\\]', 'I \\ am'),会找到反斜杠\。 [a-z], [a-c], [0-9],分别匹配的是小写字母a-z,小写字母a-c,数字0-9。 [^a-z],除了a-z的都能匹配上,^是用来取补集的。 预定义字符集: \d匹配数字,例如: re.match(r'\d', '9') \w匹配全部字母数字下划线,例如: re.match(r'\w', 'WORD') \W匹配除全部字母数字下划线以外的,例如: re.match(r'\W', '?') \s匹配空白符,[ \t\r\f\n\v]。 \S匹配非空白符。 . 通配符,匹配任意一个字符,除了'\n'(注意.可以匹配\r)用re.S能取消这个限制。 '\.'只匹配一个'.'(英文句号、period) 重复修饰符: 'E*'可以表示前面的子表达式E重复0-无穷次,默认(贪心)匹配最多的字符,注意贪心(greedy)。 例:re.match(r'\w*', 'abc_@#f3232s3'),会匹配到'abc_'。 'E?'匹配0或1次子表达式E,也是贪心匹配。 例:re.search(r'\w?', '!!!'),能匹配上0个'\w'。 'E+'匹配1到无穷次子表达式E,贪心。 'E{n}'匹配了n次。 {n,}匹配大于等于n次。 {n, m}匹配n-m次,包含m次。 {, m}匹配小于等于m次。 注:多多练习。 上述基本都是贪心匹配,如果想非贪心,加个问号即可。 例如:re.search(r'\w{, 3}?'),匹配到了0次,返回的Match的span值为(0, 0)。 分组(group),把一个re分为几块: 用途:抽取;重用 例:m=re.search(r'(\w*)\.(\w*)', 'suda.edu.cn'),匹配到了'suda.edu'。 还可以快速找到感兴趣的分组,用m.group()或m.groups()方法来找到分组。 本例子,第0组是全部,第一组是'suda',第二组是'edu'。 分组的嵌套: m = re.search(r'((\w*)(\.)(\w*))', 'suda.edu.cn'), m.groups(),返回('suda.edu', 'suda', '.', 'edu'),按照左括号先后顺序匹配。 如何匹配'(',')'括号本身? 在RE中,\(就可以把特殊字符变成自己的本来含义(元字符, meta)。 如何重用? ((((替换时?)))) re.search(r'(\w+)(\.)\1', 'suda.edu.cn.nba'),这个能匹配到'n.n','\1'代表着1号分组。 注:可能会和ascii码字符表示混 | 或 选择(alternation) re.search(r'[0-9]+|[a-z]+', 'suda.edu.cn.nba'),匹配到了'suda'。 注:优先级,一元操作符要普遍大于二元操作符。 re.search(r'(abc){1, 3}', 'abcabcabcabcabc'),匹配到'abcabcabc',其中,分组匹配到的是最后一个'abc'。 re.search(r'([a-z]{3}){1,3}', 'abcdefghij'),匹配到'abcdefghi',其中,分组匹配到的是最后一个'ghi'。 定位符anchoring(下节课仔细讲) ^表示行首 $表示行尾 \b表示单词边界 \B表示非单词边界
2020.12.11 进制,字符的8/16进制表示,raw字符串,海象运算符,位运算,正则表达式基本概念
----- 进制 进制:指一个整数按照多少进一个位(进位的制度,进位的规则)。进制问题仅限于整数,和浮点数无关。 十进制:0 1 2 3 4 5 6 7 8 9 10,按照10次进一个位。 二进制:0 1 10 11 100 101 110 111,按照2次进一次位。 八进制:0 1 2 3 4 5 6 7 10 11 12,按照8次进一个位。 十六进制:0 1 2 3 .. 8 9 A B C D E F 10,按照16次进一个位,阿拉伯数字不够用字母来补。 相应英文:十进制 decimal;二进制 binary;八进制 octal;十六进制 hexadecimal (hex) 注:我们之前字符串的格式化%d就是来格式化成十进制,%o是八进制,%x是十六进制。 无论用什么进制来写一个数字(整数),内存中的表示都是一样的。 print(0xff, 0o377, 0b11111111, 255) 为什么输出时,都输出十进制的数字? 因为整数的默认字符串表示__str__(),就是十进制的 手动转化进制: 所有的进制转换为十进制可采用如下方法: 例如:(10011)2 = 1*2^4 + 1*2^1 + 1*2^0 = 16+2+1 = 19 (362)8 = 3*8^2 + 6*8^1 + 2*8^0 = 192+48+2 = 242 (AF)16 = 10*16^1 + 15*16^0 = 160 + 15 = 175 16进制,8进制和2进制相互转化时,因为16和8分别是2的4次幂、3次幂。自己多练练。 进制转化方法1:内嵌函数 hex(255) # 十六进制,hexadecimal bin(255) # 二进制: binary oct(255) # octal 进制转化方法2:字符串格式化 x和X:十六进制 o:八进制 b:二进制(%不支持) '{0:b} {0:d} {0:o} {0:x} {0:#X}'.format(255) # '11111111 255 377 ff 0XFF' '%#x' % 255 ----- 用8/16进制ascii码来表示字符 ascii码表 http://www.asciitable.com/ ASCII码的范围为0-255,0-127是英文字符,其他为扩展字符,如希腊语字符 一个字符,除了用自己表示(如'A', '\t'),还可以用8进制或16进制(注意二进制和十进制不支持)的ascii码来表示。 print('\101', '\x31'),第一个是八进制,第二个是十六进制,分别输出'A'和'1'(相应值的ASCII码对应的字符) 八进制最多传入3个位,不够可以补0 '\7' == '\07' == '\a' == '\007' 十六进制必须传入两位 '\7' == '\x07' # '\x7'这个写法是错的,必须是 '\xXX' X表示0-F的一个数 ord('a') # 字符对应的ASCII码(American standard code for information interchange) chr(97) # 返回与ASCII码对应的字符 需要理解的一个重要规则: 当'\'和后面的字符不对应任何特殊字符时,'\'就会作为普通字符 '\9' == '\\9' 首先这个肯定不是9进制,也不是16进制。 '\y' == '\\y' 汉字如何表示?GB(国标)和Unicode两种方式。Unicode是一种国际编码方法,其具体技术实现有UTF-7/8/16/32等多种。细节我们没时间讲。等我整理好,会放到讲义中。 大家需要了解的几方面: 1)python对字符串默认是采用UTF-8编码。 2)python文件必须是utf-8编码,才可以正常运行? 3)如果遇到编码问题,不知道如何解决,最好的办法是:不要用中文字符,用PyCharm或IDLE等python集成编程环境,不要用记事本等其他文本编辑器(有的软件会在文件前面加一些标记字节,如FFEF啥的) ----- raw字符串 raw (计算机术语:未处理的,即网络常用用语:生的)字符串:主要用于正则表达式 (Regular Expression),通常RE不用处理特殊字符。 简单来说就是去除转义。去除字符串中的转义。 例:r'\b'就是'\\b' ----- 海象运算符(walrus operator,python3.8开始支持):The purpose of the walrus operator is to consolidate an assignment statement and a boolean expression when both assignment and expression would utilize a similar statement. 海象运算符':=',可在表达式内部定义变量,并赋值。 对比一下,和第2种方法比,海象运算符可以避免两次调用len(),和第3种方法比,更加简洁。 if (n := len(a)) > 10: print(f"List is too long ({n} elements, expected <= 10)") if len(a) > 10: print("too long (%d elements, expected <= 10)" \ % len(a)) n = len(a) if n > 10: print("too long (%d elements, expected <= 10)" \ % n) 注意,第1种方法中,用海象运算符定义的变量名n,在if复合语句执行完,还会继续活下去。即和第3种的情况完全相同。 函数的地方会讲:变量名字的生命域(从生到死) C++支持下面的语法吗? if (int i = len(a) < 10) { } C++支持下面的语法: for(int i=0; i < 10; i++) { } python的语法(可以在for中定义i): for i in range(10): ... 注意:for循环结束后,i还活着 ----- 位运算:以二进制的位为单位来操作。 & | ^ ~ >> << 例:A = 60,B = 13,以二进制表示,A = 0011 1100,B = 0000 1101 & 且操作符:A & B = 12,即0000 1100,A和B相应位必须都为1才是1。 | 或操作符: A | B = 61,即0011 1101,A和B相应位有一个为1就是1。 ^ 异或操作符:A ^ B = 49,即11 0001,相同取0,相反取1。 ~ 非操作符:~A = -61(涉及到负数的补码表示)。 << 左移:A>>1 = 30,所有位向右移一位,和整除2效果一样。 >> 右移:A<<1 = 120,所有位向左移一位,和乘2效果一样。 补码表示:计算机中,负整数是用补码表示的,这样就可以把减法运算转化为补码+加法运算。 补码运算:-60的二进制表示为:对60按位取反,然后加1。这样,-60 + 60就是0。 假设用1个字节来表示整数,自己试一下。 python中整数的表示范围是无限的,bin(999**999),会让补码理解起来比较麻烦。以后学C语言,会更明确一些。 ----- 正则表达式 (Regular Expression):一个RE是一个字符串,满足正则语言的语法,用以匹配一个或多个甚至无穷个具体的字符串。 用途:匹配,抽取,替换。 支持RE:C++;Java;Python;Linux;egrep;sed;vi编辑器。 在python中使用RE: re模块,import re win10使用linux:Ubuntu https://blog.csdn.net/daybreak222/article/details/87968078
2020.12.7 字典,集合
第二次考试:12月9日 周三 晚上,元组/字符串/字典/集合。 实验报告和代码:抄袭,肯定零分,几个人互相抄就几个人零分,有问题找老师商量。认真写实验报告,锻炼并提高自己的表达能力。 实验报告中同学们提了很多建议:非常感谢!
字典dict
定义: 1) {} # 空字典 {1:'a', 2:'b'} # 包含两个元素(键值对)的字典 2) 工厂函数dict (zip生成函数可以每次返回一个对) 例:l1 = [1, 2, 3] l2 = [4, 5, 6] d1 = dict(zip(l1, l2)) 结果:d1的值为{1:4, 2:5, 3:6} d2 = dict([[1,'a'],[2,'b'],[3,'c']]) 字典的特点: 可变:元素可以增加、可以删除、可以修改value key值唯一,不同key对应的value值可以是相同的(甚至可以绑定到同一个对象上) (可以用字典来实现存储一个班的成绩,key是学号,value是成绩) ; 无序(字典中的元素没有固定的顺序) 可迭代但不是序列 (不可用下标索引随机访问)。 key必须是完全不可变对象(即可哈希对象),value可以是元组、列表、集合等任意一个数据类型。 字典不是用平衡二叉树存的,现在是用哈希表(hash table)来实现的 https://www.laurentluce.com/posts/python-dictionary-implementation/ 遍历: in;not in:判断一个key是否存在于字典中(==) 1 in d1 -> True for i in d1: print(i, d1[i]) for i in d1.keys(): print(i, d1[i]) for i in d1.items(): print(i[0], i[1]) for (i,j) in d1.items(): print(i,j) []操作符:在字典中,[]内部应该放key值,通过key来访问字典的value。 例:d = {'li':4, 'zheng':5, 'hua':6} print(d['zheng']) 输出:5 如果key不存在,会抛出KeyError异常 del d1[1]:删除key为1的元素 字典在内存中的存储方式?自己画一下。非常重要! 思考:什么样的操作会修改字典? 如果字典中,某一个key对应的value是列表,你修改了那个列表,其实并没有修改字典。 不是序列的可迭代对象: range reversed dict set file dict_keys # 思考,如何判断dict_keys对象不属于序列? dict_items 内置函数:以前讲过的函数都是面向全部可迭代对象的,字典当然也是。 例如:len; str; type 字典的方法(增删改查) 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values' d1.fromkeys(),传入一个可迭代对象进去,根据可迭代对象的每个值作为key,来返回一个新的字典。 例如:dict.fromkeys('abcde'),返回{'a':None, 'b':None, 'c':None, 'd':None, 'e':None }。 可以看出.fromkeys()是一个静态(static)方法,即属于类的方法(类级别的方法),不会修改具体对象。调用静态方法时,既可以通过对象调用,也可以通过类名调用。 d1.get(),可以返回字典的某个key的value,如果key不存在返回默认值。 d1.items(),返回一个字典的键值对可迭代对象。 字典嵌套,例如: d = {'lizhenghua':{'name': 'lizhenghua', 'number': 001, 'grade': 100}, 'zhenghuali':{'name': 'zhenghuali', 'number': 002, 'grade': 100.0}} 这样就可以很方便的去访问某一个人的信息。例如d['lizhenghua']['grade']就会返回100这个分数了。 字典的拷贝: 拷贝:创建一个新的dict对象,包含原字典的key,value不拷贝(对可变对象有影响)。 深拷贝:key不需要拷贝,因为key本身就是完全不可变对象。 value遇到非完全不可变对象就会进行深拷贝,即创建一个新的和value相同的对象,并且对于value中包含的非完全不可变对象,也会递归地进行类似的深拷贝。 例如,对于([1,2,(3, [4])], 5),深拷贝时会新创建 元组,值为([1,2,(3, [4])], 5) 列表:值为[1,2,(3, [4])] 元组:值为(3, [4]) 列表:值为[4] 这块不容易理解。深拷贝的基本原则就是:如果一个对象不是完全不可变对象,那么深拷贝时就会创建一个与其值相同的新对象,并进一步递归处理其包含的对象 讲课的视频中,也给了几个例子。 什么样的对象可作为key?(在上方已经提到过) 做个实验:可否自己写个类,属于可变对象(例如包含若干int数据变量名字,但是可以改变名字绑定),但是同时又定义了__hash__(),这时候这个类对象是否可以作为key? ----- 其他资料: d1 = {1:'Jan',2:'Feb','Mar':3,'Tuesday':2} []操作符 d1[1] = 'Monday' d1['a']=3 d1['b']=[1,2,3] d1['b'].append(4) del d1['a'] 拷贝 d6={0:[1,2,3],1:[4,5,6]} d7={} d7[0] = d6[0] d7[1] = d6[1] # 浅拷贝 d7 = dict(d6) # 前面三行等价于这一行 ----- 字典嵌套:用字典设计比较复杂的数据结构 方法一,比较死板,可读性比较差 d2={} d2['001']=['name','M',99,97,30] d2['002']=['name2','f',99,99,80] d2['001'][2] 方法二,可读性更好,字典嵌套字典 d3={} d3['001']={'name':'zhenghua','gender':'M','grade':{'python':99,'C':90}} d3['001']['grade']['python'] for i in d3: if d3[k]['name']=='zhenghua': print(d3[k]['grade']['python'])
集合:set, frozenset
集合:只有key没有value的字典。可以用于去重。集合内不允许有重复元素(即元素的值不能相同)。 定义方法: 1){}:{1, 2, 3},定义一个含有三个元素的集合。 s1 = {3,5,3,7,'a','a'} # {'a',3,5,7},去重作用 2)工厂函数: set('abcd') # 参数要求是可迭代对象 s5 = set([23,3,7,3]) # s5={3,7,23} 去重 空集合定义:set() # {}定义空字典 特点:可变、元素值唯一、无序、可迭代容器 什么样的对象可以作为集合元素? 必须是完全不可变对象。 那么有深拷贝的说法吗? 当然没有了,集合里的元素已经是完全不可变对象了。 遍历:in; not in s1 = {3,5,'a','cxb'} # 无序 3 in s1 # True 'cxb' in s1 # True for i in s1: print i # 集合是可迭代对象,不支持索引。 []操作符:集合不支持! del:因此也不能用del来删除某一个元素 集合的方法:'clear', 'copy', 'discard', 'pop', 'remove', 'update' s1 = {'a','cxb',5,3} s1.add('abc') # 集合中增加一个元素(集合中不可增加可变对象)。 s1.add((1,2,3)) # 嵌套一个元组对象 s1.remove(3) # 移除 s1.discard(3) s6.update([1,3,5]) # s6 = {'o','h','l','1','3','5'} # update的参数必须为可迭代对象,这个方法很重要! 集合的运算: 'difference', 'intersection', 'symmetric_difference', 'union', 'isdisjoint', 'issubset', 'issuperset', 'difference_update', 'intersection_update', 'symmetric_difference_update', s6 = set('holiday') s7 = set('hello') s8 = set('ho') s9 = set('hello') # s7和s9不是同一个对象 s10 = set(s6) s8 < s9 # True s8是s9的真子集 s8 <= s9 # 判断s8是否是s9的子集 s6 | s7 # 求s6和s7的并集 s6-s7 # 求s6和s7的差集 s6^s7 # s6和s7异或 s6.difference(s7) #差集 s6.intersection(s7) #交集 s6.union(s7) #并集 s6.symmetric_difference(s7) # 异或 s6.intersection_update(s7) # s6中的元素是s6和s7的交集:s6 = {'o','h','l'} frozenset和set的区别:frozenset是不可变的set,因为set的全部元素必须是完全不可变对象,所以frozenset是完全不可变的。 fs1 = frozenset(s1) fs2 = frozenset([1,2,3,2,1])
2020.12.4 字符串格式化、字典
- 板书:文件:2020-12-4-1.jpg , 文件:2020-12-4-2.jpg , 文件:2020-12-4-3.jpg , 文件:2020-12-4-4.jpg , 文件:2020-12-4-5.jpg
- 视频:第15次理论课
默认的格式化:每个对象都有字符串表示,以便print,以便调试debug(这是python设计理念的一个非常大的优点) obj.__str__(),对象格式化为字符串的默认方法,print时会隐式调用这个方法 obj.__repr__(),提供给python解释器的 建议大家做一个实验,自己定义一个类。看看定义__str__()和__repr__()的效果是什么。 如果不定义,由于每一个类都应该继承object,因此会默认继承object的__str__和__repr__方法,即格式化为'类名+对象内存地址' f = 2/3 f.__str__() # 对于小数点位数比较多的浮点数,__str__有一个默认的输出精度。 print(f) f.__repr__() '%10.2f' % f '%20s' % [1,2,3] # 列表对象当成字符串来做格式化,隐式调用了__str__() print([1,2,3] # 隐式调用了__str__() 格式化的定义:以某种格式将n个相同类型或不同类型的对象转化为一个字符串。四种方法: 1. % 2. str.format() 3. f'' 不常用 4. format() 不常用 ----- %操作符方法,%为一个二元操作符: 形式:'formatting-str' % (a, b, c) (注意后面元组的元素数量要和前方数量对的上) 例: '%d' % 3.1 # '%d'的意思是将对象按照整数来对待,进行格式化。bool、int、float都可以,其他类型的对象(str等)都不可以的,会报错。 '%10d' % 3 -> ' 3' # 为这个整数设置尺寸为10,不够默认用空格填充。 '%010d' % 3 -> '0000000003' # 也是设置整数尺寸为10,不够指定0来填充。 '%10d %5s %7.2f' -> ' 7 abc 3.74',# '%5s'是指定宽度为5的字符串,'%7.2f'是指定总宽度为7的浮点数,精度为2。不够的都默认用空格填充。 除了runoob,这个网页介绍得也比较系统:https://www.cnblogs.com/songdanlee/p/11105807.html 类型(typecode): 形式:%[(name)][flags][width][.precision]typecode, 中括号[]表示参数是可选的(optional) name为字典参数,具体怎么用,可以自己研究下 flags对齐和填充标志 width字符串整体宽度,如果设置过小则无效 precision浮点数精度(小数点后的位数) 精度和有效数字不同,0.001的有效数字是3,0.100的有效数字是3 注:按精度截取时,不一定会按照十进制去四舍五入,而是和计算机存储浮点数(二进制)的原理有关。 '%.2f %.2f' % (3.155, 3.165) -> 3.15 3.17 typecode类型编码,即d、f、s等 %d 整数(十进制);%f 浮点数;%s 字符串; %c 字符(char),对应参数必须为长度为1的字符串,一个ascii码转化为相应字符'A'是65,'a'是97。 ord(),返回一个字符的ascii码(十进制数字),chr()可以把一个ascii码转化成相应字符。 %e =E,转化为科学计数法数字。 %e和%f如果不指定精度,默认会保留到小数点后6位。 科学计数法:45e-5 9.34e2 %g =G,根据值的大小,选择正常输出,或科学计数法。【没搞明白,我从没用过】 %o、%x分别表示八进制和十六进制,等后面会系统讲解,oct hex bin %%:输出literal的% 一些其他例子: '%d %d --- %d' % (3, 4, -7) # '3 4 --- -7' '%10f\n%10f\n%10f' % (9.9, 12.754, 3.34) '%6.3f\n%6.3f\n%6.3f' % (9.9, 12.754, 3.34) # 占6个字节,小数点后保留3位 '%10s' % 'hi' 思考一个编程题目:输出n以内的乘法表,并且用今天的知识对齐,格式化输出。 字符串format()方法:用{}和:来代替%。可以接受很多参数,顺序也可以指定。 通过runoob系统学习:https://www.runoob.com/python/att-string-format.html str.format的优势:1)可以不指定对象类型,使用对象的默认字符串表示;2)可以重用(多次使用)一个参数;3)可以用dict等复杂类型等(很方便) 例如:'{:.2f} {:3d} {:5s}'.format(34.2313, 1, 'sca'),返回'34.23 1 sca ' 还可以在{}内:的前面指定位置,选择format里面的哪个参数。 例如:'{1:.2f} {0:3d}'.format(1, 34.2313),返回'34.23 1' 还可以使用关键字参数:'{name} -> {score}'.format(name='zhenghua', score=98) # 这么写有啥意义呢?感觉在绕弯路。也许有些情况下是有意义的。 字典参数:'{name} -> {score}'.format(**d1) # 其中,d1={'name':'zhenghua', 'score':98, ...} # 这样用对吗?看看runoob的例子,自学一下。 列表参数: '{0[0]}.format([1,2,3]) ----- 字典 先思考一件事情,如何去把一本书里,全部的字都统计出它们的出现次数。(统计词频) 考虑列表存储[['li', 1], ['zhenghua', 1], ...] 如果列表不排序:从左向右按顺序查询是否存在,如果存在则更新(频率+1),否则在最后位置appen。查询的时间复杂度都是O(n),插入的时间复杂度是O(1)。 如果列表排序,那么可以用二分查找,查询是否存在的时间复杂度为O(logn),但是插入时,要保证排序,因此需要把插入位置后面的元素统一后移,因此插入的时间复杂度为O(n)。 字典为此而生,查询和插入的复杂度都为O(logn)【如果想琢磨怎么做到的,可以给一个提示:内部用平衡二叉树来实现】。 字典(dictionary, dict):容器,可迭代,但是不是序列。每一个元素有key和value一个键值对,key需要是完全不可变对象且不能重复,value可以是任意对象。 在字典中,我们可以通过key,来得到value。 定义方法: d1 = {a:b, c:d},直接定义。 d1 = dict([(1, 2), ('abc', 3)]),用工厂函数,传入一个可迭代对象,每次返还两项,这个例子会返还{1:2, 'abc':3} zip函数在这个地方很常用 字典的特点: 可变; key唯一; 无序,即不能假设key的顺序(用平衡二叉树存储的); 可迭代,但不是序列。
2020.11.30 字符串,字符串的方法,字符串的格式化
字符串的拼接:s1 += s2,和s1 = s1 + s2是一样的,创建新的对象。 字符串的乘法:s1 *= 3,和s1 = s1 * 3一样,也会创建新的对象。 python的优化:对于完全不可变对象,无论是浅拷贝,还是深拷贝,都只是做名字绑定,不会新创建对象。 s2 = str(s1),虽然str()是一个工厂函数,但是s2仍然和s1是同一个对象,对于"完全"不可变对象s1没有被复制。再例如: t1 = ((1, 2), 3, 'abc') t2 = tuple(t1) 此程序,t1和t2的id完全相同,即使是deepcopy也会是完全一样的id。 序列浅拷贝的几种方法:工厂函数;全切片;x1 = x2 * 1;copy.copy 序列深拷贝的方法:copy.deepcopy 如何更深入的学习Python? 学习C/C++,这样才可以了解底层:C++ Primer; STL源码剖析(侯捷);Effective C++;More Effective C++ 深入学习和理解python的底层,例如Python源码剖析 in和not in:在一个序列里找到某个元素是否存在。对于字符串,是看一个“子串”,是否存在。而不仅限于一个字符! 例如:'ab' in 'abcd'会返回True。 for i in 'abcd': ... 遍历(迭代iterate)时,会把每一个字符作为单位。为什么是这样呢?为什么不是每两个字符作为一个单元?这是由__iter__()这个特殊方法来决定的。 同学们可以自己写一个类,然后实现__iter__(),来决定如何遍历。如果没有实现__iter__()方法,那么这个类型的对象就不可迭代。 str可以转化为list/tuple对象,例如:list('string')会返回 ['s', 't', 'r', 'i', 'n', 'g']。 list也能转换为str。事实上,所有类型的对象都可以转换为字符串对象。 __str__()和__repr__()这两个特殊方法在起作用 如果你自己写了一个类,没有定义__str__()和__repr__(),那么默认是类型名和内存地址。大家可以做实验试试。 下标索引、切片: s = 'abcdef' # 下标s[1]='b',s[-1]='e' s[3:5] # 'de' s[3:] # 'def' s[:3] # 'abc' s[1:5:2] # 'bd',步长为2 s[-5:-2] # 'bcd' s[::2] # 'ace' s[::-1] # 'fedcba' s[-2,-5,-2] # 'ec' 字符串方法: 请大家结合runoob和help英文文档,系统自学一遍。有些方法很少能用上,了解一下即可。用到了再仔细看文档。 s1.capitalize(),把首个字母变成大写。 s1.center()/s1.rjust()/s1.ljust(),用于格式化输出字符串,s1.center()是把字符串居中输出,参数传入可以指定字符串总长度,可以指定用什么字符来补齐空缺,默认为空格。ljust和rjust就是左对齐和右对齐(justify)。 s1.count(),可以看到子串 (sub-string) 出现了几次。 s1.endswith(suffix),字符串以什么结束。 s1.find(),找一个子串第一次出现的位置下标。 s1.index(),和find一样,只不过找不到会报错。 s1.isalnum(),如果字符串至少长度为1,并且全是字母或数字就返回True。 s1.isspace(),判断是否为空白符,有:'\t'制表符;'\n'换行符;'\r'换行符;'\v'垂直制表;'\f'翻页符(flip over); s1.join(),可以以s1来链接一个可迭代对象,返回一个字符串。 例如:'.'join(['ab', 'cd', 'ef']),会返回'ab.cd.ef'。列表里面的元素需要是str类型。 '.'join('abcd'),返回'a.b.c.d'。 s1.lstrip(),截掉左边的指定字符,默认是空白符,有多少删多少。s1.strip()和s1.rstrip()对应。 s1.maketrans(),字符串映射,可以用于加密。 s1.replace(),把某个子串全部替换为指定字符串。 s1.split(),和s1.join()是相反的,按照分割符(delimiter)切分字符串为列表,例如:'a b c d'.split(),返回['a', 'b', 'c', 'd']。(这个函数会切出来空字符串的) s1.splitlines(),按'\r'或'\r\n'(作为字符串)或'\n'来切分,分割后,如果最后一个是空字符串,会删掉。其他空字符串不会删掉。 'abc'.center(10) # 占10个字节,居中 'abc'.isalpha() # 字符串中所有字符是否都是字母,返回布尔值 ' abc \t\n'.strip() # 去除空白符 '##abc'.strip('#') # 去除指定符号 'abc'.replace('ab','AB') # 替换 'a b c'.split() # 切分字符串 空格符的英文:blank/space characters; 空白符号的英文:whitespace,表示一个集合,\t\n\v\f\r \r\n都表示换行,为什么需要两个字符?return newline。键盘是由打字机而来的。 学习字符串相关的方法和函数,一定要搞清楚,一个参数是一个字符串,还是一个字符集合?很重要! 出几道题目: 打印各种三角形,直角三角形,等边三角形,如下 * ** *** * ** *** * *** ***** * * * * * * * * * * * * * * * (这个不对,不是等边) ----- 字符串相关的内置函数,建议大家自己系统学习一下所有的内置函数: https://www.runoob.com/python3/python3-built-in-functions.html ----- 字符串格式化(formatting):将对象以一定的形式转化为字符串。 默认的格式化方法:__str__()和__repr__() 四种方法,后两种不常用 'formatting-str' % (a, b, c) 'formatting-str'.format(a, b, c) f'formatting-str' format()
2020.11.27 元组的可变性与不可变性,元组的拷贝,字符串
元组的可变性和不可变性:不可变,指的是元组中每个元素指向的对象是不可变的;可变,指的是元组中如果含有可变对象,那么元组中的可变对象元素它自身是可以变化的。 例如:t1 = (1, 2, 3),就是一个完全不可以变化的元组;t2 = (1, [3, 4, 5], 2),这个元组中包含一个列表可变对象,那个列表自己是可以更改的,并且元组也会发生相应的“变化”,但是本质上,元组的几个元素事实上仍指向原来的那几个对象。 完全不可变对象:指一个不可变对象,其包含的所有子孙(容器嵌套)也都是不可变对象。如果一个元祖中包含了列表,那么这个元组就不是完全不可变对象。 元组的浅拷贝和深拷贝:既然有嵌套,元组也有浅拷贝和深拷贝。 元组对象绑定:t3 = t2 元组拷贝 (元组切片) :t3 = tuple(t2);t3 = t2[:]; t3 = copy.copy(t2);t3 = t2 * 1(此处t2和上面的例子是一样的) 元组深拷贝:t3 = copy.deepcopy(t2)。递归(recursive)拷贝,直到“完全”不可变对象。 字符串 (string character):最常用的数据类型,是人机交互的基础。 字(character)级别有英文,中文(C),韩文(K),日文(J),中文韩文日文有很多字符,要比英文大的多,所以需要更多字节存储一个字符。汉字一般是2-6个字节存,英文和数字是1个字节存。 有一些字符是控制字符,比如回车符、空格符、响铃符、制表符等。(ASCII码) 字符串的定义:s1 = 'abc',双单引号; s1 = "abc",双双引号;s1 = '''abc'''三单引号;s1 = """abc"""三双引号。 其中,在python中,单引号和双引号没区别,三单引号和三双引号也是没有区别的,三引号可以换行表示同一个字符串,双引号做不到。 单引号和双引号混用:例如, "john' s" 和 'john\' s' 是一样的。如果都使用单引号,因为计算机区分不出来哪个是终止的单引号,所以需要用转义字符 \' 来表示一个单引号,如果是用双引号,计算机就可以很好的区分出来。 三引号用法:定义字符串时多行输入,或者是作为注释 (comment) 注释 (comment) :只要我们按照规范来写好注释,python可以自动生成文档 (document),可以利用help()来查看文档,或者生成网页文档。有两种方法去进行注释。 利用 # 可以直接注释单行。 利用三引号来写注释。 注释规范:对于函数,在def func()的下面一行来写;对于模块,在最顶端来写。 https://www.runoob.com/python3/python3-comment.html 字符串有不可变性 (immutable),并且在内存中连续存储是一个整体,不可拆分。 特殊字符: 续行符 \,在未结束的语句的行末,就是一个续行符,后面不要再跟东西了。这个续航符不仅仅是在字符串里适用,在普通的语句中一样可以续行。例如: print(a\ , b) print('ab\ cd') 退格符 \b 响铃符 \a 空字符 \0 换行符 \n 制表符 \t (horizontal) 纵向制表符(vertical):\v
2020.11.23 元组,__str__,__repr__,拆分赋值
- 板书:文件:2020-11-23-1.jpg , 文件:2020-11-23-2.jpg , 文件:2020-11-23-3.jpg , 文件:2020-11-23-4.jpg , 文件:2020-11-23-5.jpg
- 视频:第12次理论课
如何去测试一个算法的性能:以顺序和二分查找为例,同一个列表,找很多测试点元素,循环多次测试,分别取其平均值。 iterable和sequence:sequence是一个更严格的概念,zip和enumerate、reversed、文件、range这种类型的对象是可迭代的,但是不是序列。 (区别一个对象是否是可迭代对象可以用for来测试;序列有元组、列表、str) 元组 (tuple):也是一种sequence (内存中连续存储、下标随机访问)。 和列表的主要区别是:immutable 不可变。 元组的定义:t1 = tuple(); t1 = (); t1 = (2, ),必须要有括号内的逗号,否则会优先被python判定为是一个式子,而不是一个只含一个元素的元组。 元组的拼接(concatenation):t1 += (4, 5)和t1 = t1 + (4, 5)是等价的,t1的id已经发生了变化,并不是原来的对象了,新创建了个元组对象。 元组的乘法:t1 = t1 * 3和t1 *= 3,和拼接是一样的,返回新元组对象。 元组的删除:del t1[0]报错,元组不能操作。 元组的切片:返回新元组,切片方式和列表一样。 例:t1[-1:-len(t1)-1:-1],返回一个元组的反转元组。 元组的比较:和列表的比较是一样的,(1, 2, 3, 4, 5) < (1, 2, 3, 4, 5, 1, 2, 3, 4, 5)返回True。 in, not in:查询一个元素在不在元组内,返回bool。 元组和列表的拼接:t1 + l1会报错,列表和元组是不可以拼接的。 元组和列表的相互转换:工厂函数,例:l1 = list(t1),创建一个值和t1元组相等的列表;t1 = tuple(l1),创建一个值和l1列表相等的元组。 元组的方法:只有t1.index()和t1.count(),列表中那些不会对列表操作的方法保留下来了。 序列有关的内嵌函数仍然适用,因为内嵌函数的本质通常是针对一个序列或者一个可迭代对象的,这和是元组或者是列表是没有关系的,然后返回一个新的可迭代对象。例如zip; enumerate; len; max; sum; sorted; reversed; all; any等。 例:len()这个内嵌函数,传入的只需要是一个obj对象即可,只要传入的类有__len__()这个特殊方法,就可以找到它的长度。(什么是类,自己可以提前了解,提前做个实验) sorted()返回新列表。 类特殊方法的__str__和__repr__: __str__:当print()时,会输出其返回值。将当前对象转为str对象时,会使用其返回值。 __repr__:一个对象的自我介绍 (给程序员),默认提供对象的类型,内存地址等。可以自己重写,按照自己想要的格式来print。 可以做个实验。 序列拆分赋值:和之前讲的是一样的,例如:a, *b = (1, 2, 3, 4)。 元组的使用场合: 同步赋值,例如,x, y = 5, 7 函数返回多个对象:return a, b, c 字符串格式化‘%d %f’ % (10 ,5) 元组的可变性和不可变性;元组的浅拷贝和深拷贝(下节课)
2020.11.20 查找,二分查找,unpack,列表生成表达式,模块基础
操作符优先级:一元操作符的优先级普遍高于二元操作符。not的优先级要比and高。 ----- 二分查找: 查找:对于列表,有个方法li.index()可以快速地查找到相应value的下标。 顺序查找:如果是查找一个无序的列表,顺序查找的时间复杂度为O(n) 对于一个已经排序好的列表,可以用二分查找,时间复杂度为O(logn) 二分查找 (Binary Search):查找一个已经排序好的列表,例如,对于一个序列[-4, -1, 0, 1, 3, 5, 6, 8, 10]找到元素8的下标,首先确定一个上界和下界,第一轮把上界和下界限定为整个序列,找中位点,第一次会找到3,发现8比3还大,所以把下界重新设置为5,再找中位点,会找到6,发现比8还是小,在把下界设为8,然后再找一次,就会找到8的位置。二分查找中,每一次查找,都会把列表中一半的元素筛除,所以试的次数最大为log2n,其时间复杂度为O(logn)。 编程题目:随机产生一个数字n,然后随机产生n个数字放到列表中,然后.sort(),然后用自己写的二分查找函数来做查询。可以比较一下二分查找和顺序查找的效率。 ----- zip: 18新利体育 运动会100米决赛8名队员,按照成绩从小到大排序 nums=[1001,1002,1003,1004,1005,1006,1007,1008] marks=[10.01,10.89,11.02,11.02,10.02,10.38,10.95,11.45] 这个题目需要把编号和成绩绑在一起作为一个元素,然后排序。C++的做法是用一个结构体,把两种元素绑定在一起。 python的做法: 1)用zip(marks, nums),把两个列表打包,然后指定key为分值来sort() 2)用zip(nums, marks),把两个列表打包,然后指定key为分值来sort(key = lambda x: x[1]) zip后的遍历: for i, j in zip(nums, marks): print(i, j) ----- 拆包,解包(unpack):又称为”序列”拆分赋值 (同步赋值),使用赋值语句,将序列拆分,绑定到多个名字上。例如: x = 5 x, y = y, x x, y, z = z, x, y x, y = 5, ‘abc’ 赋值语句右侧的对象,被隐式转化为元组(tuple)对象。 e, *f, g = [1, 2, 3, 4],在这个语句中,*f会对应多个元素。最后f的值为[2, 3],是一个列表。 *最多出现一次。左边的变量名数量,可以<=右边序列的len+1。如果左边的变量名的数量为len+1,那么*对应的变量名为空列表 e, *f, g = 'zhenghua',f也是一个列表,每一个元素为长度为1的字符串 ----- 列表生成表达式: [表达式 for 目标1 in 可迭代对象1 [if 条件1] …… for 目标n in 可迭代对象n [if 条件n]] 例:l1 = [i for i in range(1, 100)] 问题1:如何产生10个随机的偶数? 例:l1 = [(x, y) for x in range(5) if x%2==0 for y in range(5) if y%2==1] 最后l1的值为[(0, 1), (0, 3), (2, 1), (2, 3), (4, 1), (4, 3)] 顺序是:给定一个x,遍历所有的y;然给给定下一个x,再遍历所有的y 例:l2 = [abs(i) for i in l1] 例:l3 = [i**2 for i in range(10) if i % 2 == 0] ---- 模块(module): 模块化:在程序中,比较重要的思想就是模块化思维,把大问题分解为一个个的小问题。模块化后的一个很大的优势就是小问题的解可以重复使用(重用)。 模块 (module):在python中,一个模块就是一个.py文件。目前常用的模块有:math, random, time。 如何创建并使用自己的模块(DIY) 1)我们以前写过的一个程序prime100.py,其中包含一个is_prime()函数。下面我们演示如何把这个.py作为一个模块使用。 2)在同一个目录下,创建一个新的.py文件,命名为use_DIY_module.py(不在同一个目录下时,如何使用模块,以后会讲) 3)use_DIY_module.py中写 import prime100 (注意不需要.py) print(prime100.is_prime(7)) 4) 运行use_DIY_module.py,就会发现可以了 包 (package):当模块很多时,需要用目录树来组织,整个目录树就叫包。以后会讲。 后面,我们会专门讲解import prime100,具体发生了什么?以及其他使用模块的其他方法。
2020.11.16 列表的方法,关于序列的内嵌函数
- 板书:文件:2020-11-16-1.jpg , 文件:2020-11-16-2.jpg , 文件:2020-11-16-3.jpg , 文件:2020-11-16-4.jpg , 文件:2020-11-16-5.jpg
- 视频:第10次理论课
时间复杂度(time complexity)和空间复杂度下去自己了解一下。 列表的方法: 形式:列表名.属性,例如li.append()。其中点符号(.)是属性访问符(attribute access symbol)。 注:属性有数据和方法(method, function)。比如math模块,math.pi就是引用了他的数据,math.sin()就是引用了他的方法。 li.append(self, object, /),self是类的内部属性合集,调用方法的时候就会隐式处理,不用传参。 li.reverse(self, /),就地(In Place)反转,返回值是None。 自己写个myreverse,不允许用切片,也不允许用li.reverse(),两个数交换用x, y = y, x li.clear(self, /),就地修改,清空列表。 li.remove(self, value, /),就地修改,删除第一个在li中与value相等的元素,否则抛出异常(Exception) ValueError。 如果不想报错,就在删之前确保value在li中存在,可以用in,也可以用try语句。 li.pop(self, index=-1, /),弹出指定下标的元素,默认最后一个,返回被删除的值。 li.copy(self, /),浅拷贝(shallow copy)。 li.count(self, value, /),返回value在li中 出现的次数。 回去写个函数,删除列表中全部的值为value的元素,count和remove配合即可。 li.index(self, value, start=0, stop=9223372036854775807, /),返回第一个值为value的元素的下标。找不到就抛出异常ValueError,一般不会有内存能存下这么大的列表,所以一般不用担心stop。 回去写一个函数get_all_indices(),找到一个列表中全部的值为value的下标,返回列表。li.count()和li.index()配合使用。 li.insert(self, index, object, /),把object插入到index位置,之前的index所指的元素,及其之后的元素,都被往后推一个。插到最后一个位置等于append()。 总结:学一个对象的方法,首先要知道是否会对对象进行修改;其次要知道是否返回一个新创建对象。 上面有三个题目。 li.sort(self, /, *, key=None, reverse=False),就地排序,是稳定的排序,稳定排序指的就是列表中两个相同的值,相对位置不会发生变化。 参数:reverse是排序完之后是否反转,默认是从小到大;key是要传入一个函数,默认为None就是按照简单的比较 (等讲到函数再详细地展开)。 注:"/"和"*"都是用来在函数限定位置的分隔符,"/"前面的都必须用位置传参,如果用关键字指定就会报错,"/"之后就可以用位置指定和关键字指定了,"*"之后必须用关键字指定。如果"/"和"*"要一起用,“/”必须要在“*”前面,顺序不能反,要不然就矛盾了 与序列相关的内置函数:(和列表的方法是不太一样的) len(); max(); min(); sum(); sorted(); enumerate(); reversed(); all(); any; zip() sorted(iterable, /, *, key=None, reverse=False),和列表的sort()方法差不多。 enumerate(iterable, start=0),返回一个枚举对象,是一个工厂函数。简单来说,在用for遍历列表的时候,用enumerate()包装一下,就会同时返回循环次数。 reversed(sequence, /),返回一个reversed可迭代对象(iterable),就是把原来的列表做成一个迭代器,在迭代器循环就会得到一个反转的列表。 zip(l1, l2, l3. ...),把几个容器打包,变成一个zip可迭代对象,每次返回的是一个元组,打包了那些容器的相应位置的元素。长度不一取短桶。 视频1:26:10位置,提到了一个加分题目。谁如果做完了,把代码和运行结果截图 发给 刘泽洋助教,前3名同学给加分。不要发到群里。
2020.11.13 列表的拼接,列表的乘法,列表拷贝,列表排序
第九次理论课: 第一次考试,第一次考试占总成绩比重不是特别大。 一对一辅导,家教。自己要站起来,主要还是靠自己。 可以查成绩,没有错误减三分。 全切片:l2=l1[:] 。此时发生了什么?对比l2=l1,在内存中怎么画? 列表的拼接: l1 = l1 + l2,新创建一个列表对象,长度为l1和l2的长度之和,并且把l1绑定到新列表对象上。 l1.extend(l2),对l1就地修改,扩大长度,新增元素绑定到l2元素所绑定的对象上。 l1 += l2,和extend()是一样的,在l1原有的基础上增加长度。 增加长度时,后面的内存可能不够,这时候要整体移走。所以id()返回的是逻辑内存地址,而不是物理内存地址。 可以自己做做实验,一直extend列表,看看id是否会发生变化。体会一下内存的逻辑地址和物理地址。 列表的乘法: l2 = l1 * 3,创建个新列表,放了3次l1的元素,最后l1指向的每个对象在l2中有3个名字指向它们。 l1 *= 3,其他的和上一个一样,只不过是就地,不创建新列表。 总结:+=和*=都是就地修改,不创建新的列表 多去理解列表在内存的存储方式,再试试列表里面嵌套列表应该怎么画内存,列表拼接、乘法、拷贝应该怎么画内存的变化。 拷贝(可以称为浅拷贝)的定义:创建一个新对象,和原来的对象“值”相同。 拷贝方法:全切片;用工厂函数(工厂函数见前几次课);copy.copy();l2 = l1 * 1A;li.copy()方法。 例如:l2 = l1[:];l2 = list(l1);l2 = copy.copy(l1);l2 = l1.copy() 总结:浅拷贝只拷贝了列表的指向,但是没有一起拷贝列表指向的对象,此时对可变对象进行操作,拷贝的对象和原列表一起发生变化。 矩阵存储:l1 = [[1,2], [3, 4]](内存中怎么画?) numpy -> pytorch (GPU) 深拷贝:创建一个新对象,和原来的对象的“值”相同,并且与之前的对象毫无瓜葛 (彼此的改变,不会影响到对方)。 深拷贝方法:l2 = copy.deepcopy(l1) 注:深拷贝往往会创建多个对象,遇到容器对象(除str)会递归(recursive)拷贝。 思考:什么情况下,对一个对象做拷贝和做深拷贝存在差异?什么情况下没有区别? 容器嵌套时(str除外,虽然str也是容器)。 例如:列表中包含其他容器对象(除str)时才会存在深拷贝和浅拷贝的问题 列表排序:把一个列表按照一定的规则排序,一般情况是从小到大。 选择排序:每轮从左向右扫描,找到最大的数放到-1位置(-1位置指没有排序的部分末尾),这样每轮都能找到一个最大数,一共n-1次就能把列表变得有序。 冒泡排序:每轮从左向右扫描,每移动一步,就比较一下此时位置相邻两个的大小,如果前面的比后面大,就交换,这样每轮都会有个最大数冒泡到尾端。也是n-1次列表就变得有序了。 (冒泡排序的命名应该来自于一种物理现象:因为水越深水压就越高,所以气泡从水底浮出的过程中,气泡会越来越大,和冒泡排序的原理很像) 归并排序:基于分治法,每次把列表划分为两部分排序,递归到单个元素,时间复杂度很低。 大家做实验对比一下,上面三种排序算法的速度如何? 堆排序: 快速排序: 问题:什么是时间复杂度,这几个排序时间复杂度怎么样。
2020.11.9 循环嵌套(break continue)、列表基础
- 板书:文件:2020-11-9-1.jpg , 文件:2020-11-9-2.jpg , 文件:2020-11-9-3.jpg , 文件:2020-11-9-4.jpg , 文件:2020-11-9-5.jpg , 文件:2020-11-9-6.jpg
- 视频:第8次理论课
画流程图:输出前100个素数(质数) 我们需要用大循环,去寻找到100个素数,但是每找一个素数需要用一个小循环来判定当前数是否为素数。 这就是循环的嵌套。 这个程序有个问题:如果找10000个素数,找到后面会越来越慢,想想可以怎么优化。(通过列表保存前n-1个素数) 但是上述程序看起来非常复杂,如何在逻辑和流程上更加简洁呢?这里要可以用到一个模块化思维:用函数来实现内部循环。 判断一个数是否为素数,可以写一个函数,每个函数只完成一个功能。 基本数据类型、不可变对象:int, float, bool, NoneType, complex, str 什么叫基本数据类型?python自带的。不能再细分为更小的类型。这两种说法都不严谨。还是不用这个说法了。叫标量(非容器)和矢量(容器)好了。 str是一种序列,当然也是容器。 序列: 定义:(有序)、(连续存储)的容器。注意括号内的形容词。 访问方式:可以根据下标访问,常数时间内就能找到,速度极快。 列表:也是一个容器、序列,但是列表是可变对象。 创建列表对象:li = [1, 2, 3] 如何删除列表对象?这个问题本身是有问题的,没有办法直接删除列表对象。del li只是删除了li这个名字,同时取消了名字绑定。如果一个对象没有被任何名字绑定,就会被删除回收(python的垃圾回收机制)。 Q1:列表对象和其包含的对象之间有什么关系? 一个列表对象包含的其实是一堆名字,这些名字分别绑定到对应的对象上。(看一下板书) Q2:列表中可以“包含”不同类型的对象吗? YES 列表嵌套也是可以的 Python vs. C++ 在定义函数时,不需要明确声明参数的类型。执行过程中,python会动态判断参数的类型,进行相应的操作。 强类型程序语言 vs. 弱类型程序语言:这个概念比较模糊,以后不再提了。 列表对象名字绑定:l2 = l1 列表方法(method):(好多函数,自己查表) li.append(),可以在列表后追加一个元素。例如l1.append(l2),会把l2作为一个对象追加到l1后面。 li.clear(),列表清空。 li.count() li.index() li.insert() li.sort() vs. 内置函数sorted() # 仔细先学习一下,下节课会仔细讲一下 l1.extend(l2): 没有创建新的对象;l1的长度增加;l1的地址不变;l2不受影响; 增加了一些名字绑定 列表拼接(concatenation): l3 = l1 + l2,如果l1有3个元素,l2有3个元素,l3此时只会创建一个新对象,进行了7次对象绑定。 l1.extend(l2),在l1列表的尾巴上扩展l2列表,这个操作没有新对象创建,l1地址不变,但是有3个新的名字绑定。(注意这个操作和append()是不一样的) l1 += l2 问题:是否有新的对象创建?内存中发生了什么?in-place(就地)修改,还是新创建了一个列表对象? 列表乘法: l2 = l1 * 3 # 发生了什么,是否有新的对象产生? 列表索引和遍历: for i in l1: for i in range(len(l1)): print(i) print(l1[i]) 找某个位置的元素:l1[1]或者l1[-1],1是列表第二个元素,-1是列表最后一个元素。(j=i-n,此时i和j指向相同位置) 0, 1, ..., n-1 (i) -n, -n-1, ..., -1 (j) i = j + n j = -(n-i) = i - n 列表的切片(slicing,把列表切成子列表): 例如:l1[2:4],返回的是列表的2号元素到3号元素的子列表,和range()一样,是前闭后开。 l1[2:9:2],返回2号元素,4号元素,6号元素,8号元素的子列表。 注:序列中,第2个元素对应着1号元素,我们是从0开始计数的。 列表的比较: l1 is l2,两个名字是否指向一个对象。 l1 == l2,判断两个列表的值是否相等。(值的比较 l1 < l2,从左到右逐渐比较,注意数据类型,没法比较的数据类型就会报错。 成员判断:in关键字,2 in l1,即2这个值是否在l1中。(值的比较)
2020.11.6 2020.11.6 循环(for while else break continue),range函数,pass语句
- 板书:文件:2020-11-6-1.jpg , 文件:2020-11-6-2.jpg , 文件:2020-11-6-3.jpg , 文件:2020-11-6-4.jpg , 文件:2020-11-6-5.jpg
- 视频:第7次理论课
下周二上机课再来一次模拟考试,4个编程题,包括循环和列表。 8道题目:顺序,分支,循环,列表,各2道。 自学内容:做习题集会用到两个内容,都先自学一下。大学要培养自学能力。我希望在上课的过程中,教会大家如何自学,如果检验知识。授之以渔。 字符串格式化:第一次考试(编程题不会考) 模块的使用:math time random,结合dir help 百度 runoob来学习 为什么需要循环呢? 从两个例子入手,来解释。写程序之前请大家先画流程图,不断练习流程图。 例子1:写一个函数,从5个数字中选择最大的数,并返回。 def func(x1, x2, x3, x4, x5): y = x1 if y < x2: y = x2 if y < x3: y = x3 ... return y 如果是100个数呢? 问题 1)100个数字如何作为参数传过去呢?100个参数?显然太多了。 问题 2)即使真的传100个参数,那么这个函数能够做的事情也太局限了,只能处理100个数字;99个数字中选择最大数,还得写另外一个函数 这两个问题,就引出了列表这种数据类型。即可以把很多数字(变长)放到列表中,传给参数。 处理很多个数字、或者长度不确定的列表,就需要(最好)用循环。否则代码会很冗余。 def func(x1, x2, x3, x4, x5): y = x1 for x in x2, x3, x4, x5: # x2, x3, x4, x5其实被隐式的转化为一个元祖tuple对象,for循环对其进行遍历 if y < x: y = x return y 例子2:从一个字符串中统计一个字符(如'a')的次数。字符串类似于列表,也属于长度不定的序列,必须使用循环来遍历。 while和for都可以做。先画流程图。 循环的语法: 循环利用for和while两个关键字来操作。流程图runoob有,或者看一下板书。 https://www.runoob.com/python3/python3-loop.html while: 形式:while condition: code block 意义:如果满足condition这个条件,while就会执行下去 for: 形式:for var in iter: code block 意义:var是一个变量,iter是可迭代对象,序列就属于可迭代对象(如列表,元组,字符串),var会在这个可迭代对象中循环,直到可迭代对象结束。 for循环使用起来更方便,会有隐式的遍历语句,自动增加下标和判断是否结束。 死循环:deadlock,是指循环永远不会退出来,也叫无限循环。有的时候是需要无限循环的。 如果遇到死循环,用Ctrl+C来杀死正在shell运行的程序。 遍历列表操作时,如果要删除列表元素,要非常谨慎。尤其适用for循环时,很容易出错。while循环会安全一些。 a = 0 while a < 10: print(a) a += 2 表格法来学习和理解循环: step a 动作 0 1 打印’1’ a+=2 (3) 1 3 打印’3’ a+=2 (5) 2 5 打印’5’ a+=2 (7) 3 7 打印’7’ a+=2 (9) 4 9 打印’9’ a+=2 (11) 5 11 退出循环 while中的else语法: 形式:while condition: code block A else: code block B 意义:由于condition为False而退出循环时,才执行else。反之,如果循环由于break或return而结束,那么就不会执行else子语句。 for中的else语法:类似于while。 break:从当前循环语句中跳出(包括else一起跳了)。 continue:退出本次循环,直接进入到下一轮循环。注意,continue会在for循环中取出可迭代对象下一个值。 range函数: range()是一个生成函数(generator,生成器;上课时我称为生成器函数,需要确认一下名称),返回range对象(range是一个类型,即这个函数也是工厂函数)。 注:生成数字序列是前闭后开的,即range(10),生成的是0, 1, 2, 3, 4, 5, 6, 7, 8, 9 例:range(15, 3, -2),以15开头,小于等于3结束,步长为-2 pass语句: pass是一个关键字,pass语句是用来空转的,也叫空语句,占位语句。 继续做题,多做题目。
2020.11.2 工厂函数,复合函数,条件控制(分支),选择表达式,做题目
print(),指定sep和end参数时只能通过关键字调用。 工厂函数:类型转换,根据一个给定对象,创建一个属于该类型的“新”对象。(同类型也是可以的) 例如:bool(( )) → False (空元组用括号囊括一个空格) float(‘3e-3’) → 0.003 (e是一种科学计数法,3e-3代表着3*10^-3) 注:传入工厂函数的对象要符合转换规则,complex转化为int就会返回TypeError,错误的字符串传入float会返回ValueError。 复合函数:x = int(input(‘输入一个数:’)),即函数嵌套函数,一个函数的返回值是另一个函数的传入参数。 条件控制: 形式:(请不要以过小的分辨率看以下格式,CB是code block的缩写) if condition: | if condition1: | if condition1: CBA | CBA | CBA else: | elif condition2: | if condition2: CBB | CBB | CBB | else: | CBC | CBC | else: | | CBD 题目:自己画流程图,写函数。审题,注意边界条件,输入类型,返回结果要求。isinstance()可以判断类型。 判断一个数字是奇数还是偶数:偶数返回’even’,奇数返回’odd’。 判断一个年份是否为闰年(能被4整除但是不能被100整除或能被400整除):返回True或者False,传入的year为int型。 判断三个数字中的最大值:返回最大值。 传入一个三位自然数,计算并返回其百位十位个位上的数字元组,如果输入的不是三位自然数返回None。 选择表达式:x if x > y else y (其中x>y是条件判断,x和y必须是两个表达式) (注:赋值语句不是表达式) 例如:print(‘A’) if x > y else print(‘B’) z = x if x > y else y 表达式有哪些?创建对象(理解一下)、算术运算、条件运算、逻辑运算、函数调用(返回对象) 注意:赋值语句不是表达式!
2020.10.30 基本数据类型总结(标量 vs. 矢量)、bool、complex、关键字、name命名规则、工厂函数、对象可变与不可变性、内置函数(help, input, print)
- 板书:文件:2020-10-30-1.jpg , 文件:2020-10-30-2.jpg , 文件:2020-10-30-3.jpg
- 视频:第5次理论课
- Python创建对象时的优化:整数和字符串id测试结果 整数和字符串测试ID (因为浏览器汉字编码方式的不同,所以汉字部分出现了乱码。)
基本数据类型(标量scalar): int(Python没有限制范围);float(Python不分单精度和双精度,C语言区分);str;bool(boolean);... complex(复数): 形式:例如:1 + 2j 定义方式:例如:z = 1+2j;z = complex(a, b);z = a + b*(1j) 关键字:程序预定义的有特殊含义的名字;关键字不允许另作它用,否则执行会出现语法错误 is/ is not:用来判断两个对象是否是同一个对象。简单来说判定一下两个变量是否绑定(binding)在了同一片内存地址(当然这里涉及一些数据结构的问题,不能简单地把对象来等同于内存地址)。 注:变量名字一旦绑定,以后用到这个名字,就等同于用绑定的对象; x和y指向相同的对象,此时 id(y) == id(x); 在Python中,对于一些整数(小于256)或字符串(简单),会共用一块地址。 del:可以销毁对象,Python中也拥有一种垃圾回收机制,定期将程序(进程)中没有任何名字指向的对象销毁。 import;and;or;not;if;else;while;for;True;False... (已讲过) 查看所有的关键字 import keyword print(keyword.kwlist) 对象重用优化:对于一些简单的整数(<256)和字符串对象(注意整数和字符串均为不可变对象),在创建之前,会看内存中是否已有值相同的对象,如果有的话,就会用已有的,而不会重新创建新的对象。 例如 x = 123 y = 123 # 会重用上一个,而不会创建一个新的整数对象 id(x) == id(y) # x和y指向同一个对象 类似的,字符串也是一样的。bool只有两个不同的对象True和False,因此一定会重用。NoneType只有一个对象None,也一定会重用。 但是,其他类型,如浮点数、复数、列表等,就一定不会重用。 x = 0.5 y = 0.5 id(x) != id(y) 注意!!上课时走了一个弯路,找到正确的实验方法后,我也没有给出一个合理的解释。现在解释一下: id(123456789), id(123456789) 为什么id是一样的呢?更合理的解释是:这种对象没有name绑定,因此用完就会销毁;第二个整数对象创建时,会在同一个地址上创建 name的命名规则: 1. 首位:_ a-z A-Z (注意,首位不能是数字) 2. 后续:_ a-z A-Z 0-9 工厂函数(构造函数):int;float;str;bool;list;dict;tuple;set;complex;... 既是class类型名,也是一个函数,可以制造相应对象。 标量(scalar):不可分解的,单一的。以int;float;complex; bool。 矢量(vector,也叫向量、容器container):以list;tuple;dict;set为代表 str也是容器,所以称为矢量也可以的,还可以继续分解为更小的字符串。但是str是基本数据类型,不存在更小的数据类型(如字符)。 可变对象和不可变对象: 不可变对象(Immutable Object):int;str;float;bool;complex;tuple(!);frozenset 可变对象:list;dict;set help函数: help(input):这个语句可以调出来帮助文档,help里可以传入函数、模块、对象、名字... 注:None代表着空数据。 input函数:(我们一般会把函数直接用input()这种方式来写,以后将直接用这种方法来代表函数) input()会读入一个字符串,并且会剥离最后一个回车符。 例如:input: This is Python class.\n 。然后input()会返回'This is Python class.'这个字符串。 注:不可以用x = input(prompt='pls enter a number') 这种格式。理由如下: python3.8加了新的特性,函数可以添加限定位置参数,所以使用prompt传参会报错,课上看help(input)的反斜杠就是限定符标识。详情见:https://zhuanlan.zhihu.com/p/90563819 print函数: 用法:print(value, ..., sep=' ', end='\n') 含义:一次可以print()多个数值,每个数值用'sep'分隔,print结束输出一个'end'符。
2020.10.26 考试形式、IDE、简单语句、字符串、特殊字符
- 板书:文件:2020-10-26-1.jpg , 文件:2020-10-26-2.jpg , 文件:2020-10-26-3.jpg , 文件:2020-10-26-4.jpg , 文件:2020-10-26-5.jpg , 文件:2020-10-26-6.jpg
- 视频:第4次理论课
2020.10.23运动会,缺一次课! 考试: 电脑阅卷,选择题30分钟20~25个题目,立刻进入编程题,3~5个题目,两个小时。 编程题要提交“.py”文件,不要用shell,文件要保存到一个可以找到的地方。 注:注意养成一个文件管理的好习惯:小心误删误覆盖;可以通过复制粘贴来版本控制;不要放桌面也不让保存的目录太深;... 考试会让你写个函数,函数名题目会给定,函数要有传入参数,有返回值。对于想拿高分的同学,要注意边界条件,要缜密思考。 写完函数,用测试语句测试,记得把测试语句注释(#)掉,要不然就用“if __name__ == 'main': ”把测试语句囊括。 PyCharm和IDLE是一个集成的开发环境(IDE:Integrated Development Environment),拥有的功能有: 编辑(edit);运行(run/execute);调试(debug)。PyCharm这学期可能不会讲。 调试方法:单步执行;print();... 简单语句: 6. 逻辑运算(Logical Operation): Q1:operand must be True/False?NO! Q2:return value always be True/False?NO! 例:x = 2 and 3 print(type(x)) Output:3 注:非bool型会有一个隐式转换: 对于int,0 → False != 0 → True 对于float 0.0 → False != 0.0 → True 对于str '' → False 7. 位运算(bit) 8. 优先级(priority)由高到低: 各类括号,指数(**),位运算(~; +; -),算数运算(* / % > + -),关系运算(<; >; ==; >=; <=; !=);逻辑运算(not > and > or),赋值语句(=; -=; +=; /=; *=) 注:详情请见 https://www.runoob.com/python3/python3-basic-operators.html 复杂语句:函数定义;while;if-else;类定义;... 缩进(Indent): 为了复合语句,可以嵌套,但是不要写的过于复杂了。 4个空格或者一个TAB(制表符)为一层缩进。 注:空格和制表符不能交替使用,不过一般的IDE会自动帮你对齐缩进,并且使用一种缩进方法。 制表符不一定是8个空格,可配置,也不一定必须使用4个空格,只要文件内部是一致(consistent)的即可。 字符串(String,简称str):一串连续字符(Character,简称char)。 只考虑英文字符的话,一个字节内存存一个字符。ASCII表把字符映射成唯一数字。 “str”是Python内置(预定义提供)的基本数据类型,在Python中,char和str都属于“str”类型。 1. 定义方式:单引号('这是一个字符串');双引号("这是一个字符串");三单引号('''这是一个字符串) (有区别吗?) 2. 转义字符: '\n'回车;'\\'反斜杠;'\t'制表符;'\b'退格符(backspace);'\a'响铃;... 3. 操作函数: + :一个操作符面对不同场景(操作数类型)有不同的含义,即重载(overloading)。 例:'abc' + '123' 返回 'abc123' len():返回字符串长度。 例:len('abc\n') 的返回值为4。'\n'是一个转义字符,反斜杠(backslash)打头,代表着换行符(回车)。 写一个程序:输出当前时间,新输出的时间刷新旧时间。(提示,利用退格符,PyCharm可以,用cmd也可以,IDLE不行)
2020.10.19 Python和英语的对比、简单语句
python(作为语言)和英语作对比, 字符:大小写字母;数字;#’’... 单词:keywords:while; def;函数名;变量名;数字;字符串。和英语语言不一样,单词可以不符合英语字典里的单词,比如'_a123'对于python也是合法的变量名 句子:statement vs sentence 段落:复合语句,文件,包 vs 节,章,篇,书 语法:非常严格(格式和语法都很严格) vs 比较随意 程序语言和英语哪个难?当然是英语,程序语言有唯一语义。 语句 (statement): 简单语句: 1. 赋值语句assignment:把一个对象(object)绑定(binding)到一个名字(name, 标识符, identifier)上。 例如: x = 7 → 把整数对象绑定在了x上。 注:object是内存中实际存在的东西,有4个属性(attribute): id (identity,地址); 类型 (数据类型,类名,type,class); 数据值/内容; 方法 (method / function) (主要是class类中,详情见后面章节)。 程序执行过程中,不会有重名。 2. 函数调用: 内嵌函数 (build-in):print;abs;help;... 。无需引入“包”即可使用,python默认提供。 import math:math.sqrt(); math.pow(); math.log(); ... 3. import:把一个包引入到当前文件。 4. 算数运算(Arithmetic):加 + ;减 - ;乘 * ;除 / ;整除 // ;余数 % 5. 关系运算(Relational):数字或字符串比大小,小于 < ;大于 > ;等于 == ;不等于 != 。返回bool型,即True & False。 6. 逻辑运算(Logical Operation/Calculus):and ;or ;not。 例如:3>2 and 2<1。 注:逻辑运算符号的两边不一定要是bool型,返回的结果也不一定是bool型,请自行测试学习。 注:运算注意object的type类型;注意算术符优先级;字符串比大小有个字母序(alphabetic)。 复杂语句:函数定义;while;if-else;类定义;...
2020.10.16 计算机组成、数据结构、算法、流程图
计算机组成 冯诺依曼架构 A processing unit that contains an arithmetic logic unit and processor registers (ALU) A control unit that contains an instruction register and program counter (CU) Memory that stores data and instructions External mass storage Input and output mechanisms https://en.wikipedia.org/wiki/Von_Neumann_architecture https://zh.wikipedia.org/zh-hans/%E5%86%AF%C2%B7%E8%AF%BA%E4%BC%8A%E6%9B%BC%E7%BB%93%E6%9E%84 https://www.baidu.com/link?url=WUyrwd9zkEs6eVKyVR9Emg4_pkDsGxJLyrcixtszhapi6XO92QXBlbW-B8f_6dooqxiExzUKir5bCoZhZNvNefxSPO5WSIsI_E-lM2XUgSp5i2i7WY0Ip7tTSwXuy4rEsgtrYO5HOAHElbzI0P1O2mdy-jz9K4-Dx_xvd01rImKxbfrOKyOE4O7cW5U-94xh3XOvQK-8qcWUmN_EnFy2NqauxvnNXHKwWbYN9ZW5JKl8MgTi_HkZ7c6LG75yQ58UZ49MAY98NtbDAVCUXAPruPeO81ozIt36bB1jZOZRQw2PpFK7VV5euTIWOs68_D-i&wd=&eqid=e2294df70000e186000000045f7fd18c 数据和算法 计算 数据 流程图、伪代码:平方根 平方根的其他解法,更优的:https://note.youdao.com/ynoteshare1/index.html?id=828b637ef7ff73a51dd26c1bc59be3e9&type=note
2020.10.12 自我介绍、大学生活学习建议、课程介绍
自我介绍 演示几个平台 对同学的摸底,能力强的同学,不妨做我的基础编程练习,由简入繁,不断积累 大学生活、学习的几点建议 课程介绍(过程化课程) 课堂提问、上机实验(代码和报告)、期中、期末多次考试