python学习笔记

1基础语法

1.1字面量

学习目标

  1. 掌握字面量的含义
  2. 了解常见字面量类型
  3. 基于print语句完成各类字面量的输出

数据类型

类型 描述
数字
字符串 描述文本的一种数据类型
列表 有序的可变序列
元组 无序的不可变序列
集合 无序不重复集合
字典 无序的Key-Value集合
数字
整型
浮点数
复数
布尔

总结

  1. 代码中,被写在代码中的固定的值,称之为字面量
  2. 常见字面量类型:整型、浮点型、字符串

1.2注释

学习目标

  1. 了解注释作用
  2. 能够使用单行注释和多行注释

定义

在程序代码中对程序代码进行解释说明的文字

作用:注释不是程序。不能被执行。只是对程序进行注释说明,增强可读性。

注释的分类

  • 单行注释:以**#开头**,#右边的所有文字当作说明,而不是真正要执行的·程序,起辅助说明作用
  • 多行注释:以一对三引号引起来(‘’‘注释内容’‘’)来解释说明这一段代码作用

1.3变量

学习目标

  1. 理解变量的作用及特征
  2. 掌握变量的定义方式

什么是变量

在程序运行时,能够存储计算结果或能表示值的抽象概念。

简单的说,变量就是在程序运行时,记录数据用的。

变量的定义格式

变量名 = 变量值

变量的特征

变量的值可以改变

1.4数据类型

type()语句

直接输出类型信息,返回一个字符串。

变量没有类型,是变量存储的数据类型。

1.5数据类型转换

常见的类型转换

语句(函数) 说明
int(x) 将x转换为一个整a数,将浮点数的小数部分删掉
float(x) 将x转换为一个浮点数
str(x) 将对象x转换为字符串

总结

  1. 任何类型都可以转换成字符串
  2. 字符串不可以随意转换成数字
  3. 浮点数转整数会丢失精度

1.6标识符

什么是标识符

  • 变量的名字
  • 方法的名字
  • 类的名字

命名规则

内容限定

标识符中,只可以出现:

  • 英文
  • 中文
  • 数字
  • 下划线

大小写敏感

不可以使用关键字

注意

  1. 不推荐使用中文
  2. 数字不可以开头

变量命名规范

  1. 见名知意

  2. 下划线命名法

  3. 英文字母全小写

总结

1.7运算符

算术运算符

运算符 描述
+
-
*
/ 除,float
// 取整除,int
% 取余
** 指数

1.8字符串扩展

学习目标

  • 字符串的三种定义方式
  • 字符串拼接
  • 字符串格式化
  • 格式化的精度控制
  • 字符串格式化方法2
  • 对表达式进行格式化

字符串三种定义方式

  1. 单引号定义法
  2. 双引号定义法
  3. 三引号定义法

三引号定义法,和多行注释的写法一样,同样支持换行操作。

使用变量接收他,他就是字符串。

不使用变量接收他,就可以作为多行注释使用。

如果定义字符串本身想包含单引号、双引号自身,可以:

  • 单引号定义法,可以内含双引号
  • 双引号定义法,可以内含单引号
  • 可以使用转义字符(\)来将引号解除效果,变成普通字符串

字符串拼接

可以通过+将两个字符串拼接,可以字面量+字面量,也可以变量+字面量、变量+变量

注意:只能适用于字符串本身,不能用于其他类型

字符串格式化

name = "李明"
number = 19100
message = "名字是%s,电话是%s" % (name,number)
print(message)

其中的,%s

  • %s表示:我要占位
  • s表示:将变量变成字符串放入占位的地方
  • 多个变量占位,变量要用括号括起来,并按照占位的顺序填入
常见三类占位
格式化符号 转化
%s 字符串
%d 整型
%f 浮点型
name = "李明"
age = 18
height = 1.78
message = "名字:%s,年龄:%d,身高:%f" % (name,age,height)

格式化的精度控制

可以使用辅助符号“m.n”来控制数据的宽度和精度

  • m,控制宽度,要求是数字,设置的宽度小于数字自身,不生效,反之补齐空格
  • .n,控制小数点精度,要求是数字,会进行小数的四舍五入

字符串格式化方法2

通过语法:f"内容{变量}"的格式来快速格式化

name = "李明"
age = 18
height = 1.78
message = f"名字:{name},年龄:{age},身高:{height}"

f为format,格式化的符号,不关心类型和精度

对表达式进行格式化

表达式:一条具有明确执行结果的代码语句

print("1*1的结果是%d" % (1*1))
print(f"1*1的结果是{1*1}")
print("字符串在python中类型为%s" % type('字符串'))

1.9数据输入

使用input()语句从键盘获取输入,使用变量存储输入

print("输入名字")
name = input()
print("你是%s"%name)

输入的为字符串类型,可以通过类型转换改变数据类型。

2判断

2.1布尔类型和比较运算符

学习目标

  • 掌握布尔类型用于表示:真和假
  • 掌握比较运算符用于计算:真和假

布尔类型(bool)

  • True ,真
  • False,假

定义变量存储布尔类型数据:

变量名称 = 布尔类型字面量

比较运算符

布尔类型的数据,不仅可以通过定义得到,也可以通过比较运算符进行内容比较得到。

运算符 描述 示例
==
!=
>
<
>=
<=

2.2if语句的基本格式

学习目标

  • 掌握判断语句if的基本语法格式
  • 掌握布尔类型数据,在判断语句中的作用

语法

if 要判断的条件:
	条件成立时,要做的事情

通过缩进判断代码块属于哪一个if

注意

  • 判断条件的结果一定要是布尔类型
  • 不要忘记判断条件后的:冒号
  • 归属于if语句的代码块,需要在前方填充4个空格缩进

2.3if else 语句

语法

if 条件:
	满足条件做的事情
else:
	不满足条件做的事情

注意

  • else之后,不需要判断条件
  • 和if的代码块一样,else的代码也需要4个空格缩进

2.4if elif else 语句

语法

if 条件1:
    条件1满足时做的事情
elif 条件2:
    条件2满足时做的事情
else:
    所有条件都不满足时做的事情

判断互斥且是有顺序的:

  • 满足1将不再理会2和3
  • 满足2不理会3
  • 均不满足,进入else
  • else也可以省略不写,效果等同于三个独立的if

2.5判断语句的嵌套

语法

if 条件1:
    满足条件1做的事
    
    if 条件2:
        满足条件2做的事

嵌套的关键点在于缩进

通过缩进来决定语句之间的层次关系

总结

  1. 嵌套判断语句可以用于多条件、多层次的逻辑判断
  2. 嵌套判断语句可以根据需求,自由组合if elif else来构建多层次判断
  3. 嵌套判断语句,一定要注意空格缩进,python通过空格缩进来决定层次关系

3循环

3.1while循环的基础语法

语法

while 条件:
    条件满足做的事情

只要条件满足会无限执行

注意

  1. while的条件需要得到布尔类型数据,True表示继续循环,False表示结束循环
  2. 需要设置循环的终止条件,否则将无限循环
  3. 空格缩进和if判断一样,都需要设置

3.2while 循环的嵌套应用

学习目标

  1. 掌握while嵌套循环的基本格式
  2. 完成while嵌套循环的应用

语法

while 条件1:
    条件1满足时做的事情
    
    while 条件2:
        条件2满足时做的事情

总结

  • 注意条件的控制,避免进入无限循环
  • 多层嵌套,主要空格来确定层次关系
  • 循环条件的控制,层数越多越复杂,需要细心+耐心

