文章目录
Python快速入门
前言
早在刚入大学的时候,就听闻了Python的大名了,再加上最近Python连续霸榜编程语言排行榜,早就让我对这门有趣的语言趋之若鹜了,于是我就趁着课余时间快速入个门,大致窥探一下Python的神秘面纱。它的应用范围极广,数据分析、数据可视化、网络爬虫,Web开发、人工智能……基本上互联网的各个角落,各个行业都有它的身影,它因此也被称为”胶水语言“。”人生苦短,我用Python“让我们一起来快速了解Python的神秘面纱吧(●’◡’●)
1、Python基本介绍
-
Python是什么?
Python由荷兰数学和计算机科学研究学会的吉多·范罗苏姆于1990年代初设计,作为一门叫做ABC语言的替代品。Python提供了高效的高级数据结构,还能简单有效地面向对象编程。Python语法和动态类型,以及解释型语言的本质,使它成为多数平台上写脚本和快速开发应用的编程语言, 随着版本的不断更新和语言新功能的添加,逐渐被用于独立的、大型项目的开发。
Python官网:🚪传送门
Python3.11.0中文文档:🚪传送门
简而言之:Python是由 吉多·范罗苏姆(江湖人称“龟叔”) 开发的一款采用面对对象编程的脚本语言。
-
Python能干什么?
Python相较其它其它语言,可以说是一个“万金油”的存在,能干的东西很多,比如:爬虫(数据爬取)、机器学习(人工智能)、数据处理、图像处理、Web开发、自动化、科学计算……
可以看到目前大火的爬虫、人工智能、大数据、自动化都是与Python息息相关的,这也是它最近几年霸榜编程语言排行榜的资本。可以说在当今大数据时代,就算你不是专职Python开发,Python也是是一门很有必要学习的语言,你能够使用Python编写一些自动化的脚本,大大简化你重复的工作,解放你的双手,让你能够有更多的时间去干一些有意义的事,而不是重复意义不大的体力劳动
-
Python的特点
- 简单易学。其一,Python是一种弱类型语言,编程约束较小;其二,Python是一门高级语言,阅读一个良好的Python程序就感觉像是在读英语一样,它使你能够专注于解决问题而不是去搞明白语言本身;其三,Python生态很好,有很多完善的库和工具,以及说明文档
- 应用广泛。Python是一个“万金油”,能够应用的范围很广
- 面对对象。Python采用面对对象的思想进行编程,编写的程序具有良好的可移植性、可维护性和可扩展性
- 解释性。Python是一款解释型的语言,在执行过程中由解释器逐行分析,逐行运行
- 免费开源。Python是FLOSS(自由/开放源码软件)之一。使用者可以自由地发布这个软件的拷贝、阅读它的源代码、对它做改动、把它的一部分用于新的自由软件中。FLOSS是基于一个团体分享知识的概念
……
-
Python的历史
1989年,为了打发圣诞节假期,Gudio van Rossum吉多.范罗苏姆(龟叔)决心开发一个新的解释程序(Python雏形)
1991年,第一个Python解释器诞生
Python这个名字,来白色叔所垫爱的电视Monty Python’s Flying CircusPython单词原意是蟒蛇,所以Python的log是两条蟒蛇
Python的详细历史见:👉这里
2、Python安装&卸载
2.1 安装
-
Step1:下载Python
Python官网:🚪传送门
-
Step2:安装Python
直接无脑下一步即可,中途需要选择一下安装路径
备注:如果安装时遇到
Error writing to file
,可以参考这篇文章安装软件时提示【An error occurred while trying to create a file in the destination directory: 拒绝访问】,问题类型是一致的,都是由于安装文件权限不够,需要右击Python安装包,然后以管理员身份运行至于为什么要关闭路径长度限制可以参考这篇文章:在 Python 中禁用路径长度限制 | D栈 - Delft Stack
其实主要是由于在Windows中限制了文件的最长路径只能为260 个字符,如果文件名或路径过长,会导致Python在编译时报错,但这个在Linux和macOS中不存在
-
Step3:验证Python是否安装成功
-
Step4:编写第一个Python程序
经典案例:使用Python输出
Hello World!
备注:Python编写的文件是以.py
结尾的
2.2 卸载
右击左下角的window键,选在下面那个选项:
点击进入下面页面,搜索python点击卸载Python驱动
注意:不要卸载Python Laucher!python launcher是用于Windows中的一个实用程序,可帮助定位和执行不同版本的Python解释器。
允许脚本或者命令行指示特定的Python版本的首选项,并将定位并执行该版本。简单地说就是它能很智能的区分开是想要手动指定解释版本执行py脚本,还是想使用脚本文件指定的解释器。
3、使用PyCharm编写Python程序
前面我们在cmd窗口中运行了第一个Python程序,对于Python的编写,只要你够强大,可以直接使用记事本编写,但是对于新手这是地狱难度,没有代码提示,没有错误提示,简直就算一个煎熬。所以为了提高开发效率,让我们享受开发过程,我们需要使用专门的Python开发工具,Python开发工具有很多,这里推荐一款 PyCharm,这里就不多做介绍了,直接上手吧(●ˇ∀ˇ●)
-
Step1:创建Python工程
-
Step2:编写Python程序
总结
就我对PyCharm这款软件的初体验而言,它和我经常使用的IDEA的风格、设置、快捷键都是极其相似的,毕竟它们都是隶属于jetbrains公司的产品,所以整体而言用起来还是相当丝滑的。有一说一它们家的产品雀氏都挺好用的,而且还很人性化,提供了免费版的社区版,如果条件的可以直接购买专业版也是挺香的。
4、Python基础语法
4.1 基础概念
-
字面量:在代码中,被写下来的的固定的值,称之为字面量。本质就算一个值,每个编程语言中都由字面量这个概念。
例如:1(整数类型的字面量)、1.12(浮点数类型的字面量)、“ghp”(字符串类型的字面量)
-
变量:在程序运行时,能储存计算结果或能表示值的抽象概念。简单的说,变量就是在程序运行时,记录数据用的,相当于一个容器。格式:
变量名 = 变量值
。变量的目的是存储运行过程的数据,存储数据的目的是为了重复使用注意:变量无类型,数据有类型(变量只是一个临时存储数据的容器)。可以使用
type()
查看变量存储的数据类型示例:
-
标识符:是用户在编程的时候所使用的一系列名字,用于给变量、类、方法等命名。
标识符的命名规则:
-
内容限定:标识符命名中,只允许出现英文、中文、数字、下划线_,且不能以数字开头(不建议使用中文,其实只要按照Java的命名规则来,是绝对不会错的,毕竟Java要求比Python严格的多)
-
大小写敏感:严格区分大小写,A和a是两个完全不同的变量
-
不可使用关键字:关键字是Python中的赋有特殊意义的字符,关键字有以下这些
多个单词的变量可以采用驼峰命名法,但一般推荐使用下划线命名法,即:所有单词小写,单词之间以_间隔
-
-
注释:在程序代码中对程序代码进行解释说明的文字。
作用:注释不是程序,不能被执行,只是对程序代码进行解释说明,让别人可以看懂程序代码的作用,能够大大增强程序的可读性。
-
单行注释:以 #开头,#右边 的所有文字当作说明,而不是真正要执行的程序,起辅助说明作用
温馨提示:Python规范要求#与注释的内容之间空一格
-
多行注释: 以一对三个双引号引起来 (“”“注释内容”“”)来解释说明一段代码的作用使用方法
-
4.2 数据类型
-
Python中常见的6种数据类型:
注意:在Python种不需要手动定义数据类型(这是弱类型语言的特点,和Java是大不相同的)
- str的定义方式:
str1 = "我是字符串(文本)数据" str2 = '我也是字符串' str3 = """没想到吧,我即能做注释,也是字符串哦""" # 不同引号间可以相互嵌套
-
数据类型的转换
函数 说明 int(x)
将x转换成一个整数 float(x)
将x转换成一个浮点数 str(x)
将对象x转换成字符串 repr(x)
将对象x转换成字符串 注意事项
- 任何类型,都可以通过str(),转换成字符串
- 字符串内必须真的是数字,才可以将字符串转换为数字
知识拓展:
str
和repr
的异同- 相同点:两者都能够将其它类型的数据转换成字符串
- 不同点:
str
的范围更广,它能将任何类型的数据转换成字符串,转换的字符串是适于人类阅读的;而repr
只能转换成python解释器阅读的字符串,适合在开发和调试阶段使用,如果没有等价的语法,则会发生SyntaxError
异常
示例:
str0 = "abc" str1 = str(str0) str2 = repr(str0) print(type(str1)) print(str1) print(type(str2)) print(str2)
-
Python种数据类型的分类:
也可以根据数据是否可变分为可变数据类型和不可变数据类型
-
可变数据类型:数据存储空间中的数据可以发生变化的数据。
例如:列表、集合、字典、对象
-
不可变数据类型:数据存储空间中的数据不能发生变化的数据。
例如:数值、字符串、布尔、元组,其中数值型又包括:整型 int、 长整型 long、浮点型 float、 复数 complex
-
4.3 运算符
Python中主要有:算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符、身份运算符、成员运算符
-
算术运算符
-
赋值运算符
-
比较运算符
-
逻辑运算符
-
位运算符
-
身份运算符
-
成员运算符
知识拓展:各运算符的优先级
4.4 字符串相关操作
本小节主要会介绍:
字符串相关操作符:
格式化字符串:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Os5mAgLf-1679931080305)(null)]
格式化字符串的辅助符号:
-
字符串拼接
-
方式一:
str1 + str2
用法和Java中一样,略……
注意事项:字符串和非字符串无法进行拼接,强制拼接会报
TypeError
异常 -
方式二:
"str1" "str2“
例如:
str1 = "a" "123" print(str1) # a123
注意事项:该方式只能拼接字符串常量
-
-
字符串格式化
-
方式一:
%s
name = "张三" age = "19" address = "地球" print("大家好,我叫%s" % name) print("我今年%s,来自%s" % (age, address))
-
方式二:
f内容{变量1}内容{变量2}
name = "张三" age = "19" address = "地球" print(f"我是{name}\n我今年{age}岁,来自{address}") # 输出和上面是一样的
备注:这种方法无法对数字类型的变量进行精度控制,适用于对精度没有要求时的格式化
上诉两种方式不仅能够格式化字符串,也能格式化表达式1
-
-
格式化精度控制
%m.n
:m控制数字字符串显示的宽度,n控制数字字符串的精度a = 314.4926 print("%1d" % a) print("%2d" % a) print("%3d" % a) print("%4d" % a) print("%10d" % a)
输出:
314 314 314 # m<=a的整数位时,没有影响;m>a的整数位时,用空格补位(m是%md中的m,取整数) 314 314
a = 314.4956 print("%2.2f" % a) print("%3.2f" % a) print("%4.2f" % a) print("%10.2f" % a) print("%10.10f" % a)
输出:
314.50 314.50 314.50 314.50 # n<=a的小数位是,四舍五入取n位小数;n>a的小数位时,用0补位 314.4956000000
总结:
- 当数字的位数大于等于m时, m无作用;当数字的位数小于m时,m是多少就显示多少位,不足的在最前面补空格(如果是%d,则只需要看整数的位数,如果是%f,则是看所有数字的位数)
- 当数字的小数位大于m时,n是多少就按照四舍五入显示多少位小数;当数字的小数位小于m时,不足的在末尾补0
4.5 input函数
-
input
函数:用于接收用户在控制台的输入注意:input能接收任何类型的数据,但是得到的数据最终都会是str类型
4.6 流程控制
-
分支结构
age = int(input("请输入你的年龄:")) if age < 0: print("年龄输入有误!") elif age > 18: print("已成年") else: print("未成年")
注意:在Python中通过首行缩进来判断层级关系,这个不像其它语言是使用
{}
来确定层级关系的 -
循环结构
# while循环 s = 0 i = 1 while i <= 100: s += i i += 1 # 备注:在Python中没有i++,但是有++i print(s) # 输出:5050 # for循环 strs = "123456" for i in strs: print(i) # 遍历一行一行第依次输出1、2、3、4、5、6
break
:直接结束当前循环(跳出当前这一层循环)continue
:直接结束本次循环,进行紧接着的下一轮循环
注意:
for in
只能遍历可迭代对象。可迭代对象是指存储了元素的一个容器对象,且容器中的元素可以通过__iter__( )
方法或getitem( )
方法访问,常见的有:字符串、列表和元组知识拓展:
-
range
函数-
range(num)
:得到一个**[0,num)**的数字序列 -
range(num1,num2)
:得到一个**[num1,num2)** -
range(num1,num2,step)
:得到一个**[num1,num2)**,相邻数字之间间隔steps = 0 for i in range(101): s += i print(s)# 输出:5050
-
-
作用域访问规范:for循环中的临时变量,其作用域限定在循环内,但实际上在外部也能访问
示例如下:
# 错误示范:在for循环外部访问for循环内部的临时变量 for i in range(3): print(i) print(i) # 正确示范:规范要求不能直接在外部访问for循环内部的临时变量,想访问需要定义一个外部变量(虽然两者效果是等价的) # 这该死的规范w(゚Д゚)w i = 0 for i in range(3): print(i) print(i)
5、函数
函数基本上是高级语言中必备的东西,C、C++、C#、Java、JS、Go、Php等等都是有函数这个感念的,当然Java中可能是叫作方法,但是本质都是一样的,函数就是是组织好的,可重复使用的,用来实现特定功能的代码段。在前面我们已经接触过了Python中的一些常见函数,比如:print、input、range,现在就让我们系统地学习Python中的函数吧
5.1 函数基本概念
-
Python中函数的分类:
- 内置函数:Python内部预定义的函数,可以直接使用。常见的如:print()、int()、str()、input()……内置函数详情见:这里
- 库函数:需要通过import导入第三方库后,就能直接使用的
- 自定义函数:开发者自己编写的函数
-
函数的定义:
def 函数名(形参1,形参2,...): 函数体 return 返回值 # 备注:如果函数没有参数就省略;如果函数没有返回值就省略
示例:
自定义一个
len
函数,用于计算字符串的长度def my_len(data): count = 0 for i in data: count +=1 print(f"字符串{data}的长度是{count}") my_len("123") # 字符串123的长度是3 strs = "abc 123" my_len(strs) # 字符串abc 123的长度是7
-
局部变量:是定义在函数体内部的变量,即只在函数体内部生效
-
全局变量:指的是在函数体内、外都能生效的变量
知识拓展
None
当我们函数没有return时,其实是存在返回值时,此时的返回值为
None
,该字面量的类型是<class 'NoneType'>
。None这个字面量None表示:空的、无实际意义的意思。函数返回的None,也就是表示这个函数没有返回什么有意义的内容。也就是返回了空的意思。None的应用:
- 用于函数返回值
- 用于if判断,表示false
- 定义变量,但暂时不需要变量有具体值,可以用None来代替
其实它的含义就相当于Null
函数的说明性文档
def add(a, b): """ 求两数的和 :param a: :param b: :return:a和b的和 """ result = a + b return result print(f"所求和为{add(1, 2)}") # 输出:所求和为3(鼠标悬停再add上,能够看到文档注释)
函数的多个返回值
def test_return(): return 1, 2 x, y = test_return() print(x) # 1 print(y) # 2
备注:返回值按照顺序返回,支持不同类型的多个数据返回
5.2 函数的参数
函数的传参方式
-
位置参数:调用函数时根据函数定义的参数位置来传递参数
def user_info(name, age, gender): print(f'您的名字是{name},年龄是{age},性别是{gender}') user_info('TOM', 20, '男') # 您的名字是TOM,年龄是20,性别是男
-
关键字参数:函数调用时通过“键=值”形式传递参数
def user_info(name, age, gender): print(f"您的名字是:{name},年龄是:{age},性别是:{gender}") # 关键字传参 user_info(name="小明", age=20, gender="男") # 您的名字是:小明,年龄是:20,性别是:男 # 可以不按照固定顺序 user_info(age=20, gender="男", name="小明") # 您的名字是:小明,年龄是:20,性别是:男 # 可以和位置参数混用,位置参数必须在前,且匹配参数顺序 user_info("小明", age=20, gender="男") # 您的名字是:小明,年龄是:20,性别是:男
-
缺省参数:缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用)
作用:当调用函数时没有传递参数, 就会使用默认是用缺省参数对应的值
def user_info(name, age, gender='男'): print(f'您的名字是{name},年龄是{age},性别是{gender}') user_info('TOM', 20) # 您的名字是TOM,年龄是20,性别是男 user_info('Rose', 18, '女') # 您的名字是Rose,年龄是18,性别是女
-
不定长参数:不定长参数也叫可变参数. 用于不确定调用的时候会传递多少个参数(不传参也可以)的场景
作用:当调用函数时不确定参数个数时, 可以使用不定长参数
不定长参数的类型: ①位置传递 ②关键字传递
-
位置传递:
传进的所有参数都会被args变量收集,它会根据传进参数的位置合并为一个元组(tuple),args是元组类型
def user_info(*args): print(args) user_info('TOM') # ('TOM',) user_info('TOM', 18) # ('TOM', 18)
-
关键字传递:
参数是“键=值”形式的形式的情况下, 所有的“键=值”都会被kwargs接受, 同时会根据“键=值”组成字典,kwargs是字典类型
def user_info(**kwargs): print(kwargs) user_info(name='TOM', age=18, id=110) # {'name': 'TOM', 'age': 18, 'id': 110}
-
-
函数参数:通过将函数名作为参数传递,这是一种计算逻辑的传递,而非数据的传递
def test_func(compute): result = compute(1, 2) print(result) def compute(x, y): return x + y test_func(compute) # 3
5.3 匿名函数
在函数的定义中:
def
关键字,可以定义带有名称的函数,可以重复使用lambda
关键字,可以定义匿名函数(无名称),不可重复使用
-
匿名函数的定义:
lambda 传入参数: 函数体
注意事项:匿名函数的函数体只能写一行,无法写多行代码
示例:
# 非匿名函数 def test_func(compute): result = compute(1, 2) print(result) def compute(x, y): return x + y test_func(compute) # 3 # 匿名函数 def test_func(compute): result = compute(1, 2) print(result) test_func(lambda x, y: x + y) # 3
6、数据容器
6.0 前置知识
-
什么是数据容器?
数据容器是一种可以容纳多份数据的数据类型,容纳的每一份数据称之为1个元素每一个元素,可以是任意类型的数据,如字符串、数字、布尔等。
-
数据容器的分类
根据是否支持重复元素、是否可以修改、是否有序等特点,可以分为:列表(list)、元组(tuple)、字符串(str)、集合(set)、字典(dict)
-
方法与函数
前面我们接触到了函数这一概念,而将函数放在类中,我们称之为方法。和函数不同的是:函数可以直接使用函数名在外部进行调用,而方法必须先创建对象,通过所在类的对象进行调用
示例:
# 函数 def my_len(data): count = 0 for i in data: count +=1 print(f"字符串{data}的长度是{count}") strs = "abc" print(my_len(strs)) # 输出:3 # 方法 class Tool: def my_len(self, data): count = 0 for i in data: count += 1 return count tool = Tool() print(tool.my_len(strs)) # 输出:3
6.1 列表 list
列表的定义:列表名 = [数值1, 数值2, ...]
# 列表类型的字面量
[1, "abc", "啊啊啊", 4.23, True, [1, 2, 3]]
# 定义列表
lists = [1, "abc", "张三", 4.23, True, [1, 2, 3]]
# 列表具有索引,可以通过索引访问列表中的元素
print(lists[0]) # 1
print(list[-6]) # 1
print(lists[2][1]) # 三
print(lists[5][1]) # 2
print(lists[2:len(lists)]) # ['张三', 4.23, True, [1, 2, 3]]
# 定义空列表
lists = []
lists = list()
注意:列表可以一次存储多个数据,且可以为不同的数据类型,支持嵌套
6.1.1 列表的基本操作
包括:查询元素、插入元素、删除元素、清空列表、修改元素、统计元素个数
-
查找功能
-
查找列表中值对应的索引:
列表名.index(元素值)
names = ["张三", "李四", "王五"] print(names.index("李四")) # 输出:1 # print(names.index("赵六")) # 报错:ValueError
-
查早列表中的值:
列表名[索引号]
names = ["张三", "李四", "王五"] print(names) # 输出:["张三", "李四", "王五"] # 通过索引查找,请参考列表的定义的示例代码
-
-
修改功能
列表名[索引号] = 值
names = ["张三", "李四", "王五"] names[0] = "赵六" print(names) # ['赵六', '李四', '王五'] # 可以进行反向下标进行赋值,names[-1]等价于names[2],-2对应1,以此类推... names[-3] = "赵六" # names[-4] = "赵六" # 报错:ValueError print(names) # ['赵六', '李四', '王五']
-
插入功能
-
在列表指定索引进行插入:
列表名.insert(索引号, 元素)
names = ["张三", "李四", "王五"] names.insert(1, "赵六") print(names) # ['张三', '赵六', '李四', '王五']
-
在列表尾部追加元素:
1)方式一:
列表名.append(元素值)
将其它数据直接追加到列表尾部
names = ["张三", "李四", "王五"] names.append("赵六") names.append([1, 2, 3]) print(names) # ['张三', '李四', '王五', '赵六', [1, 2, 3]]
2)方式二:
列表名。extend(元素值)
将其它数据容器的内容取出,然后依次追加到列表后
names = ["张三", "李四", "王五"] names.extend("赵六") names.extend([1, 2, 3]) print(names) # ['张三', '李四', '王五', '赵', '六', 1, 2, 3]
-
-
删除功能
-
方式一:
del 列表名[索引号]
names = ["张三", "李四", "王五"] del names[1] print(names) # ['张三', '王五']
-
方式二:
列表名.pop(索引号)
names.pop(1) print(names) # ['张三', '王五']
-
方式三:
列表名.remove(元素值)
names = ["张三", "李四", "王五"] names.remove("张三") # names.remove("赵六") # 列表中未找到待删除的值,会报ValueError print(names) # ['李四', '王五']
-
-
清空功能
列表名.clear()
names = ["张三", "李四", "王五"] names.clear() print(names) # []
知识拓展
count
:统计某元素在列表中出现的次数lists = [1, 1, 1, 2, 3] print(lists.count(1)) # 3
列表学习总结
- 一个列表可以容纳多个元素(上限为2**63-1)
- 一个列表可以容纳多种数据类型
- 存储在列表中的数据都是有序的(每个元素都有与之对应的索引号)
- 列表允许重复数据存在
- 列表中的数据是能够增加、删除、更新的
6.1.2 列表的遍历
-
使用while循环遍历列表
names = ["张三", "李四", "王五"] i = 0 while i < len(names): print(names[i]) i += 1
-
使用for循环遍历列表
names = ["张三", "李四", "王五"] for name in names: print(name)
-
while遍历和for遍历的比较:
- 在循环控制上:while循环可以自定循环条件,并自行控制for循环不可以自定循环条件,只可以一个个从容器内取出数据
- 在无限循环上:while循环可以通过条件控制做到无限循环for循环理论上不可以,因为被遍历的容器容量不是无限的
- 在使用场景上:while循环适用于任何想要循环的场景for循环适用于,遍历数据容器的场景或简单的固定次数循环场景
6.2 元组 tuple
前面我们学习了列表(list),可以发现列表的灵活性很大,但是存储的数据是能够被任意修改的,在一些特地的场合,我们只想要展示数据,并不想要数据容器中的数据被修改,这就需要使用元组来存储数据了
-
元组的定义:
元组名 = (数值1, 数值2, ...)
# 元组类型的字面量 (1, "abc", "啊啊啊", 4.23, True, [1, 2, 3]) # 定义元组 tuples = (1, "abc", "张三", 4.23, True, [1, 2, 3]) print(tuples) # (1, "abc", "张三", 4.23, True, [1, 2, 3]) # 元组具有索引,可以通过索引获取元组的值 # 列表具有索引,可以通过索引访问列表中的元素 print(tuples[0]) # 1 print(tuples[-6]) # 1 print(tuples[2][1]) # 三 print(tuples[5][1]) # 2 print(tuples[2:len(tuples)]) # ('张三', 4.23, True, [1, 2, 3]) # 定义空元组 tuples = () tuples = tuple()
⚠注意事项:当定义的元组只有一个元素时,一定要记得在元素后面带上
,
比如:tuples = (数值,)
,这样才是一个元组类型,否则就是该数值本来的类型 -
元组相关操作
示例:
tuples = (1, "abc", "张三", 4.23, True, [1, 2, 3]) # print(tuples.index("1")) # 元组中不存在"1"这个元素,直接ValueError print(tuples.index(1)) # 0(直接按照索引号由小到大遍历,查找到元组中第一个出现该数值的索引号) print(tuples.count(1)) # 2 print(len(tuples)) # 6 # tuples[0] = 2 # 修改元组报TypeError
⚠注意事项:元组不支持修改操作(增加、删除、修改),但如果元组中嵌套了一个列表,那么元组中的列表是可以被修改的
tuples = (1, "abc", "张三", 4.23, True, [1, 2, 3]) tuples[5][1] = "我被修改了" print(tuples) # (1, 'abc', '张三', 4.23, True, [1, '我被修改了', 3])
-
遍历元组
-
方式一:while循环遍历
tuples = (1, "abc", "张三", 4.23, True, [1, 2, 3]) i = 0 while i < len(tuples): print(tuples[i]) i += 1
-
方式二:for循环遍历
tuples = (1, "abc", "张三", 4.23, True, [1, 2, 3]) for tup in tuples: print(tup)
-
元组学习总结
- 一个元组可以容纳多个元素(上限为2**63-1)
- 一个元组可以容纳多种数据类型
- 存储在元组中的数据都是有序的(每个元素都有与之对应的索引号)
- 元组允许重复数据存在
- 列表中的数据是不能够进行修改(增加,删除,更新)的,但是嵌套在元组中的列表可以进行修改
- 支持while和for循环遍历
6.3 字符串 str
字符串是存储一个一个字符的容器
-
字符串的定义
-
字符串相关操作
-
根据索引查找值:
字符串名[索引号]
# 字符串同列表、元组一样也是具有索引的 strs = "123abc" print(strs[5]) # c print(strs[-1]) # c print(strs[0:-1]) # 123ab
-
根据值查找索引:
字符串.index(字符串)
strs = "123abc" # print(strs.index(2)) # TypeError print(strs.index("2")) # 1 print(strs.index("12")) # 0
-
替换字符串:
字符串.replace(字符串1, 字符串2)
strs = "123abc" # print(strs.replace("0", "2")) # 不报错,但没有任何效果 print(strs.replace("123", "456")) # 456abc
-
通过字符串得到一个列表:
字符串.split(字符串)
strs = "123abc" lists = strs.split("3") print(strs) # 123abc print(lists) # ['12', 'abc']
-
移除字符串首尾的空格和换行符或指定字符串:
字符串.strip()
strs = " 123abc \n" # 演示去除字符串前后空格和回车 print(strs) print(strs.strip()) print(strs) # 演示去除字符串前后指定的字符串 print(strs.strip(" 123")) # abc \n
-
统计字符串内某字符串出现的次数:
字符串.count(字符串)
-
计算字符串的长度:
len(字符串)
-
-
字符串的遍历
- 方式一:while循环遍历
- 方式二:for循环遍历
字符串学习总结
- 一个字符串可以容纳多个字符(上限取决你电脑的内存大小)
- 一个字符串只能容纳字符串类型
- 存储在字符串中的字符都是有序的(每个字符都有与之对应的索引号)
- 字符串允许重复字符存在
- 字符串中的字符是不能够进行修改(增加,删除,更新)的
- 支持while和for循环遍历
6.4 序列
序列是指内容连续、有序,可使用下标索引的一类数据容器,列表、元组和字符串均可以可以视为序列。
-
序列的常用操作:
切片:是指从一个序列中,取出一个子序列(其实在前面早已经接触过了)
语法:
序列名[起始下标:结束下标:步长]
示例:
strs = "1234567890" print(strs[0:10:1]) # 1234567890 print(strs[0:10:]) # 1234567890 print(strs[0:10]) # 1234567890 print(strs[::1]) # 1234567890 print(strs[0:10:2]) # 13579 print(strs[:10:2]) # 13579 print(strs[::2]) # 13579 print(strs) # 1234567890
注意:此操作不会影响序列本身,而是会得到一个新的序列(列表、元组、字符串…)
6.5 集合 set
前面我们学了列表、元组、字符串,列表能够被修改,同时支持重复数据的存在,而元组和字符串不能够被修改,但同样支持重复数据的存在。假如我们不想一个数据容器不存在重复数据呢?显然前面学到哪些数据容器是不能够实现的,这就需要使用到集合。集合最主要的特点是不支持元素的重复(自带去重功能)、并且内容无序
-
集合的定义:
# 集合字面量 {1, 1.123, "abc"} # 定义集合变量 sets = {1, 1.123, "abc"} print(sets) # 定义空集合 # sets = {} # 注意这种不是定义一个空集合,是定义了一个空字典 sets = set()
⚠注意:集合不支持索引获取集合中的元素(因为无序),同样不支持切片、嵌套列表、元组、集合
-
集合相关操作
-
添加元素:
集合.add(元素)
sets = {1, 1.123, "abc"} sets.add("张三") print(sets) # {'abc', 1, '张三', 1.123} # 这里需要注意使用print打印集合中的元素,打印结果是随机的
-
移除元素:
-
移除指定元素:
集合.remove(元素)
sets = {1, 1.123, "abc"} # sets.remove("张三") # KeyError: '张三' sets.remove(1) print(sets) # {'abc', 1.123}
-
随机移除元素:
集合.pop()
sets = {1, 1.123, "abc"} sets.pop() print(sets) # {1, 1.123} # 注意是**随机**移除集合中的一个元素
-
-
清空集合:
集合.clear()
sets = {1, 1.123, "abc"} sets.clear() print(sets) # set()
-
取两个集合的差(取集合1中有而集合2中没有的元素),不改变原有集合:
集合1.difference(集合2)
set1 = {1, 1.123, "abc"} set2 = {"张三", 1, "1", "abc"} print(set1.difference(set2)) # {1.123} print(set1) # {1, 'abc', 1.123} print(set2) # {'张三', 1, 'abc', '1'}
-
消除两个集合的差(去掉集合1中集合2存在的元素),集合1被修改,集合2不改变:
集合1.difference_update(集合2)
set1 = {1, 1.123, "abc"} set2 = {"张三", 1, "1", "abc"} set1.difference_update(set2) print(set1) # {1.123} print(set2) # {1, '1', '张三', 'abc'}
-
取两个集合的并(取集合1和集合2中所有的元素,相同元素取一个),不改变原有集合:
集合1.union(集合2)
set1 = {1, 1.123, "abc"} set2 = {"张三", 1, "1", "abc"} print(set1.union(set2)) # {1, 1.123, '1', '张三', 'abc'} print(set1) # {1, 1.123, 'abc'} print(set2) # {1, '1', '张三', 'abc'}
-
获取集合中元素的数量:
len(集合名)
sets = {1, 1.123, "abc"} print(len(sets)) # 3
-
-
集合的遍历:
sets = {1, 1.123, "abc"} for s in sets: print(s) # 结果 1 abc 1.123
注意:集合不支持while循环遍历,因为集合无法通过索引获取元素
集合学习总结
- 一个集合可以容纳多个数据
- 一个集合可以容纳不同类型的元素,但是无法嵌套列表、元组、集合
- 存储在集合中的数据都是无序的,无法通过索引获取集合中的元素,不属于序列,所以也无法使用切片
- 集合不允许重复数据存在
- 集合中的中的数据能够可以进行修改(增加,删除,无法进行更新)的
- 仅支持for循环遍历
6.6 字典
在Java中存在Map集合,而在Python中存在字典,他和Java中的Map集合是类似的,都是Key:Value结构
-
字典的定义:
# 字典字面量 {"username": "张三", "age": 21, "score": 66.5} # 定义字典 dicts = {"username": "张三", "age": 21, "score": 66.5} print(dicts) # {'username': '张三', 'age': 21, 'score': 66.5} print(dicts["username"]) # 张三 # 定义空字典 dicts = {} dicts = dict() print(dicts) # {}
注意事项:①键值对的Key和Value可以是任意类型(Key不可为字典)②字典内Key不允许重复,重复添加等同于覆盖原有数据字典③不可用下标索引,而是通过Key检索Value
-
字典相关操作
-
新增元素
dicts = {"username": "张三", "age": 21, "score": 66.5} dicts["gender"] = "男" print(dicts) # {'username': '张三', 'age': 21, 'score': 66.5, 'gender': '男'}
-
更新元素
dicts = {"username": "张三", "age": 21, "score": 66.5} dicts["username"] = "李四" print(dicts) # {'username': '李四', 'age': 21, 'score': 66.5}
-
删除元素
dicts = {"username": "张三", "age": 21, "score": 66.5} print(dicts.pop("score")) # 66.5 print(dicts) # {'username': '张三', 'age': 21}
-
清空字典
dicts = {"username": "张三", "age": 21, "score": 66.5} dicts.clear() print(dicts) # {}
-
获取字典全部的key
dicts = {"username": "张三", "age": 21, "score": 66.5} keys = dicts.keys() print(keys) # dict_keys(['username', 'age', 'score'])
-
遍历字典
dicts = {"username": "张三", "age": 21, "score": 66.5} keys = dicts.keys() for key in keys: print(f"key={key};value={dicts[key]}") """" 结果: key=username;value=张三 key=age;value=21 key=score;value=66.5 """
注意:字典无法使用while进行遍历,因为字典无法通过索引获取元素
-
字典学习总结
- 一个字典可以容纳多个数据
- 一个字典可以容纳不同类型的元素,键值对的Key和Value可以是任意类型(Key不可为字典)
- 字典不支持索引,无法通过索引获取字典中的元素,不属于序列故无法进行切片
- 集合不允许重复的key(重复会覆盖),但允许重复的Value
- 集合中的中的数据能够可以进行修改(增加,删除,更新)的
- 仅支持for循环遍历
6.7 数据容器之间的转换
函数 | 功能 |
---|---|
list(数据容器) | 将给定数据容器转换成列表 |
tuple(数据容器) | 将给定数据容器转换成元组 |
str(数据容器) | 将给定数据容器转换成字符串 |
set(数据容器) | 将给定数据容器转换成集合 |
备注:str()
函数能将其它任何类型的数据转换成字符串,详情见前面
-
测试
list
函数:number = 3.14 strs = "abc123" lists = [1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}] tuples = (1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}) # sets = {1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}} # TypeError sets = {1, 2.2, "a"} dicts = {"int": 1, "float": 2.2, "str": "a", "list": ["list"], "tuple": ("tuple",), "set": {"set"}, "dict": {"dict": "dict"}} # 将其它类型转换成列表 # result_list = list(number) # 无法将非数据容器转发成列表,强制转换报TypeError # 将字符串转成列表 result_list = list(strs) print(result_list) # 将列表转成列表 result_list = list(lists) print(result_list) # 将元组转成列表 result_list = list(tuples) print(result_list) # 将集合转成列表 result_list = list(sets) print(result_list) # 将字典转成列表 result_list = list(dicts) print(result_list)
-
测试
tuple
函数:number = 3.14 strs = "abc123" lists = [1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}] tuples = (1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}) # sets = {1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}} # TypeError sets = {1, 2.2, "a"} dicts = {"int": 1, "float": 2.2, "str": "a", "list": ["list"], "tuple": ("tuple",), "set": {"set"}, "dict": {"dict": "dict"}} # 将其它类型转换成元组 # result_tuple = tuple(number) # 无法将非数据容器转发成元组,强制转换报TypeError # 将字符串转成元组 result_tuple = tuple(strs) print(result_tuple) # 将列表转成元组 result_tuple = tuple(tuples) print(result_tuple) # 将元组转成元组 result_tuple = tuple(tuples) print(result_tuple) # 将集合转成元组 result_tuple = tuple(sets) print(result_tuple) # 将字典转成元组 result_tuple = tuple(dicts) print(result_tuple)
-
测试
str
函数:number = 3.14 strs = "abc123" lists = [1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}] tuples = (1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}) # sets = {1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}} # TypeError sets = {1, 2.2, "a"} dicts = {"int": 1, "float": 2.2, "str": "a", "list": ["list"], "tuple": ("tuple",), "set": {"set"}, "dict": {"dict": "dict"}} # 将其它类型转换成字符串 # 将非数据容器类型(float)转换成字符串 result_str = str(number) print(result_str) # 将字符串转成字符串 result_str = str(strs) print(result_str) # 将列表转成字符串 result_str = str(tuples) print(result_str) # 将元组转成字符串 result_str = str(tuples) print(result_str) # 将集合转成字符串 result_str = str(sets) print(result_str) # 将字典转成字符串 result_str = str(dicts) print(result_str)
-
测试
set
函数:number = 3.14 strs = "abc123" lists = [1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}] tuples = (1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}) # sets = {1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}} # TypeError sets = {1, 2.2, "a"} dicts = {"int": 1, "float": 2.2, "str": "a", "list": ["list"], "tuple": ("tuple",), "set": {"set"}, "dict": {"dict": "dict"}} # 将其它类型转换成集合 # 将非数据容器类型转换成集合 # result_set = set(number) # TypeError # 将字符串转成集合 result_set = set(strs) print(result_set) # 将列表转成集合 # result_set = set(lists) # TypeError # 将元组转成集合 # result_set = set(tuples) # TypeError # 将集合转成集合 result_set = set(sets) print(result_set) # 将字典转成集合 result_set = set(dicts) print(result_set)
注意:集合不可嵌套列表、元组、集合、字典,
set()
可以直接将字符串、字典和集合转换成集合,但是只能将没有嵌套列表、元组、字典的列表、元组、字典转换成集合
6.8 数据容器总结
数据容器可以从以下视角进行简单的分类:
- 是否支持下标索引
- 支持:列表、元组、字符串(序列类型)
- 不支持:集合、字典(非序列类型)
- 是否支持重复元素
- 支持:列表、元组、字符串(序列类型)
- 不支持:集合、字典(非序列类型)
- 是否可以修改
- 支持:列表、集合(增加,删除,无法进行更新)、字典
- 不支持:元组、字符串
注意:集合无法嵌套列表、元组、集合,编译时不报错,运行时会报TypeError
,所以说集合的元素类型并不是任意的!
-
数据容器的通用功能
-
在遍历上:
5类数据容器都支持for循环遍历。列表、元组、字符串还支持while循环,而集合、字典不支持(无法通过索引获取元素)
-
在操作上:
函数 功能 max(容器)
获取容器内最大元素 min(容器)
获取容器内最小的元素 len(容器)
获取容器中元素的个数 sorted(容器, [reverse=True])
得到一个排好序的列表(降序) 5类数据容器都支持
max()、min()、len()、list
注意:
- 在使用
max
函数、min
和sorted
函数时,一定要保障数据元组中的元素类型是一致的,否则会报TypeError len
函数计算的当前容器子元素的数量,并不会统计子元素中子元素的数量
字符串的比较规则:①单个字符直接比较ASCII码②如果是字符串,两个字符串字符串从头到尾一位位进行比较,一旦有一位大,后面就无需进行比较
-
测试
max
和min
函数:number = 3.14 strs = "abc123" lists = [1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}] tuples = (1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}) # sets = {1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}} # TypeError sets = {1, 2.2, "a"} dicts = {"int": 1, "float": 2.2, "str": "a", "list": ["list"], "tuple": ("tuple",), "set": {"set"},"dict": {"dict": "dict"}} # 测试max函数 # print(max(number)) # TypeError print(max(strs)) # c # print(max(lists)) # TypeError # print(max(tuples)) # TypeError # print(max(sets)) # TypeError print(max(dicts)) # tuple # 测试min函数 print(min(strs)) # 1 print(min(dicts)) # dict
-
测试
sorted
函数:number = 3.14 strs = "abc123" lists = [1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}] tuples = (1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}) # sets = {1, 2.2, "a", ["list"], ("tuple",), {"set"}, {"dict": "dict"}} # TypeError sets = {1, 2.2, "a"} dicts = {"int": 1, "float": 2.2, "str": "a", "list": ["list"], "tuple": ("tuple",), "set": {"set"}, "dict": {"dict": "dict"}} # print(sorted(number)) # TypeError print(sorted(strs)) # print(sorted(lists)) # TypeError # print(sorted(tuples)) # TypeError # print(sorted(sets)) # TypeError print(sorted(dicts)) # 注意:sorted并不会改变原有的数据容器,得到的是一个有序的列表
- 在使用
-
7、文件操作
7.1 基本概念
-
什么是编码?
编码就是一种规则集合,记录了内容和二进制间进行相互转换的逻辑。编码有许多中,我们最常用的是UTF-8编码
-
为什么要编码?
计算机只认识0和1,所以需要将内容翻译成0和1才能保存在计算机中。同时也需要编码, 将计算机保存的0和1,反向翻译回可以识别的内容
-
什么是文件?
文件是以硬盘为载体存储在计算机上的信息集合。一篇文章、一段视频、一个可执行程序,都可以被保存为一个文件,并赋予一个文件名。操作系统以文件为单位管理磁盘中的数据。一般来说,文件可分为文本文件、视频文件、音频文件、图像文件、可执行文件等多种类别
-
为什么需要文件?
内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、U 盘等设备。为了便于数据的管理和检索,引入了“文件”的概念,目的是为了更好管理和区别这些保存在硬盘、光盘、U盘等设备的数据(可以类比文件和我们的名字,它们的作用是类似的,名字也是为了区别每个人的)
-
文件的操作:打开文件、关闭文件、读文件、写文件等操作
7.2 文件的基本操作
7.2.1 打开文件
-
打开文件:
open
open()
函数用于打开一个文件,并返回文件对象,在对文件进行处理过程都需要使用到这个函数,如果该文件无法被打开,会抛出OSError
# open函数的常用形式 open(file, mode='r') # open函数的完整形式 open(file, mode='r', buffering=-1, encoding=None, errors=None, newline=None, closefd=True, opener=None)
-
file:文件路径(相对路径或绝对路径),是字符串类型(必须)
-
mode:设置打开文件的模式(文件的访问模式),常见的有:写入模式(x)、只读模式(r)、追加模式(a)……(可选)
详情请参考知识拓展
-
buffering:设置缓冲
-
encoding:设置文件编码,一般使用UTF-8
-
error:设置报错级别
-
newline:区分换行符
-
closefd:传入的file参数类型
-
opener:设置自定义开启器,开启器的返回值必须是一个打开的文件描述符
示例:
test.txt:
abc123我爱中国
test.py:
f = open('test.txt', 'r', encoding='utf-8') strs = f.read() print(strs) # abc123我爱中国 f.close() # 如果不调用close,同时程序没有停止运行,那么这个文件将一直被Python程序占用,十分浪费资源
注意:open函数encoding参数默认是gbk,gbk和utf-8在对于英文和阿拉伯数字都是采用ASCII编码,所以当test.txt中没有中文是,可以直接使用open函数默认的编码,但是当有中文时,由于utf-8和gbk编码在对于中文编码采用的编码格式是不一致的,在PyCharm中文件编码默认是采用utf-8,所以此时如果过test.txt中存在中文时,一定要设置ecoding=‘utf-8’,否则报
UnicodeDecodeError: 'gbk' codec can't decode byte 0xad in position 14: illegal multibyte sequence
详情可以参考这篇文章
-
知识拓展
mode的取值:
模式 描述 t 文本模式 (默认) x 写模式,新建一个文件,如果该文件已存在则会报错 b 二进制模式 + 打开一个文件进行更新(可读可写) U 通用换行模式(不推荐) r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式 rb 以二进制格式打开一个文件用于只读。文件指针将会放在文件的开头。这是默认模式。一般用于非文本文件如图片等 r+ 打开一个文件用于读写。文件指针将会放在文件的开头 rb+ 以二进制格式打开一个文件用于读写。文件指针将会放在文件的开头。一般用于非文本文件如图片等。 w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件 wb 以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等 w+ 打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件 wb+ 以二进制格式打开一个文件用于读写。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等 a 打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入 ab 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。也就是说,新的内容将会被写入到已有内容之后。如果该文件不存在,创建新文件进行写入 a+ 打开一个文件用于读写。如果该文件已存在,文件指针将会放在文件的结尾。文件打开时会是追加模式。如果该文件不存在,创建新文件用于读写 ab+ 以二进制格式打开一个文件用于追加。如果该文件已存在,文件指针将会放在文件的结尾。如果该文件不存在,创建新文件用于读写
7.2.2 读文件
-
读文件:
-
read
:按字节读取文件fileObject.read(num);
num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num(默认是-1),那么就表示读取文件中所有的数据
-
readline
:一次读取文件的一行内容fileObject.readline();
-
readlines
:按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素fileObject.readlines();
示例:
# 测试read() f = open('test.txt', 'r', encoding='utf-8') strs = f.read() print(strs) # abc123我爱中国 f.close() # 测试read(num) f = open('test.txt', 'r', encoding='utf-8') strs = f.read(8) # abc123我爱 print(strs) f.close() # 测试readline() f = open('test.txt', 'r', encoding='utf-8') strs = f.readline() print(strs) # abc123我爱 f.close() # 可以使用for in一行一行读取文件中的数据 f = open('test.txt', 'r', encoding='utf-8') for line in f: print(line) # abc123我爱 f.close() # 测试readlines() f = open('test.txt', 'r', encoding='utf-8') strs = f.readlines() print(strs) # ['abc123我爱中国'] f.close() # 使用with open(使用这种方式会自动调用close方法,无需手动关流) with open('test.txt', 'r', encoding='utf-8') as f: strs = f.read() print(strs) # abc123我爱中国
-
7.2.3 写和追加文件
-
写文件:
write
fileObject.write(str)
str表示要写入文件中的字符串
注意:
- 文件写入时机:直接调用write,内容并未真正写入文件,而是会积攒在程序的内存中,称之为缓冲区当调用flush的时候,内容会真正写入文件这样做是避免频繁的操作硬盘,导致效率下降(攒一堆,一次性写磁盘)
- 使用w模式写入文件:文件如果不存在,使用”w”模式,会创建新文件文件;文件如果存在,使用”w”模式,会将原有内容清空
示例:
f = open('test.txt', 'w', encoding='utf-8') f.write('你好,Alis!') # f.flush() # 调用close方法时,会自动刷新,所以无需使用flush方法也是可以的 f.close()
-
追加文件
追加文件并没有什么特别的方法,这是将open的mode参数取值为a就可以实现追加文件了
f = open('test.txt', 'a', encoding='utf-8') f.write('你好,Tom!') # f.flush() # 调用close方法时,会自动刷新,所以无需使用flush方法也是可以的 f.close()
注意:使用a模式打开文件,如果文件不存在会创建文件;如果文件存在会在最后,追加写入文件
8、异常、模块、包
8.1 异常
-
什么是异常?
当检测到一个错误时,Python解释器就无法继续执行了,反而出现了一些错误的提示,这就是所谓的“异常”, 也就是我们常说的BUG(其实前面经常出现的
TypeError
就算一个异常) -
什么是bug?
bug就是指异常的意思,因为历史因为小虫子导致计算机失灵的案例,所以延续至今,bug就代表软件出现错误
-
什么是异常捕获?
世界上没有完美的程序,任何程序在运行的过程中,都有可能出现:异常,也就是出现bug导致程序无法完美运行下去。我们要做的,不是力求程序完美运行。而是在力所能及的范围内,对可能出现的bug,进行提前准备、提前处理。这种行为我们称之为:异常处理(捕获异常)
-
为什么需要捕获异常?
程序一旦发生异常,就会导致程序终止,而异常捕获就是对异常进行提醒,然后程序仍然能够正常运行。
捕获异常的作用在于:提前假设某处会出现异常,做好提前准备,当真的出现异常的时候,可以有后续手段
-
基本语法:
# 常用形式 try: <语句> # 可能发生错误的代码 except: <语句> # 如果出现异常执行的代码 # 完整形式 try: <语句> #运行别的代码 except <异常名字>: <语句> #如果在try部份引发了'name'异常 except <名字>,<数据>: <语句> #如果引发了'name'异常,获得附加的数据 else: <语句> #如果没有异常发生
注意:①如果尝试执行的代码的异常类型和要捕获的异常类型不一致,则无法捕获异常。② 一般try下方只放一行尝试执行的代码
各种异常类,请参考菜鸟教程文档
示例:
# 捕获一个异常 try: print(1/0) except ZeroDivisionError: print("除数不能为0!") # 捕获多个异常 try: print(1/0) except (NameError, ZeroDivisionError): print("未声明/初始化对象 (没有属性)或者除数为0") # 捕获异常并输出异常信息 try: print(num) except (NameError, ZeroDivisionError) as e: print(e) # name 'num' is not defined # 捕获所有异常 try: print(1 / 0) print(name) except Exception as e: print(e) # division by zero(执行第一个异常程序就终止了) # eles关键字,不发生异常时执行else代码 try: print(1 / 0) print(name) except Exception as e: print(e) # division by zero(执行第一个异常程序就终止了) else: print("不存在任何异常") # finally关键字,无论是否存在异常与否,最终都会执行finally代码(一般用来关流) try: f = open('test.txt', 'r') except Exception as e: f = open('test.txt', 'w') else: print('没有异常,真开心') finally: f.close()
-
异常的传递性
当函数func01中发生了异常,但是并没有捕获处理这个异常,异常就会传递到它的上一层调用者,依次类推,层层传递。总的来讲异常是自底向上传递的(在Java中也是存在异常的传递性),当所有函数都没有捕获处理异常时,程序就会终止并报错!
见图解:
8.2 模块
-
什么是模块?
Python 模块(Module),是一个 Python 文件,以 .py 结尾。模块能定义函数,类和变量,模块里也能包含可执行的代码
-
为什么需要模块?(模块的作用是什么?)
Python中有很多各种不同的模块, 每一个模块都可以帮助我们快速的实现一些功能,比如实现和时间相关的功能就可以使用time模块我们可以认为一个模块就是一个工具包,每一个工具包中都有各种不同的工具供我们使用进而实现各种不同的功能。
简而言之:模块就是一个Python文件,里面有类、函数、变量等,我们可以拿过来用(导入模块去使用)
-
模块的定义
[from模块名] import [模块 | 类 | 变量 | 函数 | *] [as别名] # 常见的组合形式 import 模块名 from 模块名 import 类、变量、方法等 from 模块名 import * import 模块名 as 别名 from 模块名 import 功能名 as 别名
示例:
# 测试1:通过import导入模块,可以使用`模块名.`的方式使用模块中所有的功能 import time # 等价于from time import *(使用*不需要`模块名.`进行调用,而是直接使用) print("你好") time.sleep(3) # 程序睡眠3秒钟 print("你好") # 测试2:通过from导入模块,只是用模块中部分功能 from time import sleep # 只能使用sleep函数 print("你好") sleep(3) # 程序睡眠3秒钟 print("你好") # 测试3:起别名 import time as t print("你好") t.sleep(3) # 程序睡眠3秒钟 print("你好")
-
自定义模块
每个Python文件都可以作为一个模块,模块的名字就是文件的名字. 也就是说自定义模块名要符合标识符命名规则
注意:当导入多个模块,且多个模块中存在同名的函数,则后面导入模块的函数会覆盖前面导入模块的函数
-
模块中常用的变量
-
__main__
:# 只有当程序是直接执行的才会进入if内部,如果是被导入的,则if无法进入 if __name__ == '__main__': print(add(1, 2))
-
__all__
:如果一个模块文件中有__all__
变量,当使用from xxx import *
导入时,只能导入这个列表中的元素注意:
__all__
针对的是 ’ from … import * ‘, 这种方式对 ‘ import xxx ’ 这种方式无效test_module.py:
__all__ = ['add'] # 外部导入该模块,只能调用add函数 def add(x, y): return x + y def sub(x, y): return x - y
test.py:
from test_module import * print(add(2, 2)) # 4 print(sub(2, 2)) # NameError
-
8.3 包
-
什么是包?
-
从物理上看,包就是一个文件夹,在该文件夹下包含了一个
__init__.py
文件,该文件夹可用于包含多个模块文件备注:
__init__.py
用于控制包的导入行为,在新建包时会自动生成该文件,通过这个文件来表示该文件夹是Python的包,而非普通的文件夹 -
从逻辑上看,包的本质依然是模块
-
-
为什么需要包?
在一个开发一个Python的项目时,经常需要导入各个模块,导入的多了,会导致项目很欢乱,而包就是为了统一管理这些模块的,包的本质还是模块,只是这个模块中含有多个模块
-
模块的导入
-
方式一:
test_module1.py:
def add_info(): print("求两数之和")
test_module2.py:
def sub_info(): print("求两数之差")
test.py:
import my_package.test_module1 import my_package.test_module2 my_package.test_module1.add_info() my_package.test_module2.sub_info()
-
方式二:必须在
__init__.py
文件中添加__all__ = []
,控制允许导入的模块列表注意:
__all__
针对的是 ’ from … import * ‘, 这种方式对 ‘ import xxx ’ 这种方式无效
-
-
第三方包
通过前面的学习,我们知道,包可以包含一堆的Python模块,而每个模块又内含许多的功能。所以,我们可以认为:一个包,就是一堆同类型功能的集合体。包的作用是管理模块,提高代码的复用率
在Python程序的生态中,有许多非常多的第三方包(非Python官方),可以极大的帮助我们提高开发效率,如:
- 科学计算中常用的:numpy包
- 数据分析中常用的:pandas包
- 大数据计算中常用的:pyspark、apache-flink包
- 图形可视化常用的:matplotlib、pyecharts包
- 人工智能常用的:tensorflow
还有很多好用的第三方包……这些第三方的包,极大的丰富了Python的生态,提高了开发效率,但由于在Python中并没有内置这些第三方包(其实也不可能内置,因为内置后会大大提高Python的内存,并且内置了这些包,我们可能也用不上),所以需要我们下载安装第三方包,然后就可以从本地导入到我们的项目中进行使用了
示例:(进入cmd窗口,通过指令进行安装)
-
方式一:通过指令安装第三方包
# 安装命令:pip install 包名称 pip install selenium # 安装selenium测试工具
由于pip是连接的国外的网站进行包的下载,所以有的时候下载速度会很慢。我们可以通过如下命令,让其连接国内的镜像网站进行包的安装:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称 # 清华:https://pypi.tuna.tsinghua.edu.cn/simple # 阿里云:http://mirrors.aliyun.com/pypi/simple # 中国科技大学:https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple # 豆瓣:http://pypi.doubanio.com/simple
-
方式二:通过PyCharm下载第三方包
由于PyCharm默认也是使用外国的链接下载第三方包的,所以导致下载速度也会比较慢,所以需要我们配置一下镜像网站:
9、面对对象
在学习Java时已经学过一遍面对对象了,这里就不在赘述了,主要是着重学习一下在Python中面对对象的不同之处,本人还是以Java学习为主
9.1 基础概念
-
什么是面对对象?
是将现实问题构建关系,然后抽象成 类 (class) ,给类定义属性和方法后,再将类实例化成 实例 (instance) ,通过访问实例的属性和调用方法来进行使用。
用我自己的话来讲:面对对象就是将变量和函数进行封装(类中的变量和函数一般叫类属性和类方法),然后通过类的实例化对象来调用类中的属性和方法。
-
面对对象的三大特征:封装、继承、多态。
-
类中定义的属性(变量),我们称之为:成员变量
-
类中定义的行为(函数),我们称之为:成员方法
成员方法和普通函数的区别在于,成员方法必须添加一个名为
self
的形参,self关键字是成员方法定义的时候,必须填写的。self的作用:当我们使用类对象调用方法的是,self会自动被python传入在方法内部,想要访问类的成员变量,必须使用self
注意:self关键字,尽管在参数列表中,但是传参的时候可以忽略它
-
私有成员:定义私有成员的方式非常简单,只需要:
- 私有成员变量:变量名以
__
开头(2个下划线) - 私有成员方法:方法名以
__
开头(2个下划线)
注意:类对象无法访问私有成员,但是在类的内部可以调用私有成员
- 私有成员变量:变量名以
-
类的定义:
class Student: # 成员变量(写了构造方法,这些可以省略) name = None age = None # 私有成员变量 __gender = None # 构造方法 def __init__(self, name, age, gender): self.name = name self.age = age self.gender = gender # 成员方法 def say_hello(self, hobby): print(f"hello,我是{self.name},今年{self.age}。我的爱好是{hobby}") student = Student('ikun', 21, '男') student.say_hello('唱、跳、rap')
-
内置方法:
__init__
构造方法,是Python类内置的方法之一。这些内置的类方法,各自有各自特殊的功能,这些内置方法我们称之为:魔术方法=
-
__str__
:对应Java中的toString方法,在输出类对象时,可以直接输出内容而不是地址def __str__(self): return f"Student类对象.name={self.name},age={self.age},gender={self.gender}" student = Student('ikun', 21, '男') student.say_hello('唱、跳、rap') # print(student) # 不<__main__.Student object at 0x000001DD909C3B10> print(student) # Student类对象.name=ikun,age=21,gender=男
-
__it__
:直接对2个对象进行比较是不可以的,但是在类中实现__it__
方法,即可同时完成小于符号和大于符号2种比较,就不需要实现__gt__
方法了 -
__le__
:小于等于比较符号方法,同理实现了__le__
方法,就不需要实现__ge__
方法了 -
__eq__
:对应的Java中的equals方法,对象直接是直接比较地址的,需要实现这个方法后,才能比较
-
9.2 继承
在Python中继承可以分为:单继承和多继承
-
继承的作用:提高代码的复用性、拓展性、可维护性。将从父类那里继承(复制)来成员变量和成员方法(不含私有)
注意:多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右,左>右)为优先级。即:先继承的保留,后继承的被覆盖
class Phone1:
version = 1
def net(self):
print("2G网络")
class Phone2:
version = 2
def net(self):
print("3G网络")
class Phone3:
version = 2
def net(self):
print("4G网络")
class MyPhone(Phone1, Phone2, Phone3):
pass
phone = MyPhone()
print(phone.version) # 1
pass
是占位语句,用来保证函数(方法)或类定义的完整性,表示无内容,空的意思
- 调用父类同名的成员:
- 方式一: 使用成员变量:父类名.成员变量 ; 使用成员方法:父类名.成员方法(self)
- 方式二: 使用成员变量:super().成员变量 ; 使用成员方法:super().成员方法()
- 成员方法的复写: 对应Java中的重写,要保证参数和方法名一致,一旦复写父类成员,那么类对象调用成员的时候,就会调用复写后的新成员
9.3 类型注解
Python 是动态语言,其显著特点是在声明变量时,你不需要显式声明它的类型。而Java是静态语言,在Java开发时,使用对象调用内部的成员方法,时对于参数IDE会有提示,可以告诉开发者这个函数有有哪些参数,这个参数是什么类型,但是在使用PyCharm开发Python时,没有任何提示!这显然会很大程度降低开发效率,于是Python在3.5版本的时候就引入了类型注解,以方便静态类型检查工具,IDE等第三方工具
-
什么是类型注解?
在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)
-
类型注解的作用是什么?
-
帮助第三方IDE工具(如PyCharm)对代码进行类型推断,协助做代码
-
提示帮助开发者自身对变量进行类型注释
-
-
类型注解的分类:变量类型、函数类型、Union类型
-
变量类型:
-
方式一:
变量: 类型
# 基础数据类型的类型注解 name: str = '张三' # 类对象类型的类型注解 student: Student = Student() # 容器数据类型的类型注解 lists: list = [1, 2, 3] dicts: dict = {'username': '张三'} # 也可以使用更加详细的类型注解 lists: list[int] = [1, 2, 3] dicts: dict[str, str] = {'username': '张三'}
-
方式二:
# type: 类型
# 基础数据类型的类型注解 name = '张三' # type: int # 类对象类型的类型注解 student = Student() # type: Student # 容器数据类型的类型注解 lists = [1, 2, 3] # type: list dicts = {'username': '张三'} # type: dict # 也可以使用更加详细的类型注解 lists = [1, 2, 3] # type: list[int] dicts = {'username': '张三'} # type: dict[str, str]
注意:类型注解仅仅是提示性的,不是决定性的,并不会真正的对类型做验证和判断,仅仅是让IDE拥有提示功能,协助开发
-
-
函数类型:
函数类型的注解又有形参注解和返回值注解
-
形参注解:
def 函数名(形参名1: 类型, 形参名2: 类型, ...): 函数体 # 示例: def add(x: int, y: int): return x+y
-
返回值注解:
def 函数名(形参名1: 类型, 形参名2: 类型, ...) -> 返回值类型: 函数体 # 示例: def add(x: int, y: int) -> int: return x+y
-
-
Union注解:
Union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用
Union注解的作用:让多种类型可以混合使用
dicts: dict[str, str] = {'username': '张三'} 面对以下这种定义就是不规范的,虽然不报错: dicts: dict[str, str] = {'username': '张三', 'password': 123} 面对上面这种情况,我们可以使用Union注解,如下所示: dicts: dict[str, Union[str, int]]
-
9.4 多态
多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。
多态的本质是向下转型,即父类类兼容子类型,这在每个面对对象的语言中都是存在的
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("汪汪汪。。。")
class Cat(Animal):
def speak(self):
print("喵猫喵。。。")
def animal_speak(animal: Animal):
animal.speak()
animal_speak(Dog()) # 汪汪汪。。。
animal_speak(Cat()) # 喵猫喵。。。
对于Animal类的那种写法,我们称之为接口类,也称抽象类,对于Animal的那种方法我们称之为抽象方法
- 抽象类:含有抽象方法的类称之为抽象类
- 抽象方法:方法体是空实现的(pass)称之为抽象方法
参考资料:
- 2022新版黑马程序员python教程:有一说一,黑马yyds(有一定编程基础的,推荐直接看课件)
- Python 基础教程 | 菜鸟教程 (runoob.com):菜鸟必备网站
- Python基础教程,Python入门教程(非常详细) (biancheng.net):C语言中文网保姆级教程,很适合小白
表达式就是一个具有明确结果的代码语句,本质是一个值 ↩︎