1、python基础语法
1.1 字面量
1.掌握字面量的含义
2.了解常见的字面量类型
3.基于print语句完成各类字面量的输出
字面量: 在代码中,被写下来的固定的值,称之为字面量
常用的值类型
Python中常用的有6种值(数据)的类型
类型 | 描述 | 说明 |
---|---|---|
数字(Number) | 支持 •整数(int) •浮点数(float) •复数(complex) •布尔(bool) | 整数(int),如:10、-10 |
浮点数(float),如:13.14、-13.14 | ||
复数(complex),如:4+3j,以j结尾表示复数 | ||
布尔(bool)表达现实生活中的逻辑,即真和假,True表示真,False表示假。 True本质上是一个数字记作1,False记作0 | ||
字符串(String) | 描述文本的一种数据类型 | 字符串(string)由任意数量的字符组成 |
列表(List) | 有序的可变序列 | Python中使用最频繁的数据类型,可有序记录一堆数据 |
元组(Tuple) | 有序的不可变序列 | 可有序记录一堆不可变的Python数据集合 |
集合(Set) | 无序不重复集合 | 可无序记录一堆不重复的Python数据集合 |
字典(Dictionary) | 无序Key-Value集合 | 可无序记录一堆Key-Value型的Python数据集合 |
字符串
1.2 注释
注释:在程序代码中对程序代码进行解释说明的文字。
作用:注释不是程序,不能被执行,只是对程序代码进行解释说明,让别人可以看懂程序代码的作用,能够大大增强程序的可读性。
-
单行注释:以 #开头,#右边 的所有文字当作说明,而不是真正要执行的程序,起辅助说明作用
# 当行注释
-
多行注释: 以 一对三个双引号 引起来,来解释说明一段代码的作用使用方法
""" 多行注释 """
-
注释的作用是?
注释是代码中的解释型语句,用来对代码内容进行注解
注释不是代码,不会被程序执行
-
单行注释如何定义?
通过 # 号定义,在#号右侧的所有内容均作为注释
建议在#号和注释内容之间,间隔一个空格
单行注释一般用于对一行或一小部分代码进行解释
-
多行注释如何定义?
通过一对三个引号来定义(“”“注释内容”“”),引号内部均是注释,可以换行
多行注释一般对:Python文件、类或方法进行解释
1.3 数据类型
# 我们可以通过type(被查看类型的数据)
type(111) # <class 'int'>
type(11.11) # <class 'float'>
type("字符串") #<class 'str'>
name ="str"
type(name)
1.4 数据类型转换
语句(函数) | 说明 |
---|---|
int(x) | 将x转换为一个整数 |
float(x) | 将x转换为一个浮点数 |
str(x) | 将对象 x 转换为字符串 |
-
任何类型,都可以通过str(),转换成字符串
-
字符串内必须真的是数字,才可以将字符串转换为数字
1.5 标识符
标识符是用户在编程的时候所使用的一系列名字,用于给变量、类、方法等命名
标识符命名中,只允许出现
- 英文
- 中文
- 数字
- 下划线(_)
其余任何内容都不被允许
不推荐使用中文,数字不可以开头 \textcolor{red}{不推荐使用中文,数字不可以开头} 不推荐使用中文,数字不可以开头
标识符大小写敏感,不可以使用关键字
命名规范:
- 见明知意
- 下划线命名法
- 英文字母全小写
1.6 运算符
运算符 | 描述 | 实例 |
---|---|---|
+ | 加 | 两个对象相加 a + b 输出结果 30 |
- | 减 | 得到负数或是一个数减去另一个数 a - b 输出结果 -10 |
* | 乘 | 两个数相乘或是返回一个被重复若干次的字符串 a * b 输出结果 200 |
/ | 除 | b / a 输出结果 2 |
// | 取整除 | 返回商的整数部分 9//2 输出结果 4 , 9.0//2.0 输出结果 4.0 |
% | 取余 | 返回除法的余数 b % a 输出结果 0 |
** | 指数 | a**b 为10的20次方, 输出结果 100000000000000000000 |
赋值运算符 | 描述 | 实例 |
---|---|---|
= | 赋值运算符 | 把 = 号右边的结果 赋给 左边的变量,如 num = 1 + 2 * 3,结果num的值为7 |
复合赋值运算符 | 描述 | 实例 |
---|---|---|
+= | 加法赋值运算符 | c += a 等效于 c = c + a |
-= | 减法赋值运算符 | c -= a 等效于 c = c - a |
*= | 乘法赋值运算符 | c *= a 等效于 c = c * a |
/= | 除法赋值运算符 | c /= a 等效于 c = c / a |
%= | 取模赋值运算符 | c %= a 等效于 c = c % a |
**= | 幂赋值运算符 | c ** = a 等效于 c = c ** a |
//= | 取整除赋值运算符 | c //= a 等效于 c = c // a |
"""
演示Python中的各类运算符
"""
# 算术(数学)运算符
print("1 + 1 = ", 1 + 1) # 1 + 1 = 2
print("2 - 1 = ", 2 - 1) # 2 - 1 = 1
print("3 * 3 = ", 3 * 3) # 3 * 3 = 9
print("5 / 2 = ", 5 / 2) # 5 / 2 = 2.5
print("11 // 2 = ", 11 // 2) # 11 // 2 = 5
print("9 % 2 = ", 9 % 2) # 9 % 2 = 1
print("2 ** 2 = ", 2 ** 2) # 2 ** 2 = 4
# 赋值运算符
num = 1 + 2 * 3
# 复合赋值运算符
# +=
num = 1
num += 1 # num = num + 1
print("num += 1: ", num) # num += 1: 2
num -= 1
print("num -= 1: ", num) # num -= 1: 1
num *= 4
print("num *= 4: ", num) # num *= 4: 4
num /= 2
print("num /= 2: ", num) # num /= 2: 2.0
num = 3
num %= 2
print("num %= 2: ", num) # num %= 2: 1
num **= 2
print("num **=2: ", num) # num **=2: 1
num = 9
num //= 2
print("num //= 2:", num) # num //= 2: 4
1.7 字符串扩展
字符串类型的不同定义方式
字符串有3种不同的定义方式:
双引号定义法 “字符串”
双引号定义法 ‘字符串’
双引号定义法 “”“字符串”“”
字符串引号嵌套
-
单引号定义法,可以内含双引号
-
双引号定义法,可以内含单引号
-
可以使用转移字符(\)来将引号解除效用,变成普通字符串
字符串拼接
print("字符串" + "拼接")
name = "zhangsan"
print("hello " + name)
# 不可以直接拼接数字类型的
"""
字符串无法和非字符串变量进行拼接
因为类型不一致,无法接上
就像接力赛一样,不是队友,不能接力的哦
"""
"""
其中的,%s
% 表示:我要占位
s 表示:将变量变成字符串放入占位的地方
"""
name = "zhangsan"
my_name ="lisi"
my_age = 17.9
message ="hello %s my name is %s" % (name,my_name)
print(message)
# hello zhangsan my name is lisi
message2 =f"hello {name} my name is {my_name} my age is {my_age}"
age =" %f " % my_age
print(message2)# hello zhangsan my name is lisi my age is 17.9
print(age) # 17.900000
print("%.2f" % my_age)# 17.90
print("%6.2f" % my_age)# 这里会有空格17.90
print("2 * 2 的结果是:%d" % (2 * 2) ) # 2 * 2 的结果是:4
print(f"2 * 2 的结果是:{2 * 2}" ) # 2 * 2 的结果是:4
print("字符串 占位符 %s " % type("占位符") ) # 字符串 占位符 <class 'str'>
格式符号 | 转化 |
---|---|
%s | 将内容转换成字符串,放入占位位置 |
%d | 将内容转换成整数,放入占位位置 |
%f | 将内容转换成浮点型,放入占位位置 (需要控制精度) |
数字精度控制
我们可以使用辅助符号"m.n"来控制数据的宽度和精度
-
m,控制宽度,要求是数字(很少使用),设置的宽度小于数字自身,不生效
-
.n,控制小数点精度,要求是数字,会进行小数的四舍五入
示例:
-
%5d:表示将整数的宽度控制在5位,如数字11,被设置为5d,就会变成:[空格][空格][空格]11,用三个空格补足宽度。
-
%5.2f:表示将宽度控制为5,将小数点精度设置为2
小数点和小数部分也算入宽度计算。如,对11.345设置了%7.2f 后,结果是:[空格][空格]11.35。2个空格补足宽度,小数部分限制2位精度后,四舍五入为 .35
-
%.2f:表示不限制宽度,只设置小数点精度为2,如11.345设置%.2f后,结果是11.35
字符串快速格式化
这种写法不做精度控制
也不理会类型
适用于快速格式化字符串
通过语法:f"内容{变量}"的格式来快速格式化
1.8 数据输入
在Python中,与之对应的还有一个input语句,用来获取键盘输入。
-
数据输出:print
-
数据输入:input
使用上也非常简单:
-
使用input()语句可以从键盘获取输入(都是字符串类型)
-
使用一个变量接收(存储)input语句获取的键盘输入数据即可
print("请输入指令:")
cmd = input()
print(cmd)
2、判断语句
2.1 布尔类型和比较运算符
比较运算符
运算符 | 描述 | 示例 |
---|---|---|
== | 判断内容是否相等,满足为True,不满足为False | 如a=3,b=3,则(a == b) 为 True |
!= | 判断内容是否不相等,满足为True,不满足为False | 如a=1,b=3,则(a != b) 为 True |
> | 判断运算符左侧内容是否大于右侧 满足为True,不满足为False | 如a=7,b=3,则(a > b) 为 True |
< | 判断运算符左侧内容是否小于右侧 满足为True,不满足为False | 如a=3,b=7,则(a < b) 为 True |
>= | 判断运算符左侧内容是否大于等于右侧 满足为True,不满足为False | 如a=3,b=3,则(a >= b) 为 True |
<= | 判断运算符左侧内容是否小于等于右侧 满足为True,不满足为False | 如a=3,b=3,则(a <= b) 为 True |
2.2 if语句的基本格式
# 定义变量
age = 30
# 进行判断
if age >= 18: # 最后已:结尾
print("我已经成年了") # 前面缩进四个空格,编辑器在:后直接回车即可
if语句的注意事项:
-
判断条件的结果一定要是布尔类型
-
不要忘记判断条件后的: 引号
-
归属于if语句的代码块,需在前方填充4个空格缩进
if else
if elif else
判断语句的嵌套
3、循环语句
"""
演示while循环基础练习题:求1-100的和
"""
sum = 0
i = 1
while i<=100:
sum += i
i += 1
print(f"1-100累加的和是:{sum}")
-
while的条件需得到布尔类型,True表示继续循环,False表示结束循环
-
需要设置循环终止的条件,如i += 1配合 i < 100,就能确保100次后停止,否则将无限循环
-
空格缩进和if判断一样,都需要设置
"""
演示使用while的嵌套循环
打印输出九九乘法表
"""
# 定义外层循环的控制变量
i = 1
while i <= 9:
# 定义内层循环的控制变量
j = 1
while j <= i:
# 内层循环的print语句,不要换行,通过\t制表符进行对齐
print(f"{j} * {i} = {j * i}\t", end='')
j += 1
i += 1
print() # print空内容,就是输出一个换行
# print语句中,加上 end=’’ 即可输出不换行了
print("hello", end = '')
print("world", end = '')
- continue的作用是:
中断所在循环的当次执行,直接进入下一次
- break的作用是:
直接结束所在的循环
- 注意事项:
-
continue和break,在for和while循环中作用一致
-
在嵌套循环中,只能作用在所在的循环上,无法对上层循环起作用
4、python函数
函数:是 组织好的,可重复使用的 \textcolor{Green}{组织好的,可重复使用的} 组织好的,可重复使用的,用来实现特定功能的代码段。
def 函数(参数...):
"""
函数说明 注释
"""
函数体
return 返回值
None类型
Python中有一个特殊的字面量:None,其类型是:<class ‘NoneType’>
无返回值的函数,实际上就是返回了:None这个字面量
None表示:空的、无实际意义的意思
函数返回的None,就表示,这个函数没有返回什么有意义的内容。
也就是返回了空的意思
应用场景
None作为一个特殊的字面量,用于表示:空、无意义,其有非常多的应用场景。
-
用在函数无返回值上
-
用在if判断上
-
在if判断中,None等同于False
-
一般用于在函数中主动返回None,配合if判断做相关处理
def check_age(age): if age> 18: return "SUCCESS" return None if not check_age(5): print("少儿不宜")
-
-
用于声明无内容的变量上
- 定义变量,但暂时不需要变量有具体值,可以用None来代替 name=None
局部变量
所谓局部变量是定义在函数体内部的变量,即只在函数体内部生效
局部变量的作用:在函数体内部,临时保存数据,即当函数调用完成后,则销毁局部变量
def test():
n = 10 #就是局部变量
print(n)
全局变量
所谓全局变量,指的是在函数体内、外都能生效的变量
n = 100 # 全局变量
global关键字
使用 g l o b a l 关键字可以在函数内部声明变量为全局变量 \textcolor{red}{使用 global关键字 可以在函数内部声明变量为全局变量} 使用global关键字可以在函数内部声明变量为全局变量
num = 100
def testA():
print(num)
def testB():
global num
num = 200
print(num)
testA() #100
testB() #200
testA() #200 如果没有global 则为100
5、python数据容器
5.1 数据容器入门
数据容器,就是为了批量存储或批量使用多份数据
Python中的数据容器:
一种可以容纳多份数据的数据类型,容纳的每一份数据称之为1个元素
每一个元素,可以是任意类型的数据,如字符串、数字、布尔等。
数据容器根据特点的不同,如:
-
是否支持重复元素
-
是否可以修改
-
是否有序,等
分为5类,分别是:
列表(list)、元组(tuple)、字符串(str)、集合(set)、字典(dict)
5.2 数据容器:list列表
列表的定义
# 字面量
[元素1,元素2,元素3,....]
# 定义变量
变量名称 = [元素1,元素2,元素3,....]
# 定义空列表
变量名称 = []
变量名称 = list()
列表内的每一个数据,称之为元素
-
以 [] 作为标识
-
列表内每一个元素之间用, 逗号隔开
列表的下标
list = [1,2,3,4]
print(list[0])
print(list[1])
print(list[2])
print(list[3])
# 反向获取
"""
或者,可以反向索引,也就是从后向前:从-1开始,依次递减(-1、-2、-3......)
"""
list = [1,2,3]
print(list[-1]) # 3
print(list[-2]) # 2
print(list[-3]) # 1
# 嵌套
list = [[1,2,3],[4,5,6]]
print(list[0][1]) # 2
注意: \textcolor{red}{注意:} 注意:
-
列表的下标索引是什么?
列表的每一个元素,都有编号称之为下标索引
从前向后的方向,编号从0开始递增
从后向前的方向,编号从-1开始递减
-
如何通过下标索引取出对应位置的元素呢?
列表[下标],即可取出
-
下标索引的注意事项:
要注意下标索引的取值范围,超出范围无法取出元素,并且会报错
列表的常用操作(方法)
-
查找某元素的下标
功能:查找指定元素在列表的下标,如果找不到,报错ValueError
语法:列表.index(元素)
index就是列表对象(变量)内置的方法(函数)
list = ['a','b','c'] print(list.index('b')) # 1 print(list.index(4)) # ValueError: 4 is not in list
列表的修改功能(方法)
-
修改特定位置(索引)的元素值:
语法:列表[下标] = 值
可以使用如上语法,直接对指定下标(正向、反向下标均可)的值进行:重新赋值(修改)
my_list = ['a', 'b', 'c']
my_list[1] = 'vvv'
my_list[-1] = 'aaa'
print(my_list) # ['a', 'vvv', 'aaa']
-
插入元素
语法:列表.insert(下标, 元素),在指定的下标位置,插入指定的元素
my_list = ['a', 'b', 'c'] my_list.insert(1, 'bbb') print(my_list) # ['a', 'bbb', 'b', 'c']
-
追加元素:
- 语法:列表.append(元素),将指定元素,追加到列表的尾部
- 语法:列表.extend(其它数据容器),将其它数据容器的内容取出,依次追加到列表尾部
my_list = ['a', 'b', 'c'] my_list.append("d") my_list.append(["aa", 2]) print(my_list) # ['a', 'b', 'c', 'd', ['aa', 2]] my_list = ['a', 'b', 'c'] my_list.extend("vvv") my_list.extend(["aaa", 222]) print(my_list) # ['a', 'b', 'c', 'v', 'v', 'v', 'aaa', 222]
-
刪除元素
-
语法1: del 列表[下标]
-
语法2:列表.pop(下标)
-
删除某元素在列表中的第一个匹配项 —> 列表.remove(元素)
-
清空列表内容,—> 语法:列表.clear()
-
统计某元素在列表内的数量 —> 语法:列表.count(元素)
-
统计列表内,有多少元素 语法:len(列表) 可以得到一个int数字,表示列表内的元素数量
-
my_list = ['a', 'b', 'c'] my_list.pop(2) del my_list[0] print(my_list) # ['b'] # 删除某元素在列表中的第一个匹配项 my_list = ['a', 'b', 'c'] my_list.remove('b') print(my_list) # ['a', 'c'] # 清空列表内容,语法:列表.clear() my_list = ['a', 'b', 'c'] my_list.clear() print(my_list) # [] # 统计某元素在列表内的数量 my_list = ['a','a','a', 'b', 'c'] print(my_list.count('a')) # 3 # 统计列表内,有多少元素 my_list = ['a','a','a', 'b', 'c'] print(len(my_list)) # 5
-
编号 | 使用方式 | 作用 |
---|---|---|
1 | 列表.append(元素) | 向列表中追加一个元素 |
2 | 列表.extend(容器) | 将数据容器的内容依次取出,追加到列表尾部 |
3 | 列表.insert(下标, 元素) | 在指定下标处,插入指定的元素 |
4 | del 列表[下标] | 删除列表指定下标元素 |
5 | 列表.pop(下标) | 删除列表指定下标元素 |
6 | 列表.remove(元素) | 从前向后,删除此元素第一个匹配项 |
7 | 列表.clear() | 清空列表 |
8 | 列表.count(元素) | 统计此元素在列表中出现的次数 |
9 | 列表.index(元素) | 查找指定元素在列表的下标 找不到报错ValueError |
10 | len(列表) | 统计容器内有多少元素 |
5.3 list遍历
my_list = ['a','a','a', 'b', 'c']
# while
i = 0
while i < len(my_list):
print(my_list[i])
i += 1
# for
for i in my_list:
print(i)
while循环和for循环,都是循环语句,但细节不同:
-
在循环控制上:
while循环可以自定循环条件,并自行控制
for循环不可以自定循环条件,只可以一个个从容器内取出数据
-
在无限循环上:
while循环*可以通过条件控制做到无限循环
for循环理论上不可以,因为被遍历的容器容量不是无限的
-
在使用场景上:
while循环适用于任何想要循环的场景
for循环适用于,遍历数据容器的场景或简单的固定次数循环场景
5.4 tuple(元组)
元组同列表一样,都是可以封装多个、不同类型的元素在内。
但最大的不同点在于: 元组一旦定义完成,就不可修改 \textcolor{red}{元组一旦定义完成,就不可修改} 元组一旦定义完成,就不可修改
定义元组
元组定义:定义元组使用小括号,且使用逗号隔开各个数据,数据可以是不同的数据类型。
# 定义元组字面量
(元素,元素,....)
# 定义元组变量
变量名称 = (元素,元素,....)
t1 = (1 , 'hello', True)
t2 = ('Hello',) # 后面的逗号一定要有,否则不是元组类型
# 定义空元组
变量名称 = ()
变量名称 = tuple()
#嵌套
t1 = ((1,2,3),(4,5,6))
print(t1[0][0]) # 1
元组的相关操作
编号 | 方法 | 作用 |
---|---|---|
1 | index() | 查找某个数据,如果数据存在返回对应的下标,否则报错 |
2 | count() | 统计某个数据在当前元组出现的次数 |
3 | len(元组) | 统计元组内的元素个数 |
元组由于不可修改的特性,所以其操作方法非常少。
t1 = (1, 2, 3)
# 尝试元组的修改
# t1[0] = 5 # 'tuple' object does not support item assignment
# 可以修改元组内的list的内容(修改元素、增加、删除、反转等)
t2 = (1, 2, 3, [4, 5, 6])
t2[3][1] = 44
print(t2)
# 不可以替换list为其它list或其它类型
# t2[3] = [3] # tuple' object does not support item assignment
print(t2)
元组的遍历
t1 = (1, 2, 3)
i = 0
while i < len(t1):
print(t1[i])
i += 1
for i in t1:
print(i)
元组的特点
经过上述对元组的学习,可以总结出列表有如下特点:
-
可以容纳多个数据
-
可以容纳不同类型的数据(混装)
-
数据是有序存储的(下标索引)
-
允许重复数据存在
-
不可以修改(增加或删除元素等)
-
支持for循环
多数特性和list一致,不同点在于不可修改的特性。
5.5 str(字符串)
和其它容器如:列表、元组一样,字符串也可以通过下标进行访问
-
从前向后,下标从0开始
-
从后向前,下标从-1开始
同元组一样,字符串是一个:无法修改的数据容器。
所以:
-
修改指定下标的字符 (如:字符串[0] = “a”)
-
移除特定下标的字符 (如:del 字符串[0]、字符串.remove()、字符串.pop()等)
-
追加字符等 (如:字符串.append())
均无法完成。如果必须要做,只能得到一个新的字符串,旧的字符串是无法修改
字符串的常用操作
-
查找特定字符串的下标索引值
-
字符串的替换
语法:字符串.replace(字符串1,字符串2)
功能:将字符串内的全部:字符串1,替换为字符串2
注意:不是修改字符串本身,而是得到了一个新字符串哦
-
字符串的分割
语法:字符串.split(分隔符字符串)
功能:按照指定的分隔符字符串,将字符串划分为多个字符串,并存入列表对象中
注意:字符串本身不变,而是得到了一个列表对象
-
字符串的规整操作(去前后空格)
语法:字符串.strip()
-
字符串的规整操作(去前后指定字符串)
语法:字符串.strip(字符串)
-
统计字符串中某字符串的出现次数
语法:字符串.count(字符串)
-
统计字符串的长度
语法:len(字符串)
数字(1、2、3…)
字母(abcd、ABCD等)
符号(空格、!、@、#、$等)
中文
均算作1个字符
编号 | 操作 | 说明 |
---|---|---|
1 | 字符串[下标] | 根据下标索引取出特定位置字符 |
2 | 字符串.index(字符串) | 查找给定字符的第一个匹配项的下标 |
3 | 字符串.replace(字符串1, 字符串2) | 将字符串内的全部字符串1,替换为字符串2 不会修改原字符串,而是得到一个新的 |
4 | 字符串.split(字符串) | 按照给定字符串,对字符串进行分隔 不会修改原字符串,而是得到一个新的列表 |
5 | 字符串.strip() 字符串.strip(字符串) | 移除首尾的空格和换行符或指定字符串 |
6 | 字符串.count(字符串) | 统计字符串内某字符串的出现次数 |
7 | len(字符串) | 统计字符串的字符个数 |
字符串的遍历 同列表 元组一样
字符串的特点
作为数据容器,字符串有如下特点:
-
只可以存储字符串
-
长度任意(取决于内存大小)
-
支持下标索引
-
允许重复字符串存在
-
不可以修改(增加或删除元素等)
-
支持for循环
基本和列表、元组相同
不同与列表和元组的在于:字符串容器可以容纳的类型是单一的,只能是字符串类型。
不同于列表,相同于元组的在于:字符串不可修改
name = "hello world"
print(name.index("d"))
print(name.index("world"))
# 不是修改字符串本身,而是得到了一个新字符串哦
name = "斗破苍穹 消炎"
new_name = name.replace("消炎", "肖炎")
print(name) # 斗破苍穹 消炎
print(new_name) # 斗破苍穹 肖炎
hobby = "足球 篮球 羽毛球"
new_hobby = hobby.split(" ")
print(hobby) # 足球 篮球 羽毛球
print(new_hobby) # ['足球', '篮球', '羽毛球']
# 字符串的规整操作(去前后空格)
name = " 我喜欢跳绳 "
new_name = name.strip()
print(name) # 我喜欢跳绳
print(new_name) # 我喜欢跳绳
# 去掉指定字符串
name = "1test"
print(name) # 1test
print(name.strip("1")) # test
print(name.strip("test")) # 1
# 统计字符串中某字符串的出现次数
name = "aaabbb"
print(name.count("a")) # 3
# 统计字符串长度
print(len(name)) # 6
# 遍历
i = 0
while i < len(name):
print(name[i])
i += 1
for i in name:
print(i)
5.6 数据容器的切片
序列是指:内容连续、有序,可使用下标索引的一类数据容器
列表、元组、字符串,均可以可以视为序列。
元素1 | 元素2 | 元素3 | 元素… | 元素n |
---|---|---|---|---|
下标0 | 1 | 2 | … | n-1 |
元素1 | 元素2 | 元素3 | 元素… | 元素n |
---|---|---|---|---|
下标-n | -(n-1) | -(n-2) | … | -1 |
序列的典型特征就是:有序并可用下标索引,字符串、元组、列表均满足这个要求
序列的常用操作 - 切片
序列支持切片,即:列表、元组、字符串,均支持进行切片操作
切片:从一个序列中,取出一个子序列
语法:序列[起始下标:结束下标:步长]
表示从序列中,从指定位置开始,依次取出元素,到指定位置结束,得到一个新序列
-
起始下标表示从何处开始,可以留空,留空视作从头开始
-
结束下标(不含)表示何处结束,可以留空,留空视作截取到结尾
-
步长表示,依次取元素的间隔
-
步长1表示,一个个取元素
-
步长2表示,每次跳过1个元素取
-
步长N表示,每次跳过N-1个元素取
-
步长为负数表示,反向取(注意,起始下标和结束下标也要反向标记)
注意,此操作不会影响序列本身,而是会得到一个新的序列(列表、元组、字符串)
my_list = [1, 2, 3, 4,5]
new_list = my_list[1:4]
print(new_list) # [2, 3, 4]
print(my_list[:]) # [1, 2, 3, 4, 5]
print(my_list[::2]) #[1, 3, 5]
print(my_list[:4:2]) #[1, 3]
my_tuple = (1,2,3,4,5)
print(my_tuple[:]) # (1, 2, 3, 4, 5)
print(my_tuple[::2]) # (1, 3, 5)
my_str = "12345"
print(my_str[::3]) # 14
print(my_str[::-1]) # 54321
5.7 数据容器 - 集合set
集合的定义
# 定义集合的字面量
{元素,元素,....元素}
# 定义集合的变量
变量名称 = {元素,元素,....元素}
# 空集合
变量名称 = set()
-
列表使用:[]
-
元组使用:()
-
字符串使用:“”
-
集合使用:{}
首先,因为集合是无序的,所以 集合不支持:下标索引访问 \textcolor{red}{集合不支持:下标索引访问} 集合不支持:下标索引访问
但是集合和列表一样,是允许修改的,所以我们来看看集合的修改方法。
-
添加新元素
语法:集合.add(元素)。将指定元素,添加到集合内
结果:集合本身被修改,添加了新元素
-
移除元素
语法:集合.remove(元素),将指定元素,从集合内移除
结果:集合本身被修改,移除了元素
-
从集合中随机取出元素
语法:集合.pop(),功能,从集合中 随机 \textcolor{red}{随机} 随机取出一个元素
结果:会得到一个元素的结果。同时集合本身被修改,元素被移除
-
清空集合
语法:集合.clear(),功能,清空集合
结果:集合本身被清空
-
取出2个集合的差集
语法:集合1.difference(集合2),功能:取出集合1和集合2的差集(集合1有而集合2没有的)
结果:得到一个新集合,集合1和集合2不变
-
消除2个集合的差集
语法:集合1.difference_update(集合2)
功能:对比集合1和集合2,在集合1内,删除和集合2相同的元素。
结果:集合1被修改,集合2不变
-
2个集合合并
语法:集合1.union(集合2)
功能:将集合1和集合2组合成新集合
结果:得到新集合,集合1和集合2不变
-
查看集合的元素数量
语法:len(集合)
功能:统计集合内有多少元素
结果:得到一个整数结果
编号 | 操作 | 说明 |
---|---|---|
1 | 集合.add(元素) | 集合内添加一个元素 |
2 | 集合.remove(元素) | 移除集合内指定的元素 |
3 | 集合.pop() | 从集合中随机取出一个元素 |
4 | 集合.clear() | 将集合清空 |
5 | 集合1.difference(集合2) | 得到一个新集合,内含2个集合的差集 原有的2个集合内容不变 |
6 | 集合1.difference_update(集合2) | 在集合1中,删除集合2中存在的元素 集合1被修改,集合2不变 |
7 | 集合1.union(集合2) | 得到1个新集合,内含2个集合的全部元素 原有的2个集合内容不变 |
8 | len(集合) | 得到一个整数,记录了集合的元素数量 |
my_set = {"hello", "world"}
# 添加元素
my_set.add("python")
print(my_set) # {'python', 'hello', 'world'}
# 移除元素
my_set.remove("hello")
print(my_set) # {'python', 'world'}
print("-------pop_set pop 随机 每次结果有可能不一致--------")
pop_set = {"hello", "world", "python"}
ele = pop_set.pop()
print(pop_set) # {'world', 'python'}
print(ele) # {'world', 'python'}
print("---------清空集合---------")
pop_set.clear()
print(pop_set) # set()
"""
集合 差集
得到一个新的集合,原来的集合不变
"""
set1 = {1, 2, 3}
set2 = {3, 4, 5}
diff_set = set1.difference(set2)
print(set1) # {1, 2, 3}
print(set2) # {3, 4, 5}
print(diff_set) # {1, 2}
print("------消除两个集合的差集--------")
set1 = {1, 2, 3}
set2 = {3, 4, 5}
diff_set = set1.difference_update(set2)
print(set1) #{1, 2}
print(set2) # {3, 4, 5}
print(diff_set) # None 无返回
print("------消除两个集合的合并--------")
set1 = {1, 2, 3}
set2 = {3, 4, 5}
union_set = set1.union(set2)
print(set1) #{1, 2, 3}
print(set2) # {3, 4, 5}
print(union_set) # {1, 2, 3, 4, 5}
print("集合元素的数量" + str(len(set1)))
print("------遍历-------")
for i in set1:
print(i)
集合特点
-
可以容纳多个数据
-
可以容纳不同类型的数据(混装)
-
数据是无序存储的(不支持下标索引)
-
不允许重复数据存在
-
可以修改(增加或删除元素等)
-
支持for循环
-
不可以使用while循环,因为不支持下标索引
5.8 数据容器-dict(字典、映射)
就是map
字典的嵌套
字典的Key和Value可以是任意数据类型(Key不可为字典)
那么,就表明,字典是可以嵌套的
字典的常用操作
-
新增元素
语法:字典[Key] = Value,结果:字典被修改,新增了元素
-
更新元素
语法:字典[Key] = Value,结果:字典被修改,元素被更新
注意:字典Key不可以重复,所以对已存在的Key执行上述操作,就是更新Value值
-
删除元素
语法:字典.pop(Key),结果:获得指定Key的Value,同时字典被修改,指定Key的数据被删除
-
删除元素
语法:字典.pop(Key),结果:获得指定Key的Value,同时字典被修改,指定Key的数据被删除
-
获取全部的key
语法:字典.keys(),结果:得到字典中的全部Key
-
遍历字典
语法:for key in 字典.keys()
-
计算字典内的全部元素(键值对)数量
语法:len(字典)
结果:得到一个整数,表示字典内元素(键值对)的数量
编号 | 操作 | 说明 |
---|---|---|
1 | 字典[Key] | 获取指定Key对应的Value值 |
2 | 字典[Key] = Value | 添加或更新键值对 |
3 | 字典.pop(Key) | 取出Key对应的Value并在字典内删除此Key的键值对 |
4 | 字典.clear() | 清空字典 |
5 | 字典.keys() | 获取字典的全部Key,可用于for循环遍历字典 |
6 | len(字典) | 计算字典内的元素数量 |
总结字典有如下特点:
-
可以容纳多个数据
-
可以容纳不同类型的数据
-
每一份数据是KeyValue键值对
-
可以通过Key获取到Value,Key不可重复(重复会覆盖)
-
不支持下标索引
-
可以修改(增加或删除更新元素等)
-
支持for循环,不支持while循环
# dict map
my_dict = {'key1': 'value1', 'key2': 'v2'}
print(my_dict.get("key1")) # value1
print(my_dict["key1"]) # value1
stu_score = {
"zhangsan": {"Chinese": 100, "Maths": 10, "English": 1},
"lisi": {"Chinese": 2, "Maths": 3, "English": 4}
}
print(stu_score['zhangsan']['Maths']) # 10
print(stu_score['lisi']) # {'Chinese': 2, 'Maths': 3, 'English': 4}
# 获取一个不存在的 key
print(stu_score.get('lssi')) # None
# print(stu_score['lssi']) # KeyError: 'lssi'
# 新增元素
stars = {
"周杰伦": 88,
"张杰":99
}
stars['张国荣'] = 100
print(stars) # {'周杰伦': 88, '张杰': 99, '张国荣': 100}
stars['张杰'] = 1
print(stars) # {'周杰伦': 88, '张杰': 1, '张国荣': 100}
# 获取全部的key
keys = stars.keys()
print(keys) # dict_keys(['周杰伦', '张杰', '张国荣'])
"""
明星:周杰伦,粉丝数:88
明星:张杰,粉丝数:1
明星:张国荣,粉丝数:100
"""
for key in stars.keys():
print(f"明星:{key},粉丝数:{stars[key]}")
# 计算字典内的全部元素(键值对)数量
print(len(stars)) # 3
# 删除元素
stars.pop('张国荣')
print(stars) # {'周杰伦': 88, '张杰': 1}
# 清空
stars.clear()
print(stars) # {}
数据容器的对比
数据容器可以从以下视角进行简单的分类:
-
是否支持下标索引
- 支持:列表、元组、字符串 - 序列类型
- 不支持:集合、字典 - 非序列类型
-
是否支持重复元素:
- 支持:列表、元组、字符串 - 序列类型
- 不支持:集合、字典 - 非序列类型
-
是否可以修改
- 支持:列表、集合、字典
- 不支持:元组、字符串
各种维度对比 | 列表 | 元组 | 字符串 | 集合 | 字典 |
---|---|---|---|---|---|
元素数量 | 支持多个 | 支持多个 | 支持多个 | 支持多个 | 支持多个 |
元素类型 | 任意 | 任意 | 仅字符 | 任意 | Key:Value Key:除字典外任意类型 Value:任意类型 |
下标索引 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
重复元素 | 支持 | 支持 | 支持 | 不支持 | 不支持 |
可修改性 | 支持 | 不支持 | 不支持 | 支持 | 支持 |
数据有序 | 是 | 是 | 是 | 否 | 否 |
使用场景 | 可修改、可重复的一批数据记录场景 | 不可修改、可重复的一批数据记录场景 | 一串字符的记录场景 | 不可重复的数据记录场景 | 以Key检索Value的数据记录场景 |
5.9 数据容器的通用作用
功能 | 描述 |
---|---|
通用for循环 | 遍历容器(字典是遍历key) |
max | 容器内最大元素 |
min() | 容器内最小元素 |
len() | 容器元素个数 |
list() | 转换为列表 |
tuple() | 转换为元组 |
str() | 转换为字符串 |
set() | 转换为集合 |
sorted(序列, [reverse=True]) | 排序,reverse=True表示降序 得到一个排好序的列表 |
6、函数进阶
6.1 函数多返回值
# 函数多返回值
def test():
return 1,2
x,y = test()
print(x) # 1
print(y) # 2
按照返回值的顺序,写对应顺序的多个变量接收即可
变量之间用逗号隔开
支持不同类型的数据return
6.2 函数的多种传参
- 位置参数
- 调用函数时根据函数定义的参数位置来传递参数
- 传递的参数和定义的参数的顺序及个数必须一致
- 关键字参数
- 函数调用时通过“键=值”形式传递参数
- 可以让函数更加清晰、容易使用,同时也清除了参数的顺序需求
- 可以让函数更加清晰、容易使用,同时也清除了参数的顺序需求
- 缺省参数
- 缺省参数也叫默认参数,用于定义函数,为参数提供默认值,调用函数时可不传该默认参数的值(注意:所有位置参数必须出现在默认参数前,包括函数定义和调用).
- 当调用函数时没有传递参数, 就会使用默认是用缺省参数对应的值
- 函数调用时,如果为缺省参数传值则修改默认参数值, 否则使用这个默认值
- 不定长参数
- 不定长参数也叫可变参数. 用于不确定调用的时候会传递多少个参数(不传参也可以)的场景
- 当调用函数时不确定参数个数时, 可以使用不定长参数
- 位置传递
- 关键字传递
# 位置参数
# 传递的参数和定义的参数的顺序及个数必须一致
def h(name, age, gender):
print(f'名字:{name},年龄:{age},性别:{gender}')
h("lisi",1,'女')
# 关键字参数
h(name="老六",age=18,gender='男')
# 可以不固定顺讯
h(age=60,name="老六",gender="男")
# 可以和位置参数混合使用,位置参数必须在前,且匹配参数顺序
h('小明',age=60,gender="男")
def h(name, age, gender="男"):
print(f'名字:{name},年龄:{age},性别:{gender}')
h('小明',age=11)
h('小明',age=11,gender="女")
"""
传进的所有参数都会被args变量收集,
它会根据传进参数的位置合并为一个元组(tuple),args是元组类型,这就是位置传递
"""
def user_info(*agrs):
print(agrs)
user_info("TOM") # ('TOM',)
user_info("TOM",2) # ('TOM', 2)
# 参数是“键=值”形式的形式的情况下, 所有的“键=值”都会被 kwargs 接受, 同时会根据“键=值”组成字典
# 注意是两个**
def user_info2(**kwargs):
print(kwargs)
user_info2(name='TOM',age=10,gender="女")
6.3 函数作为参数
def test_fun(compute):
result = compute(1,2)
return result
def compute(x,y):
return x + y
print(test_fun(compute)) # 3
def test_fun(compute):
result = compute(1,2)
return result
def compute(x,y):
return x * y
print(test_fun(compute)) # 2
def test_fun(compute):
result = compute(1,2)
return result
def compute(x,y):
return x - y
print(test_fun(compute)) # -1
函数compute,作为参数,传入了test_func函数中使用。
-
test_func需要一个函数作为参数传入,这个函数需要接收2个数字进行计算,计算逻辑由这个被传入函数决定
-
compute函数接收2个数字对其进行计算,compute函数作为参数,传递给了test_func函数使用
-
最终,在test_func函数内部,由传入的compute函数,完成了对数字的计算操作
所以,这是一种,计算逻辑的传递,而非数据的传递。
就像上述代码那样,不仅仅是相加,相见、相除、等任何逻辑都可以自行定义并作为函数传入
6.3.1 lambda匿名函数
函数的定义中
-
def关键字,可以定义带有名称的函数
-
lambda关键字,可以定义匿名函数(无名称)
有名称的函数,可以基于名称重复使用。
无名称的匿名函数,只可临时使用一次。
匿名函数定义语法:
lambda 传入参数: 函数体(一行代码)
-
lambda 是关键字,表示定义匿名函数
-
传入参数表示匿名函数的形式参数,如:x, y 表示接收2个形式参数
-
函数体,就是函数的执行逻辑,要注意:只能写一行,无法写多行代码
def test_fun(conpute):
return conpute(1,2)
print(test_fun(lambda x, y: x + y)) # 3
print(test_fun(lambda x,y: x * y)) #2
7、文件操作
文件操作包括 打开、关闭、读、写等操作
文件操作的步骤
- 打开文件
- 读写文件(这个也可以没有)
- 关闭文件
-
Mode常用的三种访问模式
模式 描述 r 以只读方式打开文件。文件的指针将会放在文件的开头。这是默认模式。 w 打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,原有内容会被删除。 如果该文件不存在,创建新文件。 a 打开一个文件用于追加。如果该文件已存在,新的内容将会被写入到已有内容之后。 如果该文件不存在,创建新文件进行写入。 -
open()函数
-
""" name:是要打开的目标文件名的字符串(可以包含文件所在的具体路径)。 mode:设置打开文件的模式(访问模式):只读、写入、追加等。 encoding:编码格式(推荐使用UTF-8) """ open(name,mode,encoding) f = open('python.txt', 'r', encoding=”UTF-8) # encoding的顺序不是第三位,所以不能用位置参数,用关键字参数直接指定 #注意:此时的`f`是`open`函数的文件对象,对象是Python中一种特殊的数据类型,拥有属性和方法,可以使用对象.属性或对象.方法对其进行访问,后续面向对象课程会给大家进行详细的介绍
-
-
read()方法
-
# num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据 文件对象.read(num)
-
readlines()方法 # readlines可以按照行的方式把整个文件中的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素 f = open('python.txt') content = f.readlines() # ['hello world\n', 'abcdefg\n', 'aaa\n', 'bbb\n', 'ccc'] print(content) # 关闭文件 f.close()
-
readline()方法:一次读取一行内容
f = open('python.txt') content = f.readline() print(f'第一行:{content}') content = f.readline() print(f'第二行:{content}') # 关闭文件 f.close()
-
for循环读取文件行
for line in open("python.txt", "r"): print(line) # 每一个line临时变量,就记录了文件的一行数据
-
close() 关闭文件对象
f = open("python.txt", "r") f.close() # 最后通过close,关闭文件对象,也就是关闭对文件的占用 # 如果不调用close,同时程序没有停止运行,那么这个文件将一直被Python程序占用。
-
with open 语法
with open("python.txt", "r") as f: f.readlines() # 通过在with open的语句块中对文件进行操作 # 可以在操作完成后自动关闭close文件,避免遗忘掉close方法
-
操作 | 功能 |
---|---|
文件对象 = open(file, mode, encoding) | 打开文件获得文件对象 |
文件对象.read(num) | 读取指定长度字节 不指定num读取文件全部 |
文件对象.readline() | 读取一行 |
文件对象.readlines() | 读取全部行,得到列表 |
for line in 文件对象 | for循环文件行,一次循环得到一行数据 |
文件对象.close() | 关闭文件对象 |
with open() as f | 通过with open语法打开文件,可以自动关闭 |
文件的写入、文件的追加
f= open("python.txt",'w')
# 文件写入
"""
文件如果不存在,使用”w”模式,会创建新文件
文件如果存在,使用”w”模式,会将原有内容清空
"""
f.write("hello1 python")
# 调用flush 内容会真正的写入文件
f.flush()
# 文件追加
# 文件存在追加
f = open("python.txt",'a')
f.write("\nhello apend pyhon")
f.flush()
# 文件不存在 创建文件
f = open("python1.txt",'a')
f.write("hello apend pyhon")
f.flush()
8、异常模块与包
8.1 异常的捕获
try:
可能发生错误的代码
except:
如果出现异常执行的代码
try:
f = open("p.txt",'r')
except:
f = open("p.txt",'w')
try:
print(name)
except NameError as e:
print("变量名称尚未定义")
print(e) # name 'name' is not defined
# 捕获多个异常
try:
print(1/0)
except (NameError,ZeroDivisionError) as e:
print('ZeroDivisionError错误...')
print(e)
# 捕获所有异常
try:
print(name)
except Exception as e:
print(e)
# else 没有异常执行
try:
print(1)
except Exception as e:
print(e)
else:
print("我是 else ,没有异常 执行此处")
try:
f = open('test.txt', 'r')
except Exception as e:
print("异常执行")
f = open('test.txt', 'w')
else:
print('没有异常,真开心')
finally:
print('有没有异常,都执行')
f.close()
异常的传递性
当函数func01中发生异常, 并且没有捕获处理这个异常的时候, 异常会传递到函数func02, 当func02也没有捕获处理这个异常的时候main函数会捕获这个异常, 这就是异常的传递性.
def fun01():
print("fun01开始")
num = 1 / 0
print("fun01结束")
def fun02():
print("fun02开始")
fun01()
print("fun02结束")
def main():
try:
fun02()
except Exception as e :
print(e)
main()
#当所有函数都没有捕获异常的时候, 程序就会报错
8.2 模块:
Python 模块(Module),是一个 Python 文件,以 .py 结尾. 模块能定义函数,类和变量,模块里也能包含可执行的代码.
模块的作用: python中有很多各种不同的模块, 每一个模块都可以帮助我们快速的实现一些功能, 比如实现和时间相关的功能就可以使用time模块,我们可以认为一个模块就是一个工具包, 每一个工具包中都有各种不同的工具供我们使用进而实现各种不同的功能.
大白话:模块就是一个Python文件,里面有类、函数、变量等,我们可以
拿过来用(导入模块去使用)
模块导入方式
[form 模块名] import [模块 | 类 |变量 |函数 | * ] [as 别名]
常用的组合形式:
-
import 模块名
import 模块名 import 模块名1,模块名2 模块名.功能名()
# 导入时间模块 import time print("开始" + str(time.time())) # 休眠1s time.sleep(1) print("结束" + str(time.time()))
-
from 模块名 import 类、变量、方法等
# 导入时间模块的 sleep 方法 from time import sleep print("start") sleep(1) print("end")
-
from 模块名 import *
# 导入时间模块中所有的方法 from time import * print("开始") # 让程序睡眠1秒(阻塞) sleep(1) print("结束")
-
import 模块名 as 别名
# 别名 import time as tt tt.sleep(1) print("end") from time import sleep as s s(1) print("e")
-
from 模块名 import 功能名 as 别名
自定义模块
每个Python文件都可以作为一个模块,模块的名字就是文件的名字. 也就是说自定义模块名必须要符合标识符命名规则
新建test_module.py
def test(a,b):
return a+ b
新建test_module2.py
def test(a,b):
return a+ b
def test3(a,b):
return a- b
if __name__ == '__main__':
print(test(1, 4))
在test.py中引用test_module.py
import test_module
print(test_module.test(1, 2))
当导入多个模块的时候,且模块内有同名功能. 当调用这个同名功能的时候,调用到的是后面导入的模块的功能
# -1from test_module import test3
from test_module2 import test3
print(test3(2, 3)) # -1
如果一个模块文件中有**__all__
**变量,当使用from xxx import *
导入时,只能导入这个列表中的元素
test_module.py
def test(a,b):
return a+ b
def test3(a,b):
return a* b
__all__ = ["test"]
测试模块
每个Python文件都可以作为一个模块,模块的名字就是文件的名字. 也就是说自定义模块名必须要符合标识符命名规则
def test(a, b):
print(a + b)
test(1, 1)
from test_module import *
print(test(2, 3))# 执行使用test方法
问题:
此时,无论是当前文件,还是其他已经导入了该模块的文件,在运行的时候都会自动执行test
函数的调用
解决方案:
def test(a, b):
print(a + b)
# 只在当前文件中调用该函数,其他导入的文件内不符合该条件,则不执行test函数调用
if __name__ == '__main__':
test (1, 1)
8.3 包
从物理上看,包就是一个文件夹,在该文件夹下包含了一个 __init__.py
文件,该文件夹可用于包含多个模块文件
从逻辑上看,包的本质依然是模块
包的作用
当我们的模块文件越来越多时,包可以帮助我们管理这些模块, 包的作用就是包含多个模块,但包的本质依然是模块
基本步骤
[New] -> [Python Package]-> 输入包名 -> [OK] -> 新建功能模块(有联系的模块)
注意:新建包后,包内部会自动创建__init__.py
文件,这个文件控制着包的导入行为
在先新建test1.py
print(1)
def info_print1():
print("test1")
在先新建test2.py
print(2)
def test_print2():
print("test2")
__init__.py
暂时为空
import my_package.test1
import my_package.test2
my_package.test1.info_print1()
my_package.test2.test_print2()
在__init__.py
文件中利用__all__
=[]控制允许导入模块的列表
# __all__针对的是 ’ from ... import * ‘ 这种方式
# 对 ‘ import xxx ’ 这种方式无效 上面的可以继续使用
__all__ = ['test1'] # test1为模块名
from my_package import *
from my_package import test2
test1.info_print1()
# AttributeError: module 'my_package.test2' has no attribute 'info_print1'
# 即使制定了导入 也会异常
test2.info_print1()
8.4 第三方包
Python程序的生态中,有许多非常多的第三方包(非Python官方),可以极大的帮助我们提高开发效率,如:
-
科学计算中常用的:numpy包
-
数据分析中常用的:pandas包
-
大数据计算中常用的:pyspark、apache-flink包
-
图形可视化常用的:matplotlib、pyecharts
-
人工智能常用的:tensorflow
-
等
这些第三方的包,极大的丰富了Python的生态,提高了开发效率。
但是由于是第三方,所以Python没有内置,以我们需要安装它们才可以导入使用哦。
安装第三方包 - pip
第三方包的安装非常简单,我们只需要使用Python内置的pip程序即可。
打开我们许久未见的:命令提示符程序,在里面输入:
pip install 包名称
即可通过网络快速安装第三方包
PIP网络优化
由于pip是连接的国外的网站进行包的下载,所以有的时候会速度很慢。
我们可以通过如下命令,让其连接国内的网站进行包的安装:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple numpy
https://pypi.tuna.tsinghua.edu.cn/simple 是清华大学提供的一个网站,可供pip程序下载第三方包
9、面向对象
类的定义和使用
类的使用语法:
"""
class是关键字,表示要定义类了
类的属性,即定义在类中的变量(成员变量)
类的行为,即定义在类中的函数(成员方法)
"""
class 类名称:
类的属性
类的行为
# 创建类对象的语法:
队形 = 类名称()
成员变量、成员方法
class Student:
name = None
age = None
def say_hello(self):
print(f"hello,{self.name}")
"""
可以忽略self. :忽略self
"""
def ignore(self,msg):
print(f"可以忽略self. :{msg}")
student = Student()
student.name = 'test'
student.say_hello()
student.ignore("忽略self") # 可以忽略self. :忽略self
在类中定义成员方法和定义函数基本一致,但仍有细微区别:
可以看到,在方法定义的参数列表中,有一个:self关键字
self关键字是成员方法定义的时候,必须填写的。
-
它用来表示类对象自身的意思
-
当我们使用类对象调用方法的是,self会自动被python传入
-
在方法内部,想要访问类的成员变量,必须使用self
类只是一种程序内的**“设计图纸**”,需要基于图纸生产实体(对象),才能正常工作
这种套路,称之为:面向对象编程
构造方法
Python类可以使用:__init__()
方法,称之为构造方法。
可以实现:
-
在创建类对象(构造类)的时候,会自动执行。
-
在创建类对象(构造类)的时候,将传入参数自动传递给
__init__
方法使用。
class Student:
"""
属性可以省略
"""
name= None
age = None
gender = None
"""
构建类时传入的参数会自动提供给__init__方法
构建类的时候__init__方法会自动执行
"""
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
print("create object")
if __name__ == '__main__':
student = Student("lisi",17,'女')
print(f"名字:{student.name} 年龄:{student.age},性別:{student.gender}")
构造方法注意事项
-
重要的事情说三遍,构造方法名称:
__init__
__init__`` __init__
, 千万不要忘记init前后都有2个下划线 -
构造方法也是成员方法,不要忘记在参数列表中提供:self
-
在构造方法内定义成员变量,需要使用self关键字
- 变量是定义在构造方法内部,如果要成为成员变量,需要用self来表示
def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender print("create object")
魔术方法
变量是定义在构造方法内部,如果要成为成员变量,需要用self来表示
方法 | 功能 |
---|---|
__init__ | 构造方法,可用于创建类对象的时候设置初始化行为 |
__str__ | 用于实现类对象转字符串的行为 |
__lt__ | 用于2个类对象进行小于或大于比较 ` |
__le__ | 用于2个类对象进行小于等于或大于等于比较 |
__eq__ | 用于2个类对象进行相等比较 |
__str__
class Student:
"""
属性可以省略
"""
name= None
age = None
gender = None
"""
构建类时传入的参数会自动提供给__init__方法
构建类的时候__init__方法会自动执行
"""
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
print("create object")
if __name__ == '__main__':
student = Student("lisi",17,'女')
# 当类对象需要被转换为字符串之时,会输出如下结果(内存地址)
print(student) # <__main__.Student object at 0x0000017DF74976D0>
print(str(student)) # <__main__.Student object at 0x0000017DF74976D0>
# 内存地址没有多大作用,我们可以通过__str__方法,控制类转换为字符串的行为
在对象实现了str方法之后
class Student:
"""
属性可以省略
"""
name= None
age = None
gender = None
"""
构建类时传入的参数会自动提供给__init__方法
构建类的时候__init__方法会自动执行
"""
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
print("create object")
def __str__(self) -> str:
return f"学生姓名:{self.name} ,学生年龄:{self.age},学生性别:{self.gender}"
if __name__ == '__main__':
student = Student("lisi",17,'女')
print(student) # 学生姓名:lisi ,学生年龄:17,学生性别:女
print(str(student)) # 学生姓名:lisi ,学生年龄:17,学生性别:女
__lt__
小于符号比较方法
直接对2个对象进行比较是不可以的,但是在类中实现__lt__方法,即可同时完成:小于符号 和 大于符号 2种比较
class Student:
"""
属性可以省略
"""
name= None
age = None
gender = None
"""
构建类时传入的参数会自动提供给__init__方法
构建类的时候__init__方法会自动执行
"""
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
print("create object")
def __lt__(self, other):
return self.age < other.age
if __name__ == '__main__':
student1 = Student("lisi",17,'女')
student2 = Student("zhangsan",28,'男')
print(student1 > student2) # False
print(student1 < student2) # True
__eq__
,比较运算符实现方法
-
不实现__eq__方法,对象之间可以比较,但是是比较内存地址,也即是:不同对象==比较一定是False结果
class Student: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender if __name__ == '__main__': student1 = Student("lisi",17,'女') student2 = Student("zhangsan",17,'男') print(student1 == student2) # False
-
实现了__eq__方法,就可以按照自己的想法来决定2个对象是否相等了
class Student: def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def __eq__(self, o: object) -> bool: return self.age == o.age if __name__ == '__main__': student1 = Student("lisi",17,'女') student2 = Student("zhangsan",17,'男') print(student1 == student2) # True
面向对象的三大特性
面向对象编程,是许多编程语言都支持的一种编程思想。
简单理解是:基于模板(类)去创建实体(对象),使用对象完成功能开发。
面向对象包含3大主要特性:
-
封装
-
将现实世界中的事物的属性、行为封装到类中,描述为成员变量、成员方法从而完成程序对现实世界事物的描述
-
继承
-
单继承
-
多继承
-
多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。
即:先继承的保留,后继承的被覆盖
-
-
-
多态
私有成员
既然现实事物有不公开的属性和行为,那么作为现实事物在程序中映射的类,也应该支持。
类中提供了私有成员的形式来支持。
-
私有成员变量
-
私有成员方法
定义私有成员的方式非常简单,只需要:
-
私有成员变量:变量名以__开头(2个下划线)
-
私有成员方法:方法名以__开头(2个下划线)
即可完成私有成员的设置
"""
私有成员无法被类对象使用,但是可以被其它的成员使用
在类中提供仅供内部使用的属性和方法,而不对外开放(类对象无法使用)
"""
class Student:
__private_param = None
def __init__(self,name,age,gender):
self.name = name
self.age = age
self.gender = gender
self.__private_param = "私有"
def __prvate_method(self):
print("私有方法:" + self.__private_param)
def __str__(self) -> str:
self.__prvate_method()
return f"私有变量:{self.__private_param}"
if __name__ == '__main__':
student1 = Student("lisi",17,'女')
print(student1)
# student1.__private_param # 'Student' object has no attribute '__private_param
多个父类中,如果有同名的成员,那么默认以继承顺序(从左到右)为优先级。
即:先继承的保留,后继承的被覆盖
class Phone:
IMEI = None
producer = "vvv"
def call_by_5g(self):
print("5g通话")
class NFCReader:
nfc_type = None
producer = "HM"
def read_card(self):
print("读取NFC卡")
def write_card(self):
print("写入NFC卡")
class RemoteControl:
rc_type = "红外遥控"
def controll(self):
print("红外线遥控开启")
# 单继承
class MySimplePhnoe(Phone):
simple = "单继承"
# 多继承
class MyPhnoe(Phone, NFCReader, RemoteControl):
pass
if __name__ == '__main__':
my_phnoe = MyPhnoe()
print(my_phnoe.producer) # None 而非后面继承的 后面继承的类属性不会覆盖前面的类
复写
类继承父类的成员属性和成员方法后,如果对其“不满意”,那么可以进行复写。
即:在子类中重新定义同名的属性或方法即可。
class Phone:
IMEI = None
producer = "vvv"
def call_by_5g(self):
print("5g通话")
class MySimplePhnoe(Phone):
IMEI = "复写一个"
def test(self):
# 方式一 调用父类成员变量
print(f"父类的IMEI是:{Phone.IMEI}")
Phone.call_by_5g(self)
# 方式二 调用父类成员变量
print(f"父类的IMEI是:{super().IMEI}")
super().call_by_5g()
""" 输出结果
复写一个
父类的IMEI是:None
5g通话
父类的IMEI是:None
5g通话
"""
if __name__ == '__main__':
simple = MySimplePhnoe()
print(simple.IMEI) # 复写一个
simple.test()
类型注解
通过工具提示快速知道使用的方法需要的是什么类型的参数
Python在3.5版本的时候引入了类型注解,以方便静态类型检查工具,IDE等第三方工具。
类型注解:在代码中涉及数据交互的地方,提供数据类型的注解(显式的说明)。
主要功能:
-
帮助第三方IDE工具(如PyCharm ctrl +p)对代码进行类型推断,协助做代码提示
-
帮助开发者自身对变量进行类型注释
支持:
- 变量的类型注解
函数(方法)形参列表和返回值的类型注解
变量:类型 = 值
var_1:int = 10
-
元组类型设置类型详细注解,需要将每一个元素都标记出来
-
字典类型设置类型详细注解,需要 2个类型,第一个是key 第二个是value
my_list: list[int] = [1, 2, 3]
# 元组类型设置类型详细注解,需要将每一个元素都标记出来
my_tuple: tuple[int, str, bool] = (1, "sdd", True)
my_set: set[int] = {1, 2, 3}
# 元组类型设置类型详细注解,需要将每一个元素都标记出来
my_dict: dict[str, int] = {"key", 111}
除了使用 变量: 类型, 这种语法做注解外,也可以在注释中进行类型注解。
语法
# type: 类型
在注释中进行类型注释
函数(方法)类型注解
def 函数方法名(形参名:类型,形参名:类型,形参名:类型,....) -> 返回值类型:
pass
Union类型
使用Union[类型, …, 类型]
可以定义联合类型注解
Union联合类型注解,在变量注解、函数(方法)形参和返回值注解中,均可使用
from typing import Union
my_list: list[Union[int,str]] = [1, 2, 3,"ddd"]
my_dict: dict[str, Union[str,int]] = {"key", 111,"key2","value"}
def func(data:Union[int,str]) -> Union[str,int] :
pass
多态
多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态。
"""
Animal
抽象类(也可以称之为接口)
抽象类:含有抽象方法的类称之为抽象类
抽象方法:方法体是空实现的(pass)称之为抽象方法
"""
class Animal:
def speak(self):
pass
class Dog(Animal):
def speak(self):
print("汪汪汪")
class Cat(Animal):
def speak(self):
print("喵喵喵")
def make_noise(animal:Animal):
animal.speak()
# 同样的行为(函数),传入不同的对象,得到不同的状态
make_noise(Dog())
make_noise(Cat())
10、数据库
pymysql
pip install pymysql
import pymysql
from pymysql import Connection
conn = Connection(
host="localhost",
port=3306,
user="root",
password="root",
# 默认不为true 不自动提交
autocommit=True
)
print(conn.get_server_info())
cursor = conn.cursor()
conn.select_db("db2019")
# cursor.execute("select * from payment")
"""
游标对象使用fetchall()方法,得到的是全部的查询结果,是一个元组
这个元组内部嵌套了元组,嵌套的元组就是一行查询结果
"""
# results:tuple = cursor.fetchall()
# for row in results:
# print(row)
cursor.execute("insert into payment values (21,'python21')")
conn.close()
11、高阶
11.1 闭包
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包
"""
在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称为闭包
"""
def account_create(initial_amount = 0):
def atm(num,deposit=True):
nonlocal initial_amount
if deposit:
initial_amount += num
print(f"存款金额:{num},账户余额:{initial_amount}")
else:
initial_amount -= num
print(f"取款金额:{num},账户余额:{initial_amount}")
return atm
"""
存款金额:300,账户余额:300
存款金额:400,账户余额:700
取款金额:300,账户余额:400
"""
fn = account_create()
fn(300)
fn(400)
fn(300,False)
优点,使用闭包可以让我们得到:
-
无需定义全局变量即可实现通过函数,持续的访问、修改某个值
-
闭包使用的变量的所用于在函数内,难以被错误的调用修改
缺点:
- 由于内部函数持续引用外部函数的值,所以会导致这一部分内存空间不被释放,一直占用内存
11.2 装饰器
装饰器其实也是一种闭包, 其功能就是在不破坏目标函数原有的代码和功能的前提下,为目标函数增加新功能。
定义一个闭包函数, 在闭包函数内部:
-
执行目标函数
-
并完成功能的添加
装饰器的一般写法
def outer(func):
def inner():
print("业务前处理")
func()
print("业务后处理")
return inner
def sleep():
import random
import time
print("休眠中")
time.sleep(random.randint(1,5))
fn = outer(sleep)
fn()
装饰器语法糖写法
def outer(func):
def inner():
print("业务前处理")
func()
print("业务后处理")
return inner
@outer
def sleep():
import random
import time
print("休眠中")
time.sleep(random.randint(1,5))
sleep()
11.3 单列模式
单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。
在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。
-
定义: 保证一个类只有一个实例,并提供一个访问它的全局访问点
-
适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它时。
在一个文件中定义
class Tool:
pass
str_tool = Tool()
在另一个文件中定义
import my_package.test1
t1 = my_package.test1.str_tool
t2 = my_package.test1.str_tool
# t1 和 t2 是同一个对象
print(t1) # <my_package.test1.Tool object at 0x00000184D806BD10>
print(t2) # <my_package.test1.Tool object at 0x00000184D806BD10>
-
节省内存
-
节省创建对象的开销
11.4 多线程
进程 与线程
现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。
进程: 就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程ID方便系统管理。
线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位。
进程就好比一家公司,是操作系统对程序进行运行管理的单位
线程就好比公司的员工,进程可以有多个线程(员工),是进程实际的工作者
操作系统中可以运行多个进程,即多任务运行
一个进程内可以运行多个线程,即多线程运行
进程之间是内存隔离的, 即不同的进程拥有各自的内存空间。 这就类似于不同的公司拥有不同的办公场所。
线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。
这就好比,公司员工之间是共享公司的办公场所
并行执行
进程之间是内存隔离的, 即不同的进程拥有各自的内存空间。 这就类似于不同的公司拥有不同的办公场所。
线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。
这就好比,公司员工之间是共享公司的办公场所
threading模块
import threading
thread= threading.Thread([group[target[,name[,agrs[,kwagrs]]]]])
# group 暂时无用,未来预留参数
# target 执行的目标任务名
# args 以元组的方式给执行任务传参
# kwagrs 以字典的方式给任务传参
# name 线程名一般不用设置
thread.start()
import threading
import time
def sing():
while True:
print("我在唱歌,到睿米发骚拉西道")
time.sleep(1)
def dance():
while True:
print("我在跳舞。。。。")
time.sleep(2)
# target 后面不要加括号
threading.Thread(target=sing).start()
threading.Thread(target=dance).start()
需要传参的话可以通过:
-
args参数通过元组(按参数顺序)的方式传参
-
或使用kwargs参数用字典的形式传参
import threading
import time
def sing():
while True:
print("我在唱歌,到睿米发骚拉西道")
time.sleep(1)
def dance(msg,msg1):
while True:
print(msg + ":"+ msg1)
time.sleep(2)
def dance2(msg):
while True:
print(msg)
time.sleep(2)
# target 后面不要加括号
threading.Thread(target=sing).start()
# agrs 根据位置顺序
threading.Thread(target=dance, args=("args1","args2",)).start()
# kwargs 使用字典, key 为参数名
threading.Thread(target=dance2, kwargs={"msg":"dd"}).start()
11.5 Socket
客户端和服务端
2个进程之间通过Socket进行相互通讯,就必须有服务端和客户端
Socket服务端:等待其它进程的连接、可接受发来的消息、可以回复消息
Socket客户端:主动连接服务端、可以发送消息、可以接收回复
Socket服务端编程
import socket
host= "127.0.0.1"
port = 9999
# 创建socket对象
socket_server = socket.socket()
# 绑定IP与端口
socket_server.bind((host,port))
# 开启监听
# backlog 为 int 值,表示允许的连接数量,超出的会等待,可以不填,不填会自动设置一个合理值
socket_server.listen()
# 接受客户端连接,获得连接对象
# accept 是阻塞方法
# 返回的是二元组
conn,address = socket_server.accept()
print(f"接收到连接 来自{address}")
# 通过recv接口客户端发送的消息
while True:
data = conn.recv(1024).decode("UTF-8")
# recv 方法返回的是字节数组(Bytes),可以通过decode解码为字符串
# recv 参数 是缓冲区大小一般设置为1024
if data == 'exit':
break
print(f"接收到的数据是:{data}")
send_data = input("请输入回复的消息:")
conn.send(send_data.encode("UTF-8"))
conn.close()
socket_server.close()
客户端
import socket
host= "127.0.0.1"
port = 9999
socket_client = socket.socket()
socket_client.connect((host,port))
while True:
senf_msg = input("请输入要发送的信息")
if senf_msg == 'exit' :
break
socket_client.send(senf_msg.encode("UTF-8"))
recv_data = socket_client.recv(1024).decode("UTF-8")
print(f"服务端回复的消息:{recv_data}")
socket_client.close()
正则表达式
正则表达式,又称规则表达式(Regular Expression),是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。
简单来说,正则表达式就是使用:字符串定义规则,并通过规则去验证字符串是否匹配。
比如,验证一个字符串是否是符合条件的电子邮箱地址,只需要配置好正则规则,即可匹配任意邮箱。
比如通过正则规则: (^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$)
即可匹配一个字符串是否是标准邮箱格式
但如果不使用正则,使用if else来对字符串做判断就非常困难了。
正则的三个基础方法
Python正则表达式,使用re模块,并基于re模块中三个基础方法来做正则匹配。
分别是:match、search、findall 三个基础方法
-
re.match(匹配规则, 被匹配字符串)
从被匹配字符串开头进行匹配, 匹配成功返回匹配对象(包含匹配的信息),匹配不成功返回空
-
search(匹配规则, 被匹配字符串)
搜索整个字符串,找出匹配的。从前向后,找到第一个后,就停止,不会继续向后
-
findall(匹配规则, 被匹配字符串)
匹配整个字符串,找出全部匹配项
元字符匹配
在刚刚我们只是进行了基础的字符串匹配,正则最强大的功能在于元字符匹配规则。
单字符匹配:
字符 | 功能 |
---|---|
. | 匹配任意一个字符(除了\n),.匹配点本身 |
[] | 匹配[]列举的字符 |
\d | 匹配数字0-9 |
\D | 匹配非数字 |
\s | 匹配空白、空格、Tab键 |
\S | 匹配非空白 |
\w | 匹配单词字符,即a-z,A-Z,0-9、_ |
\W | 匹配非单词字符 |
数量匹配
字符 | 功能 |
---|---|
* | 匹配前一个规则的字符出现0至无数次 |
+ | 匹配前一个规则的字符出现1至无数次 |
? | 匹配前一个规则的字符出现0或者1次 |
{m} | 匹配前一个规则的字符出现m次 |
{m,} | 匹配前一个规则的字符出现最少m次 |
{m,n} | 匹配前一个规则的字符出m到n次 |
边界匹配 |
字符 | 功能 |
---|---|
^ | 匹配字符串开头 |
$ | 匹配字符串结尾 |
\b | 匹配一个单词的边界 |
\B | 匹配非单词的边界 |
分组匹配 |
字符 | 功能 |
---|---|
| | 匹配左右任意一个表达式 |
() | 将括号中字符作为一个分组 |
-
匹配账号,只能由字母和数字组成,长度限制6到10位规则为:
^[0-9a-zA-Z]{6, 10}$
-
匹配QQ号,要求纯数字,长度5-11,第一位不为0规则为:
^[1-9][0-9]{4, 10}&
[1-9]
匹配第一位,[0-9]
匹配后面4到10位 -
匹配邮箱地址,只允许qq、163、gmail这三种邮箱地址
规则为:
^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+&
-
[\w-]+
表示出现a-z A-Z 0-9 _
和 - 字符最少一个,最多不限 -
(\.[\w-]+)*
,表示出现组合 . 和a-z A-Z 0-9 _
-的组合最少0次,最多不限用于匹配:
abc.ced.efg@123.com
中的ced.efg这部分 -
@表示匹配@符号
-
(qq|163|gmail)
表示只匹配这3个邮箱提供商 -
(\.[\w-]+)+
表示a-z A-Z 0-9 _
-的组合最少1次,最多不限用于匹配abc.ced.efg@123.com.cn中的.com.cn这种
最后使用+表示最少一次,即比如:.com
多了可以是:.com.cn.eu这样
11.6 递归
递归在编程中是一种非常重要的算法
递归: 即方法(函数)自己调用自己的一种特殊编程写法
# 函数调用自己,即称之为递归调用
def fun():
if ...:
fun()
return ....
1、什么是递归
在满足条件的情况下,函数自己调用自己的一种特殊编程技巧
2、 递归需要注意什么?
-
注意退出的条件,否则容易变成无限递归
-
注意返回值的传递,确保从最内层,层层传递到最外层
3、 os模块的3个方法
-
os.listdir,列出指定目录下的内容
-
os.path.isdir,判断给定路径是否是文件夹,是返回True,否返回False
-
os.path.exists,判断给定路径是否存在,存在返回True,否则返回False