3.3for 循环的基础语法

基础语法

for 临时变量 in 待处理数据集:
    循环满足条件时执行的代码
注意
  • 无法定义循环条件,只能被动取出数据处理
  • 循环内的语句,需要有空格缩进

range语句

for循环语句,本质上是遍历:序列类型。

语法:

range(num1,num2,step)

获得一个从num1开始,到num2结束的数字序列,不含num2本身。

数字之间的步长,以step为准。

变量作用域

3.4for 循环的嵌套应用

语法

for 临时变量 in 待处理的数据集:
    循环条件满足时应做的事情1
    
    for 临时变量 in 待处理的数据集:
        循环条件满足时应做的事情2

注意

  • 注意缩进,嵌套for循环通过缩进确定层级关系
  • for 循环与 while 循环可以嵌套

3.5循环中断:break和continue

continue

中断本次循环,直接进入下一次循环

break

直接结束循环

注意

  • continue与break,在for循环和while循环中作用一致
  • 在嵌套循环中,只能作用在所在的循环上,无法对上层循环起作用

4函数

4.1函数介绍

函数是组织好的,可重复使用的,用来实现特定功能的代码段

优点

  • 将功能封装在函数内,可供随时随地重复利用
  • 提高代码的复用性,减少重复代码,提高开发效率

4.2函数的定义

语法

def 函数名(传入参数):
    函数体
    return 返回值

函数使用步骤

  • 先定义函数
  • 后调用函数

注意

  • 参数不需要,可以省略
  • 返回值不需要,可以省略

4.3函数的参数

在函数进行计算的时候,接受外部(调用时)提供的数据

语法

def add(x,y):
    result = x+ y
    return result

传入参数的个数不受限制,可以不使用参数,也可以使用任意N个参数

注意

  • 函数中定义的参数,称为形式参数
  • 函数中调用的参数,称为实际参数
  • 函数的参数数量不限,使用逗号分隔开
  • 传入参数的时候,要和形式参数一一对应,逗号隔开

4.4函数的返回值

函数在执行完成后,返回给调用者的结果

语法

def 函数名(传入参数):
    函数体
    return 返回值
变量 = 函数(参数)

注意

函数在遇到return后就结束了,后面代码不执行

None

如果函数没有使用return语句返回数据,那么函数返回None。

None ,类型是

在if中,None等于False

4.5函数说明文档

通过多行注释的形式,写在函数体之前

4.6函数的嵌套调用

指在一个函数里面又调用一个函数

执行过程

函数a中执行到调用函数b的语句,会将函数b全部执行完后,继续执行a的剩余内容

4.7变量的作用域

学习目标

  1. 知道什么是局部变量
  2. 知道什么是全局变量

概念

变量的作用域指的是变量的作用范围

局部变量

定义在函数体内部的变量,即只在函数体内部生效

作用:在函数体内部,临时保存数据,即当函数调用完成后,即销毁局部变量

全局变量

指在函数体内,外都能生效的变量

global关键字

使用global关键字,可以在函数内部声明变量为全局变量

4.8多返回值

def test_return():
    return 1,2
x,y = test_return()
  • 按照返回值的顺序,写对应顺序的多个变量接受即可

  • 变量间用逗号隔开

  • 支持不同类型的数据return

4.9函数的多种参数使用形式

使用方式不同,函数有4种常见参数使用方式

  • 位置参数
  • 关键字参数
  • 缺省参数
  • 不定长参数

位置参数

调用函数时根据函数定义的参数位置来传递参数

def user_info(name,age,gender):
    print(f"您的姓名是{name},年龄是{age},性别是{gender}")
    
user_info("tom",20,"男")

注意:传递的参数和定义的参数的顺序及个数必须一致

关键字参数

函数调用时通过“键=值”的形式传递参数

作用:可以让函数更加清晰,容易使用,同时也清除了参数的顺序需求

def user_info(name,age,gender):
    print(f"您的姓名是{name},年龄是{age},性别是{gender}")
#关键字传参   
user_info(name = "tom",age = 20,gender = "男")

#可以不按照固定顺序   
user_info(age = 20,name = "tom",gender = "男")

#可以和位置参数混着用,但位置参数必须在前,且匹配参数顺序
user_info("tom",gender = "男",age = 20)

缺省参数

缺省参数也叫默认参数,对于定义函数,为参数提供默认值,调用函数时可以不传该默认参数的值,所有位置参数必须出现在默认参数之前,包括函数定义和调用。

作用:当调用函数时没有传递参数,就会使用默认是缺省参数对应的值

def user_info(name,age,gender = "男"):
    print(f"您的姓名是{name},年龄是{age},性别是{gender}")
    
user_info("tom",20)

默认参数必须写在最后面

不定长参数

不定长参数也叫可变参数,用于不确定调用的时候会传递多少个参数(不传参也可以)的场景

作用:当调用函数时不确定参数的个数,可以使用不定长参数

类型

  • 位置传递
  • 关键字传递

位置传递

def user_info(*args):
    print(args)
    
user_info("tom",20)

传进的所有参数会被args变量收集,他会根据传进参数的位置合并为一个元组,args是元组类型,就是位置传递。

关键字传递

def user_info(**kwargs):
    print(kwargs)
    
user_info(name = "tom",age = 20)

参数是“键=值”形势的情况下,所有的“键=值”都会被kwargs接受,同时会根据“键=值”形成字典

4.10函数作为参数传递

语法

def test_func(compute):
    result = compute(1,2)
    return result
def compute(x,y):
    return x+y

test_func(compute)	#结果3

函数compute,作为参数,传入了test_func函数中使用

  • test_func需要一个函数作为参数传入,这个函数需要接收2个数字进行计算,计算逻辑由这个被传入函数决定
  • compute函数接收2个数字对其进行计算,compute函数作为参数,传递给test_func函数使用
  • 最终,在test_func函数内部,由传入的compute函数,完成了对数字的计算操作

所以,这是一种,计算逻辑的传递,而非数据的传递

总结

  1. 函数本身是可以作为参数,传入另一个函数中进行使用的
  2. 将函数传入的作用在于:传入计算逻辑,而非传入数据

4.11lambda匿名函数

函数的定义中

  • def关键字,可以定义带有名称的函数
  • lambda关键字,可以定义匿名函数(无名称)

有名称的函数,可以基于名称重复使用

无名称的匿名函数,只可以临时使用一次

语法

lambda 传入参数:函数体(一行代码)
  • lambda是关键字,表示定义匿名函数
  • 传入参数表示匿名函数的形式参数,如:x,y表示接受两个参数
  • 函数体,就是函数的执行逻辑,要注意,只能写一行,无法写多行
def test_func(compute):
    result = compute(1,2)
    return result

test_func(lambda x,y :x+y)	#结果3

5数据容器

5.1数据容器入门

一种可以容纳多份数据的数据类型,容纳的每一份数据称之为1个元素,每一个元素,可以是任意类型的数据,如字符串、数字、布尔等。

5.2数据容器:list

列表的定义

语法

#字面量
[元素1,元素2,....]

#定义变量
变量名称 = [元素1,元素2,....]

#定义空列表
变量名称 = []
变量名称 = list()

列表可以一次存储多个数据,也可以为不同数据类型,支持嵌套。

列表的下标索引

从0开始,依次递增,列表名[下标]。

反向索引,从-1开始,倒数第一个。

注意不要超出范围,否则会报错

列表的常用操作

查询某元素下标

功能:查找指定元素在列表的下标,如果找不到,报错ValueError

语法

列表.index(元素)
列表的修改功能

功能:修改特定位置的元素值,对指定下标(正向、反向均可)的值进行重新赋值。

语法

列表[下标] =
列表的插入元素功能

功能:在指定的下标位置,插入指定的元素

语法

列表.insert(下标,元素)

追加元素1

功能:在列表尾部插入指定的元素

语法

列表.append(元素)

追加元素2

功能:将其他数据容器的内容取出后,依次追加到列表尾部

语法

列表.extend(其他数据容器)
列表的删除元素功能

del

功能:函数式删除

语法

del 列表[下标]

pop

功能:列表的方法,将删除的元素返回

语法

列表.pop[下标]

remove

功能:删除某元素在列表的第一个匹配项

语法

列表.remove(元素)
列表的清空功能

功能:清空列表

语法

列表.clear()
列表的统计功能

功能:统计列表中某元素的数量

语法

列表.count(元素)
列表元素的数量

功能:统计列表中所有元素的数量

语法

len(列表名)

列表的特点

  • 可以容纳多个元素(上限为2^63-1个)
  • 可以容纳不同类型的元素,混装
  • 数据是有序存储的,下标
  • 允许重复数据存在
  • 可以修改

5.3list遍历

将容器内的元素依次取出,并处理,称之为遍历操作

可以使用while循环和for循环操作

for循环
for 临时变量 in 数据容器:
    对临时变量进行处理
for 和 while 对比
  • for更简单,while更灵活
  • for用于从容器内依次取出元素并处理,while适用于任何需要循环的场景

5.4数据容器 tuple

学习目标

  1. 掌握元组的定义格式
  2. 掌握元组的特点
  3. 掌握元组的常见操作

定义元组

#定义元组字面量
(元素1,元素2,元素3)

#定义元组变量
变量名称 = (元素1,元素2)

#定义空元组
变量名称 = ()
变量名称 = tuple()

元组的操作

index()查找

查找某个数据,如果数据存在返回对应下标,否则报错

元组.index(元素)
count()统计

统计某个数据在当前元组出现的次数

元组.count()
len()长度

统计元组内的元素个数

len(元组)

元组特点

  • 可以容纳多个数据
  • 可以容纳不同类型数据,混装
  • 数据是有序存储的,下标索引
  • 允许数据重复存在
  • 不可以修改
  • 支持for循环

5.5数据容器 str

从前向后,下标从0开始

从后向前,下标从-1开始

字符串是一个无法修改的数据容器

常用操作

index

查找子串在原字符串中起始下标

字符串.index(子串)
replace

将字符串内的全部:字符串1,替换为字符串2

不是修改字符串本身,而是得到了一个全新的字符串

字符串.replace(字符串1,字符串2)
split

按照指定的分割符字符串,将字符串划分为多个字符串,并存入列表对象中

字符串.split(分隔符字符串)

字符串本身不变,而是得到了一个列表对象

strip

字符串的规整操作,去前后空格

字符串.strip()

去前后指定字符串

字符串.strip(字符串)

会将字符串变成字符去除

count

统计字符串内某个子串出现次数

字符串.count(字符串)
len
len(字符串)

包含空格

特点

  • 只可以存储字符串
  • 长度任意
  • 支持下标索引
  • 允许重复字符串存在
  • 不可以修改
  • 支持for循环

5.6数据容器的切片

学习目标

  1. 了解什么是序列
  2. 掌握序列的切片操作

序列

指的是内容连续、有序、可使用下标索引的一类数据容器

切片

从一个序列中,取出一个子序列

语法

序列[起始下标;结束下标;步长]
  • 起始下标表示从何处开始,可以留空,视作从头开始
  • 结束下标(不含)表示从何处结束,可以留空,视作截取到结尾
  • 步长表示,依次取元素的间隔
  • 步长为负数为反着取,此时起始下标和结束下标也要反着写

5.7数据容器 set

语法

#定义集合字面量
{元素,元素,...,元素}
#定义集合变量
变量名称 = {元素,元素,...,元素}
#定义空集合
变量名称 = set()

操作

add
集合.add()
remove
集合.remove()
pop

随机取一个元素并返回

集合.pop()
clear

清空集合

集合.clear()
difference

取两个集合的差集,集合1有而集合2没有,返回一个新的集合

集合1.difference(集合2)
difference_update

对比集合1和集合2,在集合1中,删除和集合2相同的元素

结果,集合1被修改,集合2不变

集合1.difference_update(集合2)
union

将集合1和集合2合并,返回新集合,原集合不变

集合1.union(集合2)
len

统计集合元素数量

len(集合)
集合遍历

集合不支持下标,不能用while循环,可以用for循环

特点

  • 可以容纳多个元素
  • 可以容纳不同类型数据
  • 数据是无序存储的
  • 不允许重复数据存在
  • 可以修改
  • 支持for循环

5.8数据容器 dict

定义

字典定义,同样使用{},不过存储的元素是一个个的:键值对

#定义字典字面量
{key:value,key:value,...,key:value}
#定义字典变量
my_dict = {key:value,key:value,...,key:value}
#定义空字典
my_dict = {}
my_dict = dict()

字典同集合一样,不可以使用下标索引

但是字典可以通过key值来取得对应的value

从字典中基于key获取value

字典[key]

字典的嵌套

字典中key和value可以是任何·数据类型(key不可以为字典)

操作

新增元素
字典[key] = value
更改元素
字典[key] = value

字典里key不可以重复,如果不存在新key,则新增元素,否则修改元素

删除元素
字典.pop(key)

获得指定key的value,同时字典被修改,指定key的数据被删除

清空字典
字典.clear()
获取字典中全部的key
字典.keys()

得到字典全部的key,是dict_keys类型,可以for循环遍历

统计元素数量
len(字典)

特点

  • 可以容纳多个元素
  • 可以容纳不同类型数据
  • 每一份数据都是key-value键值对
  • 可以通过key获得value,key不可以重复,重复会覆盖
  • 不支持下标索引
  • 可以修改
  • 支持for循环

不同数据容器对比

列表 元组 字符串 集合 字典
元素数量 支持多个 支持多个 支持多个 支持多个 支持多个
元素类型 任意 任意 仅字符 任意 key:value(key:除字典外任意类型,value:任意类型)
下标索引 支持 支持 支持 不支持 不支持
重复元素 支持 支持 支持 不支持 不支持
可修改性 支持 不支持 不支持 支持 支持
数据有序
使用场景 可修改的,可重复的一批数据记录场景 不可修改的,可重复的一批数据记录场景 一串字符的记录场景 不可重复的数据记录场景 以key检索value的数据记录场景

5.9数据容器的通用操作

遍历

len

统计数据容器长度

max

找到数据容器中最大的元素

max(数据容器)

min

找到数据容器中最小的元素

min(数据容器)

通用转换

  • list(),转列表,字符串变成字符列表,字典抛弃value,只剩下key
  • tuple(),转元组,字符串变成字符元组,字典抛弃value,只剩下key
  • str(),转字符串
  • set(),转换集合,字符串变成字符集合,字典抛弃value,只剩下key

排序

sorted(数据容器,[reverse = True])

对内容排序,返回一个列表

reverse = True表示降序

6文件操作

6.1文件编码

编码分类

  • UTF-8
  • GBK
  • Big5

UTF-8是目前全球通用的编码格式

6.2文件的读取

打开文件

使用open函数,可以打开一个已经存在的文件,或者创建一个新文件,语法如下

open(name,mode,encoding)
  • name:是打开的目标文件名的字符串(可以包含文件所在的具体路径)
  • mode:设置打开文件的模式(访问模式):只读、写入、追加等
  • encoding:编码格式
f = open('python.txt','r',encoding='UTF-8')
#encoding的顺序不是第三位,所以不能使用位置参数,用关键字参数直接指定

mode常用三种模式

模式 描述
r 以只读方式打开。文件袋指针将会放在文件的开头。这是默认模式
w 打开一个文件只用于写入。如果文件已存在则打开文件,并从头开始编辑,原有内容会删除。如果文件不存在,创建新文件
a 打开一个文件用于追加。如果文件已存在,新的内容将会被写入到已有的内容之后。如果文件不存在,创建新文件进行写入

读取文件

read方法
文件对象.read(num)

num表示要从文件中读取的数据的长度(单位是字节),如果没有传入num,那么就表示读取文件中所有的数据。指针会移动。

readlines方法
f = open('python.txt')
content = f.readlines()

#['hello world\n','abcdefg\n','aaa\n','bbb\n','ccc']
print(content)

#关闭文件
f.close()

readlines可以按照行的方式把整个文件的内容进行一次性读取,并且返回的是一个列表,其中每一行的数据为一个元素。

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临时变量,就记录了文件一行数据

关闭文件

f = open('python.txt')

f.close()

不关闭文件,文件一直占用

with open

with open("python.txt",'r') as f:
    for line in f:
        print(line)
       

执行完语句后,文件自动关闭

6.2 文件的写入

#1.打开文件
f = open("python.txt",'w')

#2.文件写入
f.write('hello world')  #写内存

#3.内容刷新
f.flush()   #写硬盘

注意:

  • 直接调用write方法,内容并未真正写入文件,而是会积攒在程序的内存中,称之为缓冲区
  • 当调用flush的时候,内容会真正写入文件
  • 这样做是避免频繁的硬盘操作,导致效率下降(攒一堆,一次写入硬盘)

6.3 文件的追加

#1.打开文件
f = open("python.txt",'a')

#2.文件写入
f.write('hello world')  #写内存

#3.内容刷新
f.flush()   #写硬盘

注意:

  • a模式,文件不存在会创建文件
  • a模式,文件存在会在最后,追加写入文件

7异常、模块与包

7.1了解异常

当检错的一个错误时,python解释器就无法继续执行,反而出现一些错误的提示,就是异常,也叫bug

7.2 异常的捕获方法

try:
    可能发生错误的代码
except:
    如果出现异常执行的代码

捕获指定的异常

try:
    
except NameError as e:
    print('出现变量未定义的异常')
    print(e)

捕获多个异常

try:
    
except (NameError,ZeroDivisionError) as e:
    print('出现变量未定义的异常')
    print(e)

捕获全部异常

try:
    
except Exception as e:
    print('出现变量未定义的异常')
    print(e)
else:
    没有异常执行代码
finally:
	最终执行代码

7.3异常的传递性

异常具有传递性。

def func1():
    print('this is func1')
    num = 1/0
    print("func0 over")
def func2():
    print("this is func2")
    func1()
    print('func2 over')
    
def main():
    try:
        func2()
    except Exception as e:
        print(e)

当函数func1中发生异常,并且没有捕获处理这个异常,异常会传递到func2,当func2也没有捕获处理这个异常的时候,main函数就会捕获这个异常,这就是异常的传递性。

7.3 python模块

模块的导入

一个模块就是一个工具包,每一个工具包都有各种不同的工具供我们使用进而实现各种不同的功能

[from 模块名] import [模块||变量|函数|*] [as 别名] 

常用组合类型

  • import 模块名
  • from 模块名 import 类、变量、方法等
  • from 模块名 import *
  • import 模块名 as 别名
  • from 模块名 import 功能名 as 别名

自定义模块并导入

当导入多个模块的时候,且模块内有同名功能,当调用这个同名功能时候,调用的是后面导入模块的功能。

**__main__**变量

if __name__ == '__main__':
    执行模块代码

**__all__**变量

如果一个模块文件中有’__all__‘变量,当使用from xxx import * 导入时候,只能导入这个列表中的元素。没有all变量则导入全部。若想导入可以手动导入。

__all__ = ['test1']

def test1():
    pass

def test2():
    pass

7.4 python包

从物理上看,包就是一个文件夹,在该文件夹下包含了一个__init__.py文件,该文件夹可用于包含多个模块文件

从逻辑上看,包的本质依然是模块

导入包

import 包名.模块名

在’__init__.py’文件中添加’__all__= []',控制允许导入的模块列表

7.5 安装第三方python包

pip install -i https://pypi.tuna.tsinghua.edu.cn/simple 包名称

基础综合案例-数据可视化开发

json数据格式

定义

  • json是一种轻量级的数据交互格式,可以按照json指定的格式去组织和封装数据
  • json本质上是一个带有特定格式的字符串

主要功能

json就是一种在各个编程语言中流通的数据格式,负责不同编程语言的数据交互和传递

pyecharts模块

基础折线图

#导包,导入line功能构建折线图对象
from pyecharts.charts import Line

#得到折线图对象
line = Line()

#添加x轴数据
line.add_xaxis(["中国","美国","英国"])
#添加Y轴数据
line.add_yaxis("gdp",[30,20,10])
#生成图标
line.render()

配置选项

  • 全局配置选项
  • 系列配置选项

全局配置选项可以通过set_global_opts方法来进行配置

set_global_opts方法

#导包,导入line功能构建折线图对象
from pyecharts.charts import Line
from pyecharts.options import TitleOpts,LegendOpts,ToolboxOpts,VisualMapOpts
#得到折线图对象
line = Line()

#添加x轴数据
line.add_xaxis(["中国","美国","英国"])
#添加Y轴数据
line.add_yaxis("gdp",[30,26,10])

#设置全局配置项
line.set_global_opts(
    title_opts=TitleOpts(title="GDP展示",pos_left="center",pos_bottom="1%"),
    legend_opts= LegendOpts(is_show=True),
    toolbox_opts= ToolboxOpts(is_show=True),
    visualmap_opts= VisualMapOpts(is_show=True)

)

#生成图标
line.render()

折线图绘制

import json
from pyecharts.charts import Line
from pyecharts.options import TitleOpts,LabelOpts

#处理数据
f_us = open("美国.txt","r",encoding="UTF-8")
us_data = f_us.read()

f_jp = open("日本.txt","r",encoding="UTF-8")
jp_data = f_jp.read()

f_in = open("印度.txt","r",encoding="UTF-8")
in_data = f_in.read()

#去掉不合理的JSON规范的开头
us_data = us_data.replace("jsonp_1629344292311_69436(","")
jp_data = jp_data.replace("jsonp_1629350871167_29498(","")
in_data = in_data.replace("jsonp_1629350745930_63180(","")

#去掉不合理JSON数据规范的结尾
us_data = us_data[:-2]
jp_data = jp_data[:-2]
in_data = in_data[:-2]

#JSON转换为python字典对象
us_dict = json.loads(us_data)
jp_dict = json.loads(jp_data)
in_dict = json.loads(in_data)

#获取trend key
us_trend_data = us_dict["data"][0]["trend"]
jp_trend_data = jp_dict["data"][0]["trend"]
in_trend_data = in_dict["data"][0]["trend"]

#获取日期数据,用于x轴,取2020年(到314下标结束)
us_x_data = us_trend_data["updateDate"][:314]
jp_x_data = jp_trend_data["updateDate"][:314]
in_x_data = in_trend_data["updateDate"][:314]

#获取确诊数据,用于Y轴,取2020年(到下标314结束)
us_y_data = us_trend_data["list"][0]["data"][:314]
jp_y_data = jp_trend_data["list"][0]["data"][:314]
in_y_data = in_trend_data["list"][0]["data"][:314]

#生成图表
#构建折线图对象
line = Line()

#添加x轴数据,x轴是公用的,所以添加一个就可以
line.add_xaxis(us_x_data)

#添加y轴数据
line.add_yaxis("美国确诊人数",us_y_data,label_opts=LabelOpts(is_show=False)) #显示数字
line.add_yaxis("日本确诊人数",jp_y_data,label_opts=LabelOpts(is_show=False))
line.add_yaxis("印度确诊人数",in_y_data,label_opts=LabelOpts(is_show=False))

#设置全局选项
line.set_global_opts(
    title_opts= TitleOpts(title="2020年美日印三国确诊人数对比折线图",pos_left="center",pos_bottom="1%")
)

#调用render方法,生成图标
line.render()

#关闭文件对象
f_us.close()
f_jp.close()
f_in.close()

地图绘制

全国疫情地图

import json
from pyecharts.charts import Map
from pyecharts.options import *

#读取文件数据
f = open("疫情.txt","r",encoding="UTF-8")
data = f.read()
#关闭文件
f.close()
#读取各省数据
#将字符串json转换为python字典
data_dict = json.loads(data)
#从字典读取各省数据
province_data_list = data_dict["areaTree"][0]["children"]
#组装每个省份和确诊人数为元组,并各个省的数据都封装入列表内
data_list = []  #绘图需要的数据列表
for province_data in province_data_list:
    province_name = province_data["name"] #省份名称
    if province_name in ["北京","天津","重庆","上海"]:
        province_name = province_name + "市"
    elif province_name in ["内蒙古","西藏"]:
        province_name = province_name + "自治区"
    elif province_name == "新疆":
        province_name = province_name + "维吾尔自治区"
    elif province_name == "宁夏":
        province_name = province_name + "回族自治区"
    elif province_name == "广西":
        province_name = province_name + "壮族自治区"
    else:
        province_name = province_name + "省"
    province_confirm = province_data["total"]["confirm"]    #确诊人数
    data_list.append((province_name,province_confirm))




#创建地图对象
map = Map()
#添加数据
map.add("各省份确诊人数",data_list,"china")
#设置全局选项
map.set_global_opts(
    title_opts= TitleOpts(title="全国疫情地图"),
    visualmap_opts= VisualMapOpts(
        is_show=True,
        is_piecewise=True,
        pieces=[
            {"min": 1, "max": 99, "lable": "1-99人", "color": "#CCFFFF"},
            {"min": 100, "max": 999, "lable": "100-999人", "color": "#FFFF99"},
            {"min": 1000, "max": 4999, "lable": "1000-4999人", "color": "#FF9966"},
            {"min": 5000, "max": 9999, "lable": "5000-9999人", "color": "#FF6666"},
            {"min": 10000, "max": 99999, "lable": "10000-99999人", "color": "#CC3333"},
            {"min": 100000, "lable": "100000+", "color": "#990033"},
        ]
    )

)
map.render("全国疫情地图.html")


河南省疫情地图

import json
from pyecharts.charts import Map
from pyecharts.options import *

#读取文件数据
f = open("疫情.txt","r",encoding="UTF-8")
data = f.read()
#关闭文件
f.close()
#读取各省数据
#将字符串json转换为python字典
data_dict = json.loads(data)
#从字典读取河南数据
cities_data = data_dict["areaTree"][0]["children"][3]["children"]
#组装每个省份和确诊人数为元组,并各个省的数据都封装入列表内
data_list = []  #绘图需要的数据列表
for city_data in cities_data:
    city_name = city_data["name"] + "市"
    city_confirm = city_data["total"]["confirm"]    #确诊人数
    data_list.append((city_name,city_confirm))
data_list.append(("济源市",5))



#创建地图对象
map = Map()
#添加数据
map.add("河南省疫情分布",data_list,"河南")
#设置全局选项
map.set_global_opts(
    title_opts= TitleOpts(title="河南省疫情地图"),
    visualmap_opts= VisualMapOpts(
        is_show=True,
        is_piecewise=True,
        pieces=[
            {"min": 1, "max": 99, "lable": "1-99人", "color": "#CCFFFF"},
            {"min": 100, "max": 999, "lable": "100-999人", "color": "#FFFF99"},
            {"min": 1000, "max": 4999, "lable": "1000-4999人", "color": "#FF9966"},
            {"min": 5000, "max": 9999, "lable": "5000-9999人", "color": "#FF6666"},
            {"min": 10000, "max": 99999, "lable": "10000-99999人", "color": "#CC3333"},
            {"min": 100000, "lable": "100000+", "color": "#990033"},
        ]
    )

)
map.render("河南疫情地图.html")


柱状图

基础时间线柱状图

from pyecharts.charts import Bar,Timeline
from pyecharts.options import *
from pyecharts.globals import ThemeType

bar1= Bar()
bar1.add_xaxis(['中国','美国','英国'])
bar1.add_yaxis("GDP",[30,30,10],label_opts=LabelOpts(
    position="right"
))
bar1.reversal_axis()

bar2= Bar()
bar2.add_xaxis(['中国','美国','英国'])
bar2.add_yaxis("GDP",[30,20,40],label_opts=LabelOpts(
    position="right"
))
bar2.reversal_axis()

bar3= Bar()
bar3.add_xaxis(['中国','美国','英国'])
bar3.add_yaxis("GDP",[50,60,70],label_opts=LabelOpts(
    position="right"
))
bar3.reversal_axis()

timeline = Timeline(
    {"theme": ThemeType.LIGHT}
)
timeline.add(bar1,"点1")
timeline.add(bar2,"点2")
timeline.add(bar3,"点3")

timeline.add_schema(
    play_interval=1000,
    is_timeline_show=True,
    is_auto_play=True,
    is_loop_play=True
)

timeline.render("基础时间线柱状图.html")



动态GDP柱状图

from pyecharts.charts import Bar,Timeline
from pyecharts.options import *
from pyecharts.globals import ThemeType

#读取数据
f = open('1960-2019全球GDP数据.csv','r',encoding='GB2312')
data_lines = f.readlines()
#关闭文件
f.close()
#删除第一条数据
data_lines.pop(0)
#将数据转换为字典存储
data_dict = {}
for line in data_lines:
    year = int(line.split(',')[0])
    country = line.split(',')[1]
    gdp = float(line.split(',')[2])
    #判断字典有没有key
    try:
        data_dict[year].append([country,gdp])
    except KeyError:
        data_dict[year] = []
        data_dict[year].append([country,gdp])

#创建时间线对象
timeline = Timeline({"theme" : ThemeType.LIGHT})

#排序年份
sorted_year_list = sorted(data_dict.keys())

for year in sorted_year_list:
    data_dict[year].sort(key=lambda element:element[1],reverse=True)
    #取出本年份前8的国家
    year_data = data_dict[year][0:8]
    x_data = []
    y_data = []
    for country_gdp in year_data:
        x_data.append(country_gdp[0])
        y_data.append(country_gdp[1])

    #构建柱状图
    bar = Bar()
    x_data.reverse()
    y_data.reverse()
    bar.add_xaxis(x_data)
    bar.add_yaxis("GDP(亿)",y_data,label_opts=LabelOpts(position="right"))
    #反转坐标轴
    bar.reversal_axis()
    #设置每一年标题
    bar.set_global_opts(
        title_opts=TitleOpts(title=f"{year}年全球前八GDP数据")
    )
    timeline.add(bar,str(year))

#设置自动播放
timeline.add_schema(
    play_interval=1000,
    is_timeline_show=True,
    is_auto_play=True,
    is_loop_play=True
)
timeline.render('1960-2019全球GDP前八.html')

8面向对象

8.1成员方法

定义类

class 类名称:
    类的属性
    
    类的行为
  • class是关键字,表示要定义类了
  • 类的属性,即定义在类中的变量(成员变量)
  • 类的行为,即定义在类中的函数(成员方法)

创建类对象的语法

对象 = 类名称()

成员方法的定义语法

def 方法名(self,形参1,形参2,....):
    方法体

在定义方法的参数列表中,有一个self关键字必须填写

  • 它用来表示类对象自身的意思
  • 当我们使用类对象调用方法时,self会自动被python传入
  • 在方法的内部,想要访问类的成员变量,必须使用self

self关键字尽管在参数列表总,但传参时可以忽略。

8.2构造方法

python类中可以使用**__init__()**方法,称之为构造方法

可以实现

  • 在创建类对象(构造类)的时候,会自动执行
  • 在创建类对象(构造类)的时候,将传入参数自动传递给__init__()方法使用
class Student:
    def __init__(self, name, age, tel):
        self.name = name
        self.age = age
        self.tel = tel
        print("student创建一个类对象")
        
stu = Student("张三", 31, "1856666")

注意

  • 构造方法不要忘了self关键字
  • 在方法内部使用成员变量需要使用self

8.3其他内置方法

内置方法也称之为魔术方法

__str__()字符串方法

class Student:
    def __init__(self, name, age, tel):
        self.name = name
        self.age = age
        self.tel = tel
        print("student创建一个类对象")

    def __str__(self):
        return f"Student类对象,name:{self.name},age:{self.age},tel:{self.tel}"

stu = Student("张三", 31, "1856666")
print(stu)

#student创建一个类对象
#Student类对象,name:张三,age:31,tel:1856666

当类对象需要被转换为字符串时,调用__str__(),会输出对象地址。可以自己设计转化字符串的行为

__lt__()小于符号比较方法

直接对两个对象进行比较是不可能的,但在类中实现__lt__方法,即可同时完成:小于符号和大于符号2种比较

class Student:
    def __init__(self, name, age, tel):
        self.name = name
        self.age = age
        self.tel = tel
        print("student创建一个类对象")

    def __str__(self):
        return f"Student类对象,name:{self.name},age:{self.age},tel:{self.tel}"

    def __lt__(self, other):
        return self.age < other.age

stu = Student("张三", 31, "1856666")
stu2 = Student('李四',25,'4566')
print(stu>stu2)

__le__()小于等于符号比较方法

__le__()可用于:<=、>=两种比较运算符上

class Student:
    def __init__(self, name, age, tel):
        self.name = name
        self.age = age
        self.tel = tel
        print("student创建一个类对象")

    def __str__(self):
        return f"Student类对象,name:{self.name},age:{self.age},tel:{self.tel}"

    def __lt__(self, other):
        return self.age < other.age

    def __le__(self, other):
        return self.age <= other.age

stu = Student("张三", 31, "1856666")
stu2 = Student('李四',25,'4566')
print(stu<=stu2)

__eq__()等于符号比较方法

== 符号默认比较内存地址

class Student:
    def __init__(self, name, age, tel):
        self.name = name
        self.age = age
        self.tel = tel
        print("student创建一个类对象")

    def __str__(self):
        return f"Student类对象,name:{self.name},age:{self.age},tel:{self.tel}"

    def __lt__(self, other):
        return self.age < other.age

    def __le__(self, other):
        return self.age <= other.age

    def __eq__(self, other):
        return self.age == other.age

stu = Student("张三", 31, "1856666")
stu2 = Student('李四',31,'4566')
print(stu==stu2)

8.4封装

概念

将现实世界在类中描述属性和方法,即为封装。

类中提供私有成员的形式:

  • 私有成员变量
  • 私有成员方法

定义私有成员变量和私有成员方法,只需要:

以__(两个下划线)开头,即可完成设置

访问限制

类对象无法访问私有成员,类中其他成员可以访问私有

class Phone:

    __current_voltage = 0.5  #当前运行电压


    def __keep_single_core(self):
        print("电量不足,让CPU单核运行")
    def call_5g(self):
        if self.__current_voltage >= 1:
            print('5g已经打开')
        else:
            self.__keep_single_core()
p = Phone()
p.call_5g()

8.5继承

单继承

一个类继承另一个类

class Phone:
    TMEI = None #序列号
    Producer = None #厂商


    def call_by_4g(self):
        print('4g通话中')

class Phone2022(Phone):
    face_id = True  #面部识别

    def call_by_5g(self):
        print('2022最新5g通话')

p = Phone2022()
p.call_by_4g()

多继承

一个类继承多个类,按照从左向右顺序继承

class Phone:
    TMEI = None #序列号
    Producer = None #厂商
    def call_by_4g(self):
        print('4g通话中')

class NFC:
    nfc_type = '第五代'
    producer = 'HM'

    def reader(self):
        print('NFC读卡')
    def writer(self):
        print('NFC写卡')

class RemoteControl:
    rc_type = '红外遥控'

    def control(self):
        print('红外打开')

class myphone(Phone,NFC,RemoteControl):
    pass

p = myphone()
p.call_by_4g()
p.reader()
p.control()

pass表示空,用处是保证完整性。

多继承中,遇到同名的成员属性和成员方法,按照谁先继承谁优先级最高的顺序。

复写

子类继承父类的成员属性和成员方法后,如果不满意,可以进行复写

即在子类中重新定义同名的属性和方法即可。

class Phone:
    TMEI = None #序列号
    Producer = None #厂商
    def call_by_5g(self):
        print('5g通话中')


class myphone(Phone):

    producer = 'itcast'

    def call_by_5g(self):
        print('打开5g')
        print('关闭CPU')

p = myphone()
p.call_by_5g()

调用父类同名成员

  • 调用父类成员

    • 使用成员变量:父类名.成员变量

    • 使用成员方法:父类名.成员方法(self)

  • 使用super()调用父类成员

    • 使用成员变量:super().成员变量
    • 使用成员方法:super().成员方法()
class Phone:
    TMEI = None #序列号
    Producer = 'ITCAST' #厂商
    def call_by_5g(self):
        print('5g通话中')


class myphone(Phone):

    producer = 'ITHIEMA'

    def call_by_5g(self):
        print('打开5g')
        #方式1
        print(f'父类厂商{Phone.Producer}')
        Phone.call_by_5g(self)	#需要传入self
        #方式2
        print(f'父类厂商{super().Producer}')
        super().call_by_5g()
        print('关闭CPU')

p = myphone()
p.call_by_5g()

8.6类型注解

变量的类型注解

支持

  • 变量的类型注解
  • 函数形参列表和返回值的类型注解

为变量设置类型注解

基础语法:变量:类型

var_1: int = 10
var_2: float = 3.1415
var_3: bool = True
var_4: str = 'hello'

基础容器类型注解

my_list: list = [1,2,3]
my_tuple: tuple = (1,2,3)
my_set: set = {1,2,3}
my_dict: dict = {key:value}
my_str: str = 'hello'

容器类型详细注解

在python3.9直接注解,3.7版本需要导入包

from __future__ import annotations
my_list:list[int] = [1,2,3]
my_tuple:tuple[str,int,bool] = ('hello',66,True)
my_set:set[int] = {1,2,3}
my_dict:dict[str,int] = {key:value}

注意

  • 元组类型设置详细注解,需要将每一个元素都标记出来
  • 字典类型设置详细类型注解,需要两个类型,第一个是key,第二个是value

除了使用变量:类型这种语法做注解外,也可以在注释中进行类型注解

语法

#type: 类型
class Student:
    pass
var_1 = random.randint(1,10)	#type:int
var_2 = json.loads(data)	#type:dict[str,int]

类型注解的限制

类型注解只是提示性的,并非决定性的。数据类型和注解类型无法对应也不会导致错误

函数和方法的类型注解

对形参进行类型注解

def add(x:int,y:int):
    return x+y

对返回值进行类型注解

def func(data:list) -> list:
    return data

Union类型

可以定义联合类型注解

from __future__ import annotations
from typing import Union
mylist:list[Union[str,int]] = [1,2,'hell']

可以对变量进行类型注解,同时也可以对返回值注解

from __future__ import annotations
from typing import Union

my_list:list[Union[int,str]] = [1,2,'hello']

def func(data:Union[int,str]) -> Union[int,str]:
    pass
func()
                               

8.7多态

多态,指的是:多种状态,即完成某个行为时,使用不同的对象会得到不同的状态

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()
    
dog = Dog()
cat = Cat()
make_noise(dog)
make_noise(cat)

抽象类(接口)

设计的含义:

  • 父类来确定有哪些方法
  • 具体的方法实现,由子类自行决定

这种写法称之为抽象类

  • 抽象类:含有抽象方法的类称之为抽象类
  • 抽象方法:方法体是空实现的(pass)称之为抽象方法

综合案例-数据分析

main

'''
面向对象,数据分析案例,主业务逻辑代码
实现步骤:
1.设计一个类,可以完成数据的封装
2.设计一个抽象类,定义文件读取的相关功能,并使用子类实现具体功能
3.读取文件,生产数据对象
4.进行数据需求的逻辑计算(计算每一天的销售额)
5.通过pyecharts进行图形绘制
'''
from __future__ import annotations
from file_define import FileReader,TextFileReader,JSONFileReader
from data_define import Record
from pyecharts.charts import Bar
from pyecharts.options import *
from pyecharts.globals import ThemeType

text_file_reader = TextFileReader('2011年1月销售数据.txt')
json_file_reader = JSONFileReader('2011年2月销售数据JSON.txt')

jan_data:list[Record] = text_file_reader.read_data()
feb_data:list[Record] = json_file_reader.read_data()

#将2个月份的数据合并为一个list来存储
all_date:list[Record] = jan_data + feb_data

#开始数据计算
data_dict = {}
for record in all_date:
    if  record.date in data_dict.keys():
        #当前日期已有记录,和老记录累加即可
        data_dict[record.date] += record.money
    else:
        data_dict[record.date] = record.money


#可视化开发
bar = Bar(init_opts=InitOpts(theme=ThemeType.LIGHT))
bar.add_xaxis(list(data_dict.keys()))   #添加x轴数据
bar.add_yaxis("销售额",list(data_dict.values()),label_opts=LabelOpts(is_show=False))   #添加y轴数据
bar.set_global_opts(
    title_opts= TitleOpts(title="每日销售额")
)
bar.render("每日销售额柱状图.html")

file_define

'''
和文件相关的类定义
'''
from __future__ import annotations

import json

from data_define import Record

#先定义一个抽象类用来做顶层设计,确定有哪些功能需要实现
class FileReader:

    def read_data(self) -> list[Record]:
        '''读取文件的数据,读到的每一条数据都转换为Record对象,将他们都封装到list内返回即可'''
        pass

class TextFileReader(FileReader):

    def __init__(self,path):
        self.path = path

    #复写父类方法
    def read_data(self) -> list[Record]:
        f = open(self.path,'r',encoding='UTF-8')
        record_list:list[Record] = []
        for line in f.readlines():
            line = line.strip()
            data_list = line.split(",")
            record = Record(data_list[0],data_list[1],int(data_list[2]),data_list[3])
            record_list.append(record)
        f.close()
        return record_list

class JSONFileReader(FileReader):

    def __init__(self,path):
        self.path = path

    #复写父类方法
    def read_data(self) -> list[Record]:
        f = open(self.path,'r',encoding='UTF-8')
        record_list:list[Record] = []
        for line in f.readlines():
            data_dict = json.loads(line)
            record = Record(data_dict['date'],data_dict['order_id'],int(data_dict['money']),data_dict['province'])
            record_list.append(record)
        f.close()
        return record_list

if __name__ == '__main__':
     text_file_reader = TextFileReader('2011年1月销售数据.txt')
     json_file_reader = JSONFileReader('2011年2月销售数据JSON.txt')
     list1 = text_file_reader.read_data()
     list2 = json_file_reader.read_data()
     for l in list1:
         print(l)
     for l in list2:
         print(l)

data_define

'''
数据定义的类
'''
from __future__ import annotations

class Record:

    def __init__(self,date:str,order_id:str,money:int,province:str):
        self.date = date                #订单日期
        self.order_id = order_id        #订单id
        self.money = money              #订单金额
        self.province = province        #订单省份

    def __str__(self):
        return f"{self.date},{self.order_id},{self.money},{self.province}"

9高阶

9.1闭包

在函数嵌套的前提下,内部函数使用了外部函数的变量,并且外部函数返回了内部函数,我们把这个使用外部函数变量的内部函数称之为闭包。

def outer(logo):
    def inner(msg):
        print(f"<{logo}><{msg}><{logo}>")

    return inner

fn1 = outer('hello')
fn1('world')

fn2 = outer('nihao')
fn2('we')

如果在闭包中内部修改外部函数的值,需要使用nonlocal关键字修饰外部函数的变量才可以在内部函数中修改它

def outer(num1):
    def inner(num2):
        nonlocal num1
        num1 += num2
        print(num1)
    return inner

fn = outer(10)
fn(10)
fn(20)

使用闭包实现ATM取钱

def account_creat(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

atm = account_creat()

atm(100)
atm(200)
atm(150,deposit=False)

优点:

  • 无需定义全局变量即可实现通过函数,持续的访问、修改某个值
  • 闭包使用的变量的所用于函数内,难以被错误的调用修改

缺点:

  • 由于内部函数持续引用外部函数的值,所以导致这一部分内存空间不被释放,一直占用内存

9.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()

9.3设计模式

设计模式是一种编程套路,可以极大的方便程序的开发

最常见、最经典的设计模式,就是面向对象

除了面向对象外,还有其他设计模式:

  • 单例、工厂模式
  • 建造者、责任链、状态、备忘录、解释权、访问者、观察者、中介、模版、代理模式
  • 等等模式

单例模式

创建类的实例后,就可以得到一个完整的、独立的类对象

某些场景下,我们需要一个类无论获取多少次类对象,都仅仅提供一个具体的实例以节省创建类对象的开销和内存开销

比如某些工具类,仅需要一个实例,即可在各处使用

这就是单例模式达到的效果


单例模式(singleton Patern)是一种常见的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在。

在整个系统中,某个类只能出现一个实例时,单例对象就能派上用场。

  • 定义:保证一个类只能有一个实例,并提供一个访问它的全局访问点
  • 适用场景:当一个类只能有一个实例,而客户可以从一个众所周知的访问点访问它

StrTools.py

class StrTools:
    pass

str_tool = StrTools()

test.py

from StrTools import str_tool

s1 = str_tool
s2 = str_tool

print(id(s1))
print(id(s2))

工厂模式

当需要大量创建一个类的实例的时候,就可以使用工厂模式。

即,从原生的使用类的构造去创建对象的形式

迁移到,基于工厂提供的方法去创建对象的形式。

class Person():
    pass

class Woorker(Person):
    pass

class Student(Person):
    pass

class Teacher(Person):
    pass

class PersonFactory:
    def get_person(self,p_type):
        if p_type == 'w':
            return Woorker()
        elif p_type == "s":
            return Student()
        else:
            return Teacher()
        
pf = PersonFactory()
worker = pf.get_person('w')
stu = pf.get_person('s')
teacher = pf.get_person('t')

好处

  • 大批量创建对象的时候有统一的入口,易于代码维护
  • 当发生修改,仅修改工厂类的创建方法即可
  • 符合现实世界的模式,即由工厂来制作产品(对象)

9.4多线程

进程:就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程ID方便系统管理

线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程的实际工作最小单位

操作系统中可以运行多个进程,即多任务运行

一个进程内可以运行多个线程,即多线程运行

注意

  • 进程之间是内存隔离的,即不同的进程拥有各自的内存空间。

  • 线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间。

并行执行

指的是同一时间做不同的工作。

进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行。

除了进程外,线程也是可以并行执行的。

import threading

thread_obj = threading.Thread([group [, target [, name [, args [, kwargs]]]]])
- group:暂时无用,未来功能的预留参数
- target:执行的目标任务名
- args:以元组的方式给执行任务传参
- kwargs:以字典方式给执行任务传参
- name:线程名,一般不用设置
import time
import threading
def sing(msg):
    while True:
        print(msg)
        time.sleep(1)

def dance(msg):
    while True:
        print(msg)
        time.sleep(1)

if __name__ == '__main__':
     sing_therd= threading.Thread(target=sing,args=("我在唱歌,啦啦啦",))
     dance_therd = threading.Thread(target=dance,kwargs={"msg":"我在跳舞,呱呱呱"})

     sing_therd.start()
     dance_therd.start()

9.5网络编程

Socket

socket(简称 套接字)是进程之间通信的一个工具,进程之间想要进行网络通信需要socket

Socket服务端:等待其他进程的连接、可接受发来的消息、可以回复消息

Socket客户端:主动连接服务端、可以发送消息、可以回复消息

服务端

'''
演示socket服务端开发
'''
#1.创建socket对象
import socket
socket_server = socket.socket()

#2.绑定ip地址和端口
socket_server.bind(('localhost',8888))

#3.监听端口
socket_server.listen(1)
#listen方法内接受一个整数传参数,表示接受的连接数量

#4.等待客户端连接
coon, address = socket_server.accept()   #coon表示连接对象,address表示客户端地址信息
#accept()是一个阻塞方法,等待客户端的连接,如果没有连接,就卡在一行不向下执行
print(f"接收到客户端信息{address}")

while True:
    #5.接受客户端信息,使用客户端和服务端的本次连接对象而非socket_server对象
    data = coon.recv(1024).decode('UTF-8')
    #recv()方法的返回值是一个字节数组也就是bytes对象,不是字符串,可以通过decode方法通过UTF-8将字节数据转化为字符串对象
    print(f"客户端信息为{data}")
    
    #6.发送回复消息
    msg = input("请输入你和客户端回复的消息:")
    if msg == 'exit':
        break
    coon.send(msg.encode('UTF-8'))
    
#7.关闭连接
coon.close()
socket_server.close()

客户端

'''
演示socket客户端
'''
#1.创建socket对象
import socket
socket_client = socket.socket()

#2.连接到服务器
socket_client.connect(("localhost",8888))

#3.发送消息
while True:   
    msg = input("请输入要给服务端发送的消息:")
    if msg == 'exit':
        break
    socket_client.send(msg.encode("UTF-8"))
    
    #4.接受返回信息
    recv_data = socket_client.recv(1024)
    print(f"服务端回复消息为{recv_data.decode('UTF-8')}")
    
#5.关闭连接
socket_client.close()

正则表达式

正则表达式,又称规则表达式,是使用单个字符串来描述、匹配某个句法规则的字符串,常被用来检索、替换那些符合某个模式(规则)的文本。

基础匹配

re.match(匹配规则,被匹配字符串)

从被匹配字符串开头进行匹配,匹配成功返回匹配对象(包含匹配信息),匹配不成功返回空

re.search(匹配规则,被匹配字符串)

搜索整个字符串,找出匹配的。从前向后,找到第一个后,就停止,不会继续向后。整个字符串找不到返回空。

findall(匹配规则,被匹配字符串)

匹配整个字符串,找出全部匹配项。找不到返回空list

import re

#match从头匹配
s = 'python itheima python itheima '
result = re.match('python',s)
print(result)
#search搜索匹配
res = re.search("python",s)
print(res)
#findall搜索全部匹配
ress = re.findall("python",s)
print(ress)

元字符匹配

单字符匹配

字符 功能
. 匹配任意1个字符(\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位

r = '^[0-9a-zA-Z]{6,10}$'

匹配QQ号,要求纯数字,长度5-11位,第一位不为0

r = '^[1-9][0-9]{4,10}$'

匹配邮箱地址,只允许QQ、163、gmail这三种邮箱地址

r = '^[\w-]+(\.[\w-]+)*@(qq|163|gmail)(\.[\w-]+)+$'

递归

即方法自己调用自己的一种特殊写法

注意

  • 注意退出的条件,否则容易变成无限递归
  • 注意返回值的传递,确保从最内层,层层传递到最外层

os模块的三个方法

  1. os.listdir,列出指定目录下的内容
  2. os.path.isdir,判断给定路径是否是文件夹,是返回True,否返回False
  3. os.path.exists,判断给定路径是否存在,存在返回True,否返回False

案例

找到目录下所有文件

'''
演示递归操作
需求:通过递归,找到一个指定文件夹的全部文件

思路:写一个函数,列出文件夹内全部内容,如果是文件就收集到list
如果是文件夹,就递归调用自己,再次判断
'''

import os
def get_files_recursion_from_dir(path):
    '''
    从指定的文件夹中使用递归的方式,获取全部的文件列表
    :param path: 被判断的文件夹
    :return: list,包含全部的文件,如果目录不存在或者无文件就返回一个空list
    '''
    file_list = []
    if os.path.exists(path):
        for f in os.listdir(path):
            new_path = path + "/" + f
            if os.path.isdir(new_path):
                #进入这里表明这个目录是文件夹
                file_list += get_files_recursion_from_dir(new_path)
            else:
                file_list.append(new_path)

    else:
        print(f"指定的目录{path},不存在")
        return []
    return file_list

print(get_files_recursion_from_dir("D:/DownLoad"))

你可能感兴趣的:(python,学习,笔记)