李行波笔记

一、Python 基础

1. 代码规范

1.1 进制转换和编码和解码

​ 二进制:0b – binary,符号集有0,1,函数bin(数字)可以转二进制数据

​ 八进制:0o – octal,符号集有0,1,2,3,4,5,6,7。函数oct(数字)可以转八进 制数据

​ 十进制:符号集0,1,2,3,4,5,6,7,8,9。

​ 十六进制:0x – hexadecimal,符号集有0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f。 在十六进制中a-f表示10-15,hex(数字)函数可以转化十六 进制数据

1.1.1 进制转化
  1. 十进制转二进制

    整数部分:将数据除2去余数,再将商重复这个操作,知道商为0,将所有的余数逆向拼接,结果就是二进制数据

    24 == 0b11000
    24/2=12 ... 0
    12/2=6 .... 0
    6/2=3  .... 0
    3/2=1  .... 1
    1/2=0  .... 1
    

    小数部分:将数据乘2取整数部分,再将乘积的小数部分乘2取整数部分,重复操作,直到小数的部分为0,将证书部分正序拼接在一起

    0.125 = 0b.001
    0.125*2=0.25 ... 0
    0.25*2=0.5   ... 0
    0.5*2=1.0    ... 1
    
  2. 二进制转十进制

    1. 按权展开求和,倒着以此乘以2的0次方开始
    2. 幂数对应原则,64,32,16,8,4,2,1 倒序对应
  3. 八进制和二进制之间的转化

    1.二进制转八进制:将二进制数据倒序每三位分隔为一位,将分开的每一位按权展开求和,再将数字从左向右拼接起来就是八进制数据

    0b1100111011
    1 100 111 011
    1  4   7   3  ===>0o1473
    

    2.八进制转二进制:将每一位数字按权展开拼接起来

    0o2574
    2 -- 010
    5 -- 101
    7 -- 111
    4 -- 100 ===> 0b10101111100
    
  4. 十六进制和二进制之间的转化

    1.二进制转十六进制:将二进制数据倒序每四位分隔为一位,将分开的每一位按权展开求和,将和从左到右拼接起来

    0b1100111011
    11 0011 1011
    3    3   b  ===> 0x33b
    

    2.十六进制转二进制:将每一位数按权展开转化为二进制数据,将结果顺序拼接

    0x9ae3   8,4,2,1
    9 -- 1001
    a -- 1010
    e -- 1110
    3 -- 0011 ===>0b1001101011100011
    
  5. 十六进制和八进制转十进制需要先转化为二进制,然后按权展开求和

1.1.2 编码和解码
  1. GBK标准中文2个字节
  2. utf-8标准中文3个字节
  3. 0对应的十进制数据是48,a对应的十进制数据是 97,A对应的数据是 65,汉字第一个到最后一个对应 4e00 9fa5

1.2 注释

  1. 对代码进行解释和说明,目的是为了让别人和自己能够清晰的看懂代码的含义,提高代码的可读性
  2. 注释是不会被计算机识别的
  3. 一般写注释的时候是写在代码的上方或者右方
  4. 注释还有一个作用:修改代码的时候,可能代码需要进行调试, 有部分代码用不到,没有必要删除,可以先注释掉
  5. 戴航注释是#+空格,多行注释是三对引号[英文下的单引号或双 引号]单行注释的快捷键 Ctrl + /, 去掉注释Ctrl + /

1.3 标识符

定义:标识某个实体的符号,使用这个符号可以替代实体参与运算;自定义名称的规则:由字母、数字、下滑线组成,不能以数字开头,不能使用关键字和保留字

  1. 关键字和保留字:

    关键字:变成语言中的具有特殊含义的单词

识别关键字的方法: 
import keyword
print(keyword.kwlist)

​ 保留字:变成语言中已经使用过的名称,不建议在使用

1.4 变量和数据类型

  1. 变量:变化的数据,通过使用变量名来调取变化的数据
# 早晨温度
air_temp = 24	
# 中午温度
air_temp = 30
print(air_temp)
  1. 数据类型:
整数类型(int)、
小数类型(浮点类型float)、
文本类型(字符串类型str)、字符串数据是需要使用引号包含的['']或者[""]
布尔类型(bool)、逻辑结果只有两种(成立与不成立),布尔值只有两个 True 和 False
空类型(None)、空字符串[]和空值不能一概而论,比如空值(None)代表什么都没有,空字符串可以看做是个空箱子,而空值连空箱子都没有
  1. 获取数据类型的方法:print(type(变量))

  2. 类和对象:类–对具由相同属性和特征的数据的抽象描述,比如整数类型。对象–所属类型下实际存在的实体,比如 10 。

1.5 输出

  1. print的参数
print(value, ..., sep=' ', end='\n', file=sys.stdout, flush=False)
      value, ...,代表可以同时输出多个内容,书写的时候用逗号隔开
      sep=' ',代表分隔符,一次性输出多个数据的时候默认用空格分开
      end='\n',end代表结束符,\n代表换行符,输出结束是默认是换行符	
      file=sys.stdout,file代表输出的目的地,默认的是sys.stdout(控制台)
      flush=False,代表是否快速的将通道中的数据输出到文件中
  1. 更新内容输出地:

​ r–rend,只读,在程序中读取文件内容

		  w--write,只写,将信息从程序中写入文件当中,文件如果不存在,会创建文件,如果文件存在,会把文件里面已有的内容清空再写入要输出的内容

​ a–append,追加,把要输出的信息追加在已有的信息后面,如果文件不存在,会创建文件,如果文件存 在,会在文件原有内容的下面追加要输出的内容

​ 步骤:先建立通道,然后把数据输出到文件当中

# open(路径,编辑方式(写诗w,读是r),编码标准utf-8)
h1 = open('D:\kf\笔记\诗.txt','w',encoding='utf-8')
# print(内容,文件,是否快速输出)
print('1,2,3', file=h1, flush=True)
  1. 路径

​ 绝对路径:Windows系统,从盘符开始到文件名的路径—D:\kf\笔记\诗.txt

​ mac系统,从根路径/开始到文件名—/Users/kf/笔记/诗.txt

​ 相对路径[推荐使用]:需要文件配置在项目路径下,相对路径的参照物就是当下编辑的文件

​ 符号: . --当前正在操作的文件的文件夹 eg:笔记

​ … --当前正在操作的文件所在文件夹的上一级 eg:kf

h1 = open('./静夜思.txt','a',encoding='utf-8')
print('窗前',file=h1)

1.6 输入

  1. input(提示语),需要在控制台输入,会让程序暂停,直到输入内容为止

  2. 输入的内容无论是什么格式的,输出后都是字符串类型

1.7 类型转化

语法:类型(数据或变量名)
int(数据/变量名):数据必须是整点类型,否则会报错
float(数据/变量名):转换为浮点类型的数据

1.8 运算符

​ 分为一元运算符、二元运算符、三元运算符

1.8.1 算数运算符

​ 一元运算符:正号(+),负号(-)

​ 二元运算符: + - * / //(整除) %(取余) **(幂数)

​ ※优先级:幂数 > 负号 > 乘、除、整除、余、> 加、减

1.8.2 复合赋值运算符

​ +=: x += 1 —> x = x + 1

​ -+: x -= 1 —> x = x - 1

​ *=: x *= 1 —> x = x * 1

​ /=: x /= 1 —> x = x / 1

​ //=: x //= 1 —> x = x // 1

​ %=: x %= 1 —> x = x % 1

​ ** =: x ** = 1 —> x = x ** 1

1.8.3 比较运算符

​ 大于:>

​ 大于等于:>=

​ 小于:<

​ 小于等于:<=

​ 等于:==

​ 不等于:!=

​ ※优先级:算数运算符 > 比较运算符

​ ※拓展:

# 如何X个位上的数字???
x1 = x % 10  # 让数字除10取余数就是个位上的数字
print(x1)
# 如何X十位上的数字???
x2 = x // 10  # 让数字先对10整除
X2.1 = X2 % 10  # 然后再对整除后的数字除10取余就是十位上的数字
print(x2.1)
# 如何X百位上的数字???
x3 = x // 100  # 让数字先对100整除
x3.1 = x3 % 10  # 然后再对整除后的数字除10取余就是百位上的数字
print(x3.1)
1.8.4 逻辑运算符

​ and : 逻辑与:条件与条件之间是并且的关系,必须所有条件都成立的时候结果才是成立的。

执行机制:具备短路原则,左边表达式成立,结果取决于右边表达式;左边表达式不成立,右边表达式不被执行

​ or : 逻辑或:条件与条件之间是或者的关系,所有条件中有一个成立的时候结果就成立

执行机制:也具备短路原则,左边表达式成立,右边不执行;左边表达式不成立,结果取决于右边表达式的值

​ not :逻辑非:对条件结果取反的操作

​ ※优先级:比较运算符 > 逻辑运算符

​ ※not > and > or

布尔类型的值参与数学运算,True是1,False是0

数据类型参与逻辑运算:非0即为True

容器参与逻辑运算:非空即为True

隐式布尔类型:

把非布尔类型的数据转化为布尔类型参与逻辑判断【数据本身不变】,这种行为称为隐式布尔类型转换

# ch = input('请输入一个字符:')
# # ch 是什么类型的数据??? 字符串【容器型的  转换规则:非空容器即为True】
# if ch:  # 隐式布尔转换
#     print(ch)
# else:
#     print('没有输入内容')

# 判断一个数是否为偶数
# num = int(input('请输入一个整数:'))
# if num % 2:  # %2的结果为0或者1, 数值转换为布尔类型 非0即为True
#     print('奇数')
# else:
#     print('偶数')


# 把非布尔类型的数据参与逻辑运算符的运算
print(not 1)  # False

"""
and 执行过程
    True and True = True
    True and False = False
    False and False = False
    False and True = False
    执行机制是左边表达式成立,结果取决于右边表达式;左边表达式不成立,结果就是左边表达式的值
"""
# max_value = num1 if num1 > num2 and num1 > num3 else (num2 if num2 > num3 else num3)
# num1 > num2 and num3  能否写成这样?????  不能!!!!!!
# 这样写 and 右边的表达式不是布尔值 而是一个非布尔类型的数据
num1 = 17
num2 = 15
num3 = 27

res = num1 > num2 and num1 > num3
print(res)  # False
"""
and 左边的表达式的结果为真
所以最终的结果 等于 右边表达式的值
"""
res1 = num1 > num2 and num3
"""
and 左边的表达式的结果为真
所以最终的结果 等于 右边表达式的值
"""
print(res1)  # 27

max_value = num1 if num1 > num2 and num1 > num3 else (num2 if num2 > num3 else num3)
# 但是 条件写成
# max_value = num1 if num1 > num2 and num3 else (num2 if num2 > num3 else num3)


"""
执行机制:左边表达式成立,结果就是左边表达式的值;左边表达式不成立,解决取决于右边表达式的值
"""
# 非布尔类型的数据参与逻辑运算的时候 和 布尔类型的数据参与逻辑运算不一样的
# 因为布尔类型的值只有两个 True或者False  参与逻辑运算结果 也就只有两个 要么为True 要么为False
# 但是非布尔类型的数据 ,因为有一个隐式转换规则  结果又取的是表达式的  这个结果不一定是True或者False
res = 1 or 0
print(res)
1.8.5 成员运算符

​ in :把数据当做一个整体,判断数据是否在容器里面,格式:数据 in 数据容器

​ not in:把数据当做一个整体,判断数据是否不在一个容器里面,格式:数据 not in 数据容器

# in 案例
s = 'hello'
l = 'h' in s
print(l)
结果为True

# not in 案例
s = 'hello'
l = 'q' not in s
print(l)
结果为True
1.8.6 身份运算符

判断两个数据的地址在内存是否是一致的

数据1 is 数据2
	判断两个数据在内存中的地址是否一样
数据1 is not 数据2
	判断两个数据在内存中的地址是否不一样

启动程序,维护程序运行的数据存储于内存中,当程序结束时,数据在内存中被释放了

内存也是分为不同的区域的,不同的区域存储的数据是不一样的,数据的生命周期也是不一样的,区域有:

  1. 栈区

  2. 堆区

  3. 常量池:常量池中存储的是数值类型以及字符串类型的数据,

    特点:获取数据时检查数据是否在常量池中存在,如果存在的话直接根据地址获取数据,如果不存在,先分配空间添加数据,再把数据的地址赋值给变量

    生命周期:当程序结束的时候才会释放

  4. 静态池

    存储全局变量【直接在py文件中定义的变量成为全局变量】

    生命周期:当程序结束的时候才会释放

  5. 方法区

1.8.7 位运算符

这个运算符的运算机制是对二进制数据进行计算的,因为直接是对二进制数据计算,计算的效率是非常高的

&  按位与
	 相同位置的数据,只要有一个是0,该位置的结果为0
|  按位或
	 相同位置的数据,只要有一个是1,该位置的结果为1
^  按位异或
	 相同位置的数据一样,该位置为0,否则为1【相同为0,不同为1】
	 一个数与相同的数异或两次结果使其本身【交换两个变量的值】
	 交换两个变量的值,a = 10  b = 20 ====> a = 20  b = 10
    案例:
    使用三种方法交换两个变量的值
    方法一:借助第三方变量
        c = a
        a = b
        b = c
    方法二:采用异或【不用第三方变量】
        a = a ^ b  # 10 ^ 20
        b = a ^ b  # 10 ^ 20 ^ 20
        b = a ^ b  # 10 ^ 20 ^ 10
    方法三:采用距离问题【不采用第三方变量】
        a = a + b # 10 + 20
        b = a - b # 10 + 20 - 20 
        a = a - b # 10 + 20 - 10
    方法四:python独有的方法
        a,b=b,a
~ 按位取反
	 该位置上的数据0变1,1变0
>> 按位右移
	 将二进制数据向右移动N位,左边空出,如果是正数,左边补0;
	 							   如果是负数,左边补1
	 规律:数值//(2**n)
<< 按位左移
	 将二进制数据向左移动N位,右边空出补0
	 规律:数据*(2**n)

运算符都是针对于位【二进制数据中一个0或者一个1成为一位】的

在计算机发展过程中,根据数据计算的发展的一个过程,二进制形态出现过三种:原码、反码、补码。现在的计算机中存储处理二进制数据的形态是补码

对于正数来说,三码合一,格式是一样的,原码样子就是将十进制数据按照规则转化为二进制的格式

负数的原码:在正数的基础上将最高位置为1,则表示这个数据为负数。【二进制的最高位为符号位:0表示正数,1表示负数】,之前接触过的进制转换,数据大小大小不一样,获取的二进制数据中位的个数不一样,怎么确定那个是最高位???【计算机中数据存储时开辟的最小单元是字节,1个字节是8位,位数一确定,最高位就确定了】,比如常规的数据开启的字节就是4个字节

0000 0000 0000 0000 0000 0000 0000 0001

0000 0000 0000 0000 0000 0000 0000 1010 [10]

1000 0000 0000 0000 0000 0000 0000 1010 [-10]

负数的反码:在负数原码的基础上,除了符号位,0变1,1变0

-10的反码1111 1111 1111 1111 1111 1111 1111 0101

负数的补码:在负数反码的基础上加1

-10的补码:1111 1111 1111 1111 1111 1111 1111 0110

2. 流程控制语句

2.1 if分支结构语句

  1. 单分支结果:在顺序流程中,如果条件达到了,就按照按照达到条件后的流程走,如果没有达到,就按照原流程走
# 如果时间在18点之前,可以去一趟洗车店
print('从学校出发')
# 需要记录当前的时间点
hour = 17
# 要求时间在18点之前 进行洗车的操作
if hour < 18:
    print('洗车店洗车')
print('回到家中')
  1. 双分支结构:有两种流程,满足条件走流程1,不满足走流程2
# 做一个成绩判定器: 判断输入的成绩是及格还是不及格
score = float(input('成绩:'))
if score >= 60:
    print('及格')
else:
    print('不及格')
  1. 多分支结构:有多个流程,满足条件1就走流程1,满足条件2就走流程2 … 满足条件N就走流程N
# 给成绩分等级
"""
    >=90  A
    >=80  B
    >=70  C
    >=60  D
    <60   E
"""
score = float(input('请输入成绩:'))
if score >= 90:
    print('A')
elif score >= 80:  # score 肯定是小于90
    print('B')
elif score >= 70:  # score < 80
    print('C')
elif score >= 60:
    print('D')
else:
    print('E')
  1. 三元运算符
# 普通if表达式
if a > b:
	max_value = a
else:
	max_value = b
# 三元运算符表达式
max_value = a if a > b else b

2.2 循环语句

​ 循环:周而复始的做一件事情

2.2.1 for循环

​ 语法:for 变量 in 容器型数据:

​ 循环体

# 逐个将字符串s中的元素取出来
s = 'hello'
for ch in s:
    print(ch)
输出结果为
h
e
l
l
o
# 获取字符串中小于l的字符
# 循环获取之后,要对其进行判断,判断是否是小于l的
for ch1 in s:
    if ch1 < 'l':
        print(ch1)
2.2.2 range使用

range(start, stop, step)—>start:起始值(包含),stop:终止值(不包含),step:步长(正负都可以)

※如果步长为负数:要求起始值大于终止值,为递减数列;如果步长为整数,要求起始值小于终止值,为递增数列。

2.2.3 for循环的应用
# 求1-100的和
ta = 0
for i in range(1,101):
    ta += i
print(ta)
# 求 1-2+3-4+5-6+...-98+99-100
# 第一种
for i in range(1,101):
    if i % 2 == 0:
        qh -= i
    else:
        qh += i
print(qh)
# 第二种
q1 = 0
q2 = 0
for i1 in range(1,100,2):
    q1 += i1
for i2 in range(2,101,2):
    q2 += i2
print(q1 - q2)
#  1-100之间取是3的倍数且个位数是5的数的前两个
count = 0
for i in range(1,101):
    if i % 3 == 0 and i % 10 == 5:
        count += 1
        print(i)
        if count == 2:
            break
2.2.4 while循环
  1. 循环结构:
定义一个变量作为循环判断的起始值
while 条件判断:
	代码块
对标for in range 解读:
                                         i = 1    起始值
for in range( 1 , 101 , 1 )   =====>    while i < 101:  终止值
	         起始  终止  步长                        i += 1  步长
  1. 案例
练习1:  有1023个桃子, 猴子每天吃桃子剩余个数的一半多1个,问多少天能吃完
tz = 1023  # 桃子的个数
day = 0  # 计数器【天数】
while tz > 0:    # 吃完 记录的剩余的桃子的个数
    tz = tz - (tz // 2 + 1)  # 整除的意思是每一次循环的时候都是整数的桃子,桃子不能有小数
    day += 1
print('1023个桃子猴子需要', day, '天吃完')						
练习2:录入一个大于1的整数,如果这个数值是偶数,将数据整除2后-1,如果这个数是奇数,将这个数据-1再整除2,问多少次之后才能将数据设置为0
num = int(input('请输入一个大于1整数:'))
num1 = num  #  记录原始的数据
count1 = 0
while num > 0:
    if num % 2 == 0:   # 判断奇偶性
        num = num // 2 - 1
    else:
        num = (num - 1) // 2
    count1 += 1
print(num1, '经过运算', count1, '次之后置为0')

练习3:一张纸的厚度是0.8mm,假设纸足够长,问对折多少次可以将纸的高度达到1000米
# 第一种方法
gd = 0.8  
count2 = 0
while gd < 1000 * 1000:  # 首先要统一单位,纸的厚度是毫米,那么1000米的厚度也要转换成毫米
    gd *= 2              # 每次对折都是厚度乘以2
    count2 += 1
print('0.8mm的纸经过', count2, '次对折后达到千米')
# 第二种方法
gd = 0.8
count2 = 0
while True :
	gd *= 2
	count += 1
	if gd >= 1000 * 1000
		print(count2)
		brank
小游戏
"""
数字炸弹:
        规则:有一个出题人 随便在1-100之间设定一个数据,然后其他人猜
            猜小了、猜大了  出题人提示新的范围
            猜中了 就一顿暴揍
        假如是69
        你: 85    出题人 1-85
        他: 70    出题人 1-70
        她: 55     出题人 55-70
        我:  69    挨揍了
"""
import random
sjs = random.randint(1, 100)  # 生成1-100之间的随机数
start = 1                     # 起始值1
stop = 100                   # 终止值100
while True:                  # 猜的动作是循环的
     # 提示输入数字,数字范围改成变量
    pass_sz = int(input('请在' + str(start) +'-' + str(stop) +'之间竞猜:' ))   
    if pass_sz > sjs:      # 然后对输入的值进行判断,大的话把输入的值赋值给终止值,小的话赋值给起始值
        print('猜大了')
        stop = pass_sz
    elif pass_sz < sjs:
        print('猜小了')
        start = pass_sz
    else:
        print('你猜对了,暴揍中。。。。。。')            # 猜中就结束
        break
print('游戏结束了')
2.2.5 关键字
  1. break:在循环中起到中断循环的作用,退出循环

  2. continue:在循环中可以结束当前循环,回到循环条件,可开启下一轮的循环

2.2.6 while 和 for 循环的区别

​ while 使用在循环次数不确定的情况下

​ for 使用在循环次数确定的情况下

2.2.7 循环练习
# 1. 使用range生成1999年到2023年,获取其中闰年的个数

count1 = 0
for n in range(1999,2024):
    if (n % 4 == 0 and n % 100 != 0) or n % 400 == 0:
        count1 += 1
print(count1)

# =======================================================================================
# 2. 录入一个正整数,判断其是否是完美数【完美数:该数所有真因子[除了自身以外的因子]之和等于该数本身 比如6的真因子有1 2 3; 1+2+3 = 6  因此6是完美数】
#    提示: 我们需要获取1-N的数,找到N的真因数[相除余数为0],然后累加后判断

num1 = int(input('请输入一个正整数:'))
sum1 = 0
for i in range(1,num1):
    if num1 % i == 0:   # 判断数列里面任一数字能被num1整除的就是因子
        sum1 += i       # 把所有的因子累加
if num1 == sum :         # 判断
    print(num1,'是完美数')
else:
    print('不是完美数')
    
# =======================================================================================
# 3. 录入一个正整数,求该数的阶乘 [比如5的阶乘=`5*4*3*2*1`]

num2 = int(input('请输入一个正整数:'))
jc = 1
for i in range(1,num2 + 1):
    jc *= i
print(jc)

# =======================================================================================

# 4. 每个苹果0.8元,第一天买2个苹果,第二天开始,每天买前一天的2倍,直至购买的苹果个数达到不超过100的最大值。编写程序求每天平均花多少钱
# 自己写
sum2 = 0   # 总花销
count = 0 # 计天数
sl = 2   # 第一天购买数量
while True:
    sum2 = sum2 + sl * 0.8
    count += 1
    sl *= 2
    if sl > 100 :
        break
pj = sum2 / count
print(pj)

# 老师写
apple_count = 2  # 苹果个数初始值是2  代表第一天买了苹果
day = 1  # 天初始值从1开始
total = 0.8 * apple_count
# 开始每天购买循环操作  第二天购买的数量在100以下 才能进行购买
# 第二天的数量=前一天的2倍  在循环条件进行校验:第二天的数量是否是小于等于100的
while apple_count * 2 <= 100:
    # 每天购买的苹果数量
    apple_count *= 2  # 每天是前一天的两倍
    # 天数 +1
    day += 1
    # 金额
    total = total + apple_count * 0.8
# 当循环结束的时候 代表今天的苹果数量到达了目的需求
# 求平均金额
day_avg = total / day
print(day_avg)

# =======================================================================================

# 5. 我国1991年有11.6亿人口,要求根据人口平均年增长率0.4%,计算从1991年算起经过多少年后我国的人口增加15亿

r1 = 11.6
n1 = 0
while True:
    r1 = r1 * (1+0.004)
    n1 += 1
    if r1 > 15 + 11.6:
        break
print(n1)

# =======================================================================================

# 6. 输入两个正整数 n和 m,找它们的最大公因数
#   提示:获取最小的数 从最小的数递减,找到那个能同时整除的数即为最大公因数

n = int(input('请输入数字1:'))
m = int(input('请输入数字2:'))
if n < m :  # 先判断,谁小谁做范围
    min_num = n
else:
    min_num = m
for i in range(min_num,0,-1):         # 让数列倒序,就是从大到小排序,第一个复合条件的就是结果
    if min_num % i == 0 and min_num % i == 0: # 再判断找出他们的因数,因为是从大到小排列,
        print(i)
        break

# =======================================================================================

# 7. 找出100到999之间的水仙花数
for sz1 in range(100,1000):
    if (sz1 % 10) ** 3 + (sz1 // 10 % 10) ** 3 + (sz1 // 100 % 10) ** 3 == sz1 :
        print(sz1)
        
# =======================================================================================

# 8. 将数字的反转:比如 1234====4321
   提示:追个将每位上的数据变成个位上的数,获取个位上的数据,将记录者*10+个位数据

# 老师写的
'''
1,倒着取数,先取个位,再取十位··· 取数的动作是重复的,可以使用循环
2,每取一个数就放进容器里面,因为取下一个数的时候上一个数就往前进了一位,所以要*10,
3,一直到数字整除10为0的时候说明整个数的各个位置上的数字取完了
'''
sz = int(input('请输入一个数字:'))
bl = 0   # 新数的起始值为0
while True:
    szx = sz % 10
    bl = bl * 10 + szx
    sz = sz // 10
    if sz == 0:
        break
print(bl)

# 自己写的
ygs = input('请输入一个数:')
count = 0
sum3 = 0
for i in ygs:
    q1 = int(i)
    sum3 += q1 * 10 ** count
    count += 1
print(sum3)
2.2.8 for - else
语法:

for ~:

​	pass

else:

​	pass

解读:当for遍历完容器后,才会执行else
案例1# 1. 判断一个字符串中的数据是否是纯数字的
s = '153af55422'
for i in s:
	if not('0' =< i <= '9'):  # 当有一个符合条件的就不是纯数字
		print('不是纯数字')
        break
else:
	print('是纯数字')
案例2# 判断一个数是不是质数,质数:这个数的因数只有1和本身
num = int(input('请输入一个整数'))
for i in range(2,num):
	if num % i == 0:
		print('不是')
		break
else:
	print('是')
2.2.9 循环嵌套

​ 场景:重复的操作被重复的执行

​ 注意:尽量避免过多的循环嵌套,建议最多三层

# 百元白鸡问题:公鸡5元一只,母鸡3元一只,小鸡3只一元,找出适合的组合,必须三种类型都有
# 一百元只买公鸡能买25只
# 只买母鸡能买33只
# 只买小鸡可以买300
for g in range(1,26): # 公鸡范围
    for m in range(1,34): # 母鸡范围
        for x in range(3,101,3): # 小鸡范围,最多是一百只
            if g + m + x == 100 and g * 5 + m * 3 + x * 1//3 == 100:
                print(g,m,x)
                
# 简化:因总数是100只,所以小鸡可以写为100-g-m
for g in range(1,26):
    for m in range(1,34):
        x = 100 - g - m
        if x % 3 == 0 and g * 5 + m * 3 + x * 1//3 == 100:
            print(g,m,x)
# 白马百担问题:大马驮3担,中马驮2担,两匹小马驮1担,找出合适的组合
for d in range(1,34)for z in range(1,51):
        for x in range(2,101,2):
            if d + z + x == 100 and 3 * d + 2 * z + 0.5 * x == 100:
                print(d,z,x)
# 优化
for d in range(1,34):
    for z in range(1,51):
        x = 100 - d - z
        if x % 2 == 0 and d * 3 + z * 2 + x // 2 == 100:
            print(d,z,x)

3. 函数

3.1 定义:

​ 把常用的逻辑功能、独立的代码段进行封装, 在需要使用的时候直接调用即可,在调用之前不不允许的

3.2 语法:
语法:
    def 函数功能名(形式参数1, 形式参数2, ..形式参数n):
        函数功能体代码块
        return 功能运行结果的返回值
解读:
    1. def 定义函数的关键字
    2. 函数功能名 自己定义标识符名称 【遵守标识符定义规则】
    3. (形式参数1, 形式参数2, ..形式参数n)
        在参与功能运算的时候,有些数据值是动态、不确定的,将其设置成变量,给其设置一个标记名,等待调用函数			的时候赋予运算值,这个标记名就叫做形式参数,简称形参。如果没有未知动态的数据参与功能运算,形式			  参数就不用定义了,但是()必须有
    4. return 也是一个关键字
        功能是结束函数 并将结果返回到函数调用的位置
        如果一个函数没有返回值  return 是可以省略的  或者写为  return None
定义函数的时候如何分析
    1. 分析是否有未知项[不确定的数据 动态的数据]参与运算, 如果有的话 有几个?  【决定了形式参数的个数】
    2. 分析函数是否需要把运行结果返回  决定了是否有return返回值
3.3 参数
  1. def add(a,b)
    这个a,b就是位置参数,也叫必须参数;调用函数的时候必须给这些参数赋值,赋值的顺序和参数的顺序一致并且个数也要一致

  2. 默认参数

    定义参数的时候,有些参数已经被赋予了数值,这种参数就成为默认参数。

    特点:不给他赋值的时候,就使用默认值参与运算,赋值的话就用新值参与运算。

    适应的场景:有些参数不确定的,这个要传,有些参数大部分的场景下都是一样的,为了简单使用,在定义函数的时候传一个默认值。

    ※ 注意:默认参数定义的位置必须在必须参数的后面

  3. 关键字参数

    针对调用的时候,传递的实参格式可以写成形参=实参,这种格式称为关键字参数

    关键字参数在进行传值的时候,顺序可以发生变化【本质上是给变量名进行本质,找到匹配的变量名进行相应的赋值】

  4. 可变参数

    定义函数的时候,需要按照要求分析未知项的个数,来确定形参的个数,但是有些场景格式无法确定具体个数,比如封装一个函数:求读个数据的和,像这种形式参数的个数不能确定使用的可变参数,可变参数有两个格式:

    1. *args[args就是一个变量,可以定义成其他的名字]

      应用:多个位置参数的传值

    2. **kwargs[kwargs也是一个变量,可以定义成其他名字]

      应用:多个关键字参数的传值,例如:形参名=数据值

3.4 递归算法

算法:前辈们总结出来的解决某种问题的方法

递归:函数自己内部调用自己的结构称为递归,递归是循环的另一种表达方式,能形成递归代码的问题都是有规律的,找到规律后映射成函数

斐波拉契数列
公式:
F(1) = 1
F(2) = 1
F(n) = F(n-1) + F(n-2)  (n>=3)

F可以映射成函数名,小括号中的n映射成形参

注意:

  1. 写递归的时候一定要有已知项【代表递归的出口】,没有这个已知项相当于形成了死循环
  2. 递归的层次不能太深【递归就是在重复调用自己,本质上是一个循环,可能会造成程序卡顿和崩坏】
# 案例1:
# 使用递归求1-N的和
def qiuhe(N):
	sumn = 0
	for i in range(N,0,-1):
		sumn += i
	return sumn
# 案例2:
# 不要使用字符串,传递数据的位数,呈现指定的数据:
# 为1位数,呈现的2
# 为2位数  呈现的22
# 为3位数  呈现的222

'''
F(1) = 2
F(2) = 22 = 20 + 2 = F(1) * 10 + 2
F(3) = 222 = 220 + 2 = F(2) * 10 + 2
F(N) = F(N-1) * 10 + 2
'''
def get_num(n):
    if n == 1:
        return 2
    else:
        return get_num(n-1) + 2
# 拓展:吃豆子问题,一次只能吃1颗或者2颗,但是不能连续两次吃2颗, 问N颗豆子的吃完的方法有多少种
'''
1颗  1种  1
2颗  2种  11 2
3颗  3种  111  12 21
4颗  4种  1111 121 112 211
5颗  6种  11111 1211 1121 1112  212 2111 
6颗  9种  111111 12111 11211 11121 11112 1212 21111 2121 2112
'''
def douzi(n):
    if n <= 3:
        return n
    else:
        return douzi(n-1) + douzi(n-3)

3.5 函数嵌套
  1. 作用域:

    限定标识符可用性的使用的范围,称为作用域。就是变量可使用的范围

  2. 局部变量:

    在一般的语言中,一个代码块都是一个单独的作用域,比如分支结构/循环结构/函数,在python中只有函数有单独的作用域,这个作用域限定了函数定义的变量只能在函数内使用,这个变量称为局部变量。

    注意:形式参数也是局部变量,随着函数的定义才会出现。

  3. 全局变量:

    定义的这个变量可以再任意位置使用,称为全局变量

  4. 修饰符:global

    使用场景:要在函数中修改全局变量的值的时候,需要在函数中先对变量进行标记【将其标记为全局变量】,如果没有这个标记,解释器会认为这个变量是在函数内部重新定义的,与全局没有关系

  5. 函数嵌套

# 格式
def outher():
    print('外层函数')
    def inner():
        print('内部函数')
        
# 如何调用内层函数???
# 函数中有自己的作用域,内部定义的函数,只能在内部使用,外部无法直接调用,只能间接调用

# 调用内部函数格式1
def outher1():
    print('外层函数')
    def inner1():
        print('内部函数')
    # 调用
    inner1()
# 调用内部函数格式2
def outher1():
    print('外层函数')
    def inner1():
        print('内部函数')
    # 调用
    return inner1


# global关键字:要在函数中使用外部变量,需要在函数内部中标记,标识他为全局变量,
name = '小花'
def show_name():
    # 局部变量
    # 修改全局变量 global  告诉解释器 name 是全局name
    global name
    name = '小红'
    print(name)
# name '小花'
show_name()

# nonlocal 内部函数中 修改外部函数的变量
# 内层函数使用某个变量的时候  首先在内层函数中查找  如果有  使用内层函数的 如果没有
# 来到外部函数中查找  如果外部函数中有  使用外部函数的   如果外部函数没有
# 来到全局中查找  如果全局中有  使用 全局的  如果全局中没有  报错
age = 10
def outer():
    age = 18
    def inner():
        # 对外部函数中的age进行修改 nonlocal的作用 告诉解释器 变量来源于外部函数
        nonlocal age
        age = 20
        print(age)
    inner()
    print(age)

outer()
# 复习:

# 方法一:外层函数返回值为内层函数名,不能加括号
def outer():
	print('外层函数')
	def inner():
		print('内层函数')
	return inner
# 调用内存函数时
outer()() # 第一个括号是在调用外层函数,第二个括号代表调用内层函数

# 方法二:在函数内部直接调用内层函数,调用时必须加括号
def outer1():
    print('外层函数1')
    def inner1():
        print('内层函数1')
    inner()
    
outer() # 同时返回内层函数值
3.6 lambda函数

​ lambda:匿名函数,表达式:lambda 参数1,参数2:功能表达式

# 求两个数的和
f = lambda a,b:a + b
print(f(20, 30))

​ 主要应用场景:作为一个高阶函数的参数传入高阶函数内

3.7 高阶函数
  1. 一个函数作为另一个函数的参数进行传值,这个函数叫做高阶函数

    # 演化过程,求最值思路
    def max_values(l):
        max_value = l[0] # 先定义一个初始最大值
        for i in l[1:]: # 然后遍历后续的每一个元素
            if i > max_value:
                max_value = i # 找到大于的值然后赋值
        reture max_value    # 循环结束后返回最大值
        
        
    # 这是求序列里面值最大的思路,但是如果序列里的元素是字符串或者其他,要找长度最大的值就要封装一个新函数
    def max_len(l):
        max_value = l[0] # 先定义一个初始长度最大的值
        for i in l[1:]: # 然后遍历后续的每一个元素
            if len(i) > len(max_value):
                max_value = i # 找到大于的值然后赋值
        reture max_value    # 循环结束后返回最大值
        
    # 如果找末尾数最大,首位最大的数字就要封装更多的函数
    # 经过上面两个函数的对比,函数体内大致相同,只有判断时的元素不同,可以以函数的形式体现,所以可以使用高阶函数,在定义函数时,定义一个参数
    def max_(l,key=None):
        max_value = l[0]
        for i in l[1:]:
            if kye == None:
                compare_i,compare_max_value = i,max_value
            else:
                compare_i,compare_max_value = key(i),key(max_value)
            if compare_i > compare_max_value:
                max_value = i
        return max_value
    
    
    
    
  2. 内置高阶函数

    函数用法
    max,找最大值max(*arg,key=None),找最大值,可以传入一个序列,key默认是none,可以传入别的函数,也可以传入lambda
    min,找最小值min(*arg,key=None),找最小值,可以传入一个序列,key默认是none,可以传入别的函数,也可以传入lambda
    列表.sort,对列表排序列表.sort(key=None,reverse=False),列表排序,reverse默认是False,是升序,想要降序把reverse设置成True,仅对列表有效
    sorted,排序sorted(iterable,key,reverse),参数分别是,可遍历的序列,函数,降序还是升序,降序是False,升序是True,任何序列使用后都会返回为列表
    map,映射map(函数,序列),筛选和转化序列元素的过程,比如:将序列中的每一个元素扩大十倍map(lambda i:i * 10,序列)
    filter,筛选filter(函数,序列),筛选满足条件的元素,函数可以是lambda
    reduce,累计函数reduce(函数,序列),
    求和:求1-100数据的合计,reduce(lambda x,y : x + y,range(1,101) )
    阶乘:求10的阶乘,reduce(lambda x,y : x * y,range(1,11))
    拼接:把[‘a’,‘b’,‘c’]使用‘+’拼接起来,reduce(lambda x,y : x+ ‘+’ + y,[‘a’,‘b’,‘c’])
3.8 常用的内置函数
  1. open文件操作
# open打开文件操作 open(文件路径,操作方式(读r,写 w或a),编译方式 utf-8)
# 注意:如果操作的是图片等字节数据的文件,不需要编译方式
fp = open('./静夜思.txt','r',encoding='utf-8')
# 操作方式是r时,如果没有找到文件会报错;如果是w 或 a ,如果没有此文件会创建一个
data = fp.read() # 或write()
# 关闭通道
fp.close()
  1. with语句
# with语句,上下文管理语句,不需要手动关闭通道;可以同时操作多个文件
with open(文件,操作方式,编译方式) as 变量1open(文件2,操作方式,编译方式) as 变量2:
	data = 变量1.read()
    变量2.write(data)

4. 容器

4.1 字符串

4.1.1 概念及特点
  1. 概念:包含零个或多个字符的有序不可变的序列

  2. 特点:

    有序性:添加顺序和显示顺序一致,添加字符时,会设置编号,编号从0开始,被称为索引、下标、脚标。

    不可变性:在内存地址不变的情况下,字符串的内容不允许改变,如果内容变了,内存地址一定变了。

4.1.2 定义字符串
  1. 使用引号包含:一堆单引号或双引号或三引号,预编译文本可以保持文本原有格式输出
  2. 使用str()函数构造
4.1.3 索引和切片
  1. 索引:

    可以通过索引获取对应的字符,字符串[索引]

    索引分为两种:

    正向索引从左到右,从0开始到len(字符串)-1
    负向索引从右到左,从-1到-len(字符串)
  2. 切片

    字符串[起始索引:结束索引:步长],不包含结束索引

    表达形式:以s='abc’字符串为例含义
    s[0:3:1]、s[0:3]、s[:]复制字符串,步长省略默认为1,起始和终止位置省略默认是全部
    s[0:3:-1]、s[::-1]反转字符串
    s[0:-1]起始和结束索引可以同时使用正向索引和负向索引
    注意步长为负数时,起始位置要在结束位置的右边
4.1.4 转义字符

转义符:\可以是某些符号转变为其他意思,

符号表达含义
\nnewline,换行符,将光标定位到下一行的行首
\rreturn,回车,和换行符的区别在于讲光标定位在本行的行首
\ttab,横向制表符
\v纵向制表符
\uunicode编码,编译后边的文本
\f翻页
\’
\""
\\\,不需要转义的时候多加一个转义符

修饰符:r/R

防止转义,在字符串的前面加

4.1.5 字符串的操作
1.查找

字符串.find(子串,字符串起始位置,字符串结束位置),查找子串第一次出现的位置

字符串.rfind(子串,字符串起始位置,字符串结束位置),查找子串最后一次出现的位置

字符串.index(子串,字符串起始位置,字符串结束位置),查找子串第一次出现的位置

字符串.rindex(子串,字符串起始位置,字符串结束位置),查找子串最后一次出现的位置
index 和 find 的区别是 在没有找到时 index报错 find返回-1

字符串.count(子串,字符串起始位置,字符串结束位置),统计子串出现的次数,不重叠计数

2.转换
  1. 将字母变大写:字符串.upper()
  2. 将字母变小写:字符串.lower()
  3. 将大写变小写,小写变大写:字符串.swapcase()
  4. 首字母大写,其他小写:字符串.capitalize()
  5. 每个单词首字母大写:字符串.title() 【单词:没连接在一起的就视作一个新的单词】
3.判断
  1. 判断是否是纯数字,字符串.isdigit()
  2. 判断字符串的内容是否为纯字母,字符串.isalpha(),不单指英文字母
  3. 判断字符串的内容是否为数字或字母,字符串.isalnum(),不单指英文字母
  4. 判断字符串中的字母是否为大写,字符串.isupper(),只判断字符串中的英文字母
  5. 判断字符串中的字母是否为小写,字符串.islower(),只判断字符串中的英文字母
  6. 判断字符串中的内容是否来源于ASCII码表,字符串.isascii()
  7. 判断字符串中的每个英文单词是否是首字母大写开头的,字符串.istitle()
  8. 判断是否以指定内容开头,字符串.startswith(指定内容),指定内容可以是容器:表示是否是容器中的一个
  9. 判断是否以指定内容结尾,字符串.endswith(指定内容),指定内容可以是容器:表示是否是容器中的一个
4.编码和解码
  1. 编码:utf-8或gbk

    语法:字符串.encode(‘编码方式’)

  2. 解码:

    语法:字符串.decode(‘编码方式’)

  3. 注意:

    1. 用什么编码方式编码,就用什么解码
    2. ASCII码中的128个符号,编码前后内容不变
  4. 应用场景:

# 判断字符串是否是纯英文组成
s = '你好'
print(s.encode('utf-8').isalpha()) # False
# 判断字符串是否是纯数字组成
s = 'abc123'
print(s.encode('utf-8').isalnum()) # False
# 判断是否是数字和字母组成 1.满足数字或字母  2.不是纯数字  3.不是纯字母
s = 'abc123'
sn = s.encode('utf-8')
print(sn.isalnum() and not(sn.isdigit()) and not(sn.issalpha()))
5.切割和拼接
  1. 切割:返回值是一个列表

    从左切割语法:字符串.split(切割内容,切割次数),切割内容默认为空白字符:\n,\t,\f,\v,\r,空格。所以空格不等于空白字符

    从右切割语法:字符串.rsplit(切割内容,切割次数)

  2. 拼接:

    语法:拼接符号.join(序列),序列内容必须都是字符串。

s = 'how are you'
# 切割
s1 = s.split()
print(s1)  # ['how', 'are you']
# 拼接
print(' '.join(s1)) # how are you
6.替换
  1. 语法:字符串.replace(旧内容,新内容,替换次数)
  2. 次数不指定默认替换全部
s = 'how are you'
# 把空格替换成_
print(s.replace(' ', '_')) # how_are_you
7.去除
  1. 概念:去除字符串两端的指定内容
  2. 语法:字符串.strip(指定内容),字符串.lstrip(指定内容):只去除左边,字符串.rstrip(指定内容):只去除右边
  3. 逻辑:遍历字符串,如果在指定内容中,那就去除,不在的话停止去除

4.2 列表

4.2.1 概念及特点
  1. 概念:包含0个或多个元素的有序可变序列,元素之间用逗号隔开,数据类型可以是多种

  2. 特点:

    有序:

    1. 添加顺序和显示顺序一致,编号从0开始
    2. 可以通过索引和切片获取对应的元素

    可变:

    1. 内存地址不变的情况下,数据可以发送改变【增加,删除,修改】
    2. 列表里是空格的也为空列表
    3. 非空列表:如果用list(数据)构造,数据必须是可遍历的,数据不能乱写,如果写一个字符串,会把字符串中每个元素放在列表中
4.2.2 定义列表

使用[],使用 list() 函数构造

4.2.3 索引和切片、修改
  1. 索引和切片与字符串一样

  2. 修改:

    当获取的元素是连续的,可以随意赋值,不要求数量

    当获取的元素不是连续的,需要按数量赋值,元素个数不一样会报错。

4.2.4 运算符
符号含义
+合并两个列表中的数据
*将列表中的数据重复
+=在当前列表的基础上合并序列
注意:
1.列表内容可变,地址不会发生变化
2.合并序列【不限于列表,字符串,元组,range等】
*=在当前列表的基础上重复
比较运算符比较规则:相同位置的元素逐个比较,直到比较出大小
成员运算符判断数据是否为列表中的元素
4.2.5 遍历列表
  1. 直接遍历列表:
  2. 遍历索引:
  3. 枚举遍历:

以上方式均和字符串一样

4.2.6 列表推导式

对遍历列表的一种简写:[返回结果 for 变量 in 列表 if 判断条件]

4.2.7 列表的操作
  1. 添加元素

    函数性质
    列表.append(元素)添加在列表最后面
    列表.insert(索引,元素)在指定的索引下添加元素(插队)
    列表.extend(序列)合并序列内容,等同于 +=
    append和extend的区别append只能追加元素,extend是合并序列的元素
  2. 删除元素:

    函数性质
    列表.pop(索引)删除指定索引下的元素,不写索引的话默认删除最后一个元素
    列表.remove(元素)删除指定的元素,如果列表中有多个,只能删除找到的第一个;如果没有找到该元素就报错
  3. 修改元素:

    使用切片或索引获取指定元素,然后赋值

  4. 获取操作

    函数含义
    列表.index(元素,起始位置,结束位置)获取该元素在列表中第一次出现的位置,超过范围会报错
    列表.count(元素)获取该元素在列表中出现了几次
    和字符串的区别没有find操作
  5. 其他操作

    函数含义
    列表.copy()赋值列表,等同于:列表[:]
    列表.reverse()反转,类似于:列表[::-1]
    区别:列表[::-1]产生一个新列表,不修改原有列表;列表.reverse()修改原有列表
    列表.sort()排序,直接修改原有列表。默认是升序,降序的话是:列表.sort(reverse=True)
4.2.8 列表的易错点
  1. 列表是可变的,遍历列表的同时,对满足条件的数据进行移除,可能会遍历不到一些元素

  2. 解决方案:复制一份列表,遍历复制的列表,操作原列表

    # 列表中存储学生的成绩`scores = [98, 67, 56,56, 77, 45, 81, 54]`,去除掉不及格的成绩
    scores = [98, 67, 56,56, 77, 45, 81, 54]
    for i in scores[:]:
        if i < 60:
            scores.remove(i)
    print(scores)
    
4.2.9 二维列表

就是列表里面套列表

l = [[1,2,3],[4,5,6]]
# 获取1这个元素
l[0][0]
4.2.10 查找算法
  1. 顺序查找

    直接遍历,把复合条件的元素输出

  2. 二分法查找

    要求:数据一定是排好序的,

    逻辑:首先确定好查找范围【用索引来确定】,确定在中间位置,将中间位置的数据和待查找的数据作比较

    nums1 = [15, 18, 23, 27, 36, 45, 49, 53, 57, 68, 72, 81]
    # 查找值
    key = int(input('请输入要查找的值:'))
    # 定义起始位置
    start = 0
    # 结束位置
    stop = len(nums1)-1
    # 中间位置
    mid = (start + stop)//2
    
    while nums1[mid] != key:
        # 先判断查找值在那范围
        if key > nums1[mid]:
            start = mid + 1
        else:
            stop = mid - 1
        # 范围确定后,再判断这个值存在吗,如果范围长度<0,不存在,结束循环
        if start > stop:
            print('无此数据')
            break
        # 如果存在,重新赋值中间值
        else:
            mid = (start + stop) // 2
    # 循环结束后,输出位置
    else:
        print(mid)
    
4.2.11 排序算法
  1. 冒泡排序:

    # 排序规则:每次都从索引为0的位置开始,相邻的两个元素两两比较,如果前者大于后者,则交换位置
    # l = [46,28,59,17,38]
    # 第一次:[28,46,17,38,59] 产生最大值
    # 第二次:[28,17,38,46,59] 产生第二大的值,不需要比较最大值
    # 第三次:[17,28,38,46,59] 产生第三大的值,不需要比较后两个
    # 第四次:[17,28,38,46,59] 就剩前两个没有比较,比较前两个
    # 总共比较len-1次
    
    # 外层循环控制循环次数
    # 内层循环控制每次都从索引为0开始
    l = [46,28,59,17,38]
    for i in range(1,len(l)):
        for j in range(0,len(l)-i):
            if l[j] > l[j+1]: # 升序
                l[j],l[j+1] = l[j+1],l[j]
    print(l) # [17,28,38,46,59] 
    
    
  2. 选择排序

    # 排序规则:升序
    # 从未排序的序列中找到最小值的位置,和索引为0的交换
    # 再从未排序的序列中找最小值的位置,和索引为1的交换
    # 再从未排序的序列中找最小值的位置,和索引为2的交换
    # 重复以上过程
    # l = [46,28,59,17,38]
    # 第一次 [17,28,59,46,38]
    # 第二次 [17,28,59,46,38]
    # 第三次 [17,28,38,46,59]
    # 第四次 [17,28,38,46,59]
    # 总共比较len-1次
    
    l = [46, 28, 59, 17, 38, 50, 60, 10, 12]
    # 外层循环控制循环次数
    # 内存循环控制找最小值的索引和要放的位置交换,从0开始
    for i in range(0,len(l)-1):
        min = i # 最小值所在的位置
        for j in range(i+1,len(l)): # 假设i是最小的,遍历后面的值和i比较,如果小就交换位置
            if l[j] < l[min] :
                min = j
        # 遍历结束后,找到最小的位置后交换位置
        l[min],l[i] = l[i],l[min]
    print(l)
        
    
  3. 插入排序

    # 排序规则:升序
    # 相当于起牌,第一张牌不需要排序,从第二张牌开始,拍好手里已有的牌
    # 第1次从索引为1的开始和手里索引为0的比较,如果小于前面的,互换
    # 第2次从索引为2的开始和手里索引为1,0的比较,如果小于前面的,互换
    # 第3次从索引为3的开始和手里索引为2,1,0的比较,如果小于前面的,互换
    # 重复执行,循环次数为len-1
    l = [46, 28, 59, 17, 38, 50, 60, 10, 12]
    for i in range(1,len(l)): 
    # 外层循环控制循环次数,且控制从索引为1的开始遍历
    	for j in range(i,0,-1):# 倒序查找
            if l[j] < l[j-1]:
                l[j],l[j-1] = l[j-1],l[j]
    print(l)
        
    
  4. 快速排序

    # 排序规则:升序
    # 封装一个函数,使用递归,如果列表长度小于2,不需要排序,返回原列表
    # 如果长度大于2,首先设置索引是0的为基准值,大于基准值的空列表,小于基准值的空列表,
    # 然后从索引为1的开始遍历,小于基准值的添加在小于的列表,大于的添加在大于的列表zh
    # 返回为 小于列表 + [基准值] + 大于列表
    l = [46, 28, 59, 17, 38, 50, 60, 10, 12]
    def kuaipai(l):
        if len(l) < 2:
            return l
        else:
            jizhunzhi = l[0]
            da = []
            xiao = []
            for i in l[1:]:
                if i < jizhunzhi:
                    xiao.append(i)
                else:
                    da.append(i)
                    
             return kuaipai(xiao) + [jizhunzhi] + kaipai(da)
    print(kuaipai(l))
    

4.3 元组

4.3.1 概念及定义
  1. 数据表示**()**,和列表类似,可以看做不可变的列表
  2. 存储0个或者多个有序不可变的序列,元素之间使用逗号分隔
  3. 有序:有索引,有切片
  4. 不可变:内存地址不变的情况下,数据不允许发生改变
  5. 建议:数据已经确定时,建议使用元组,存储空间比列表小
4.3.2 定义元组
  1. 使用**()**包含,当元素只有一个的时候,元素后面跟一个逗号
  2. 使用 **tuple()**函数构造
4.3.3 索引和切片
  1. 和字符串一样
  2. 只能获取,不能修改
4.3.4 运算符
符号含义
+合并两个元组
*元祖中的元素重复
+=仅能合并元祖,不能合并其他类型序列,并产生一个新元祖
*=重复,且产生一个新元组
比较和字符串,列表一样
成员和字符串,列表一样
4.3.5 遍历

​ 和字符串,列表一样有三种遍历方式

4.3.6 元组的操作

​ index,查找元素第一次出现的位置

​ count,统计元素出现的次数

4.3.7 拆包和装包
  1. 装包:

    把多个值付给一个变量,把多个值打包成一个元组赋值给这个变量,这个过程叫做装包

    a = 10,20,30     # a = (10,20,30),类型是一个元组
    
  2. 拆包:

    把一个序列赋值给多个变量,这个过程叫做拆包

    # 当变量数和序列元素数量一样时,按顺序赋值
    a,b,c = (1,2,3)   # a=1 b=2 c=3
    a,b=[1,2]        # a=1 b=2
    # 当变量数比序列元素数量多时,会报错
    # 当变量数比序列元素数量少时,需要让某一个变量接收多个值
    # 赋值形式
    a,*b = (1,2,3)  # a=1 b=[2,3]
    # 当某个变量被赋多个值时,以列表的形式呈现
    

4.4 字典

4.4.1 概念及定义
  1. 标识是**{}**,数据是以键值对的形式存在的
  2. 存储0个或者多个键值对的无序可变序列
  3. 无序:没有索引,但是键相当于索引,可以通过键找到对应的值,键不能重复,要求是不可变数据类型,字符串、元组、整型、浮点、布尔均可
  4. 可变:内存地址不变的情况下,数据可以发生变化。
4.4.2 定义字典
  1. 使用**{}**包含
  2. 使用**dict()**函数构造
4.4.3 运算符

​ 仅支持成员运算符

4.4.4 字典的操作
  1. 增加

    1. 增减键值对:字典[键]=值,如果键不存在,则为增加键值对,如果存在,则为修改值
    2. 函数添加:字典.setdefault(j键,值),如果键不存在,则为增加键值对,如果存在,则没有反应
    3. 合并序列:字典.updata(序列),序列可以是字典,列表,元组,但是必须是二维数据,或者是变量=值
  2. 删除:根据键来删除

    1. del 字典[键]
    2. 字典.pop(键)
    3. 清空:字典.clear()
  3. 修改

    ​ 字典[键] = 值

  4. 获取

    1. 字典[键],键存在,获取对应的值,键不存在,报错

    2. 字典.get(键),键存在,获取对应的值,键不存在,返回None

      如果键不存在,可以返回自定义数字:字典.get(键,自定义数字)

    3. 获取字典的所有的键:字典.keys()

    4. 获取字典的所有的值:字典.values()

    5. 复制:字典.copy()

    6. 快速构建字典:dict.fromkeys(键序列,值),不写值默认为None

4.4.5 遍历
  1. 直接遍历:实际上是遍历字典的键

  2. 遍历字典的值:字典.values()

  3. 遍历键值对:字典.items()

    for k,v in l.items():
        print(k,v)
    
4.4.6 字典推导式

类似列表推导式

# 语法:
{返回结果 for k,v in 字典.items() if 条件}
4.4.7 字典的应用
  1. 词频统计

    s = 'hdjgkjagsdfhjkgfsdhjagsfhjgfkjh'
    # 统计没有字符出现的次数,以键值对发形式体现
    d = {}
    for i in s:
        if i not in d:
            d[i] = s.count(i)
    print(d)
    
  2. 获取

    import string
    s = 'HFjdh57256FHF'
    # 获取大写,小写,数字的个数,以键值对的形式体现
    
    # 方式一
    d = {'大写'0'小写':0,'数字':0}
    for i in s:
        if i in string.ascii_uppercase:
            d['大写'] += 1
        elif i in string.ascii_lowercase:
            d['小写'] += 1
        elif i in string.digits:
            d['数字'] += 1
    print(d)
    # 方式二
    da = len([i for i in s if i in string.ascii_uppercase])
    xiao = len([i for i in s if i in string.ascii_lowercase])
    shu = len([i for i in s if i in string.digits])
    d = {'大写':da,'小写':xiao,'数字':shu}
    
4.4.8 字典和列表的结合
# 列表中的元素是字典
students = [
    {'学号': 1, '姓名': '乐乐', '年龄': 12, '性别': '男', '成绩': 71},
    {'学号': 2, '姓名': '倩倩', '年龄': 11, '性别': '女', '成绩': 83},
    {'学号': 3, '姓名': '苗苗', '年龄': 13, '性别': '女', '成绩': 98},
    {'学号': 4, '姓名': '旭旭', '年龄': 12, '性别': '男', '成绩': 65},
    {'学号': 5, '姓名': '花花', '年龄': 11, '性别': '男', '成绩': 66}
]

# 获取性别是男的学生信息
print([i for i in students if i['性别'] == '男'])

4.5 集合

4.5.1 概念及特点
  1. 标识:{},存储多个确定且我不重复元素无序可变的序列
  2. 特点:无序,可变,元素唯一
4.5.2 定义集合
  1. 使用{}包含
  2. 使用set()函数构造
4.5.3 运算符
符号含义
&求两个集合的交集,集合间共有的部分
|求两个集合的并集,集合合并不重复部分
^求两个集合的对称差集,去除集合间共有的部分,并集-交集
-求两个集合的差集,去除一个集合在另一个集合中交集的部分
&=在原集合上变化
|=在原集合上变化
^=在原集合上变化
-=在原集合上变化
比较运算判断子集和真子集,子集:自己的全部或部分,真子集:自己的部分
>判断后者是前者的真子集
>=判断后者是前者的子集
<判断前者是后者的真子集
<=判断前者是后者的子集
==或!=判断两个集合的元素是否相等或不相等,和顺序无关
成员运算符in not in
4.5.4 遍历

只能直接遍历元素,构造空集合是,必须用函数构造,d = set()

4.5.5 集合的操作
  1. 增加

    增加元素:集合.add(元素),如果存在,不做反应,不存在时添加

  2. 删除

    集合.remove(元素),如果存在,删除,不存在时报错

    集合.discard(元素),如果存在,删除,不存在不做反应

  3. 其他:

    1. 赋值:集合.copy()
    2. 判断两个集合是否不存在交集:集合1.isdisjoint(集合2),返回布尔值

4.6 深浅拷贝

一层数据,没有区别

核心区别在于内层是否产生新的

  1. 深拷贝

    变量 = copy.deepcopy(序列)

    两个数据完全没有影响

  2. 浅拷贝

    变量 = copy.copy(序列)

    新久序列外层添加元素是,互相没有影响,当任意一个内层添加元素时,两者都会发生变化

  3. 变量赋值 变量1=变量2

    本质是把另一个变量的地址赋值给了变量,传递的是地址,两者本质是一个东西,所以两者无论哪个改变,其中一个也会跟着改变

5. 包和模块

5.1.1 模块

5.1.1.1 定义

一个python文件就是一个模块模块分为三类:

  1. 系统模块:python环境自带的

  2. 三方模块:别人写好上传到网站的,如果想使用,需要下载【pip install 模块名】,常用的pip指令

指令作用
pip list查看当前环境下的三方模块
pip install 模块名下载模块
pip uninstall 模块名卸载模块
pip install 模块名 == 版本号下载指定版本的模块
  1. 自定义模块:自己写的模块
5.1.1.2 导入

方式有三种:

方式使用方法
import 模块名模块名.操作
from 模块名 import 操作名直接使用操作名
from 模块名 import *直接使用操作名,*代指所有的操作名,但是模块中使用_ _ all _ _规定了对外公开的内容,只能回去规定中的内容
5.1.1.3 常用模块
1.string模块: import string
序号操作含义
1string.octdigits获取8进制的所有字符集
2string.hexdigits获取16进制的所有字符集
3string.whitespace获取所有空白字符集
4string.punctuation获取所有标点符号
5string.printtable获取所有可打印的内容,数字,英文字母,标点符号,空白字符
2.random模块: import random
序号操作含义
1randmon.randint(初始值,结束值)在范围内,生成随机数,包含结束值
2random.choice(序列)随机选择序列中的一个
3random.choice(序列,k=值)随机选择多个,有放回式的随机选择,k可以大于序列长度
4random.sample(序列,k=值)随机选择多个,无放回式的选择多个,k值不能大于序列长度,如果容器本身元素不重复,取值也不会重复
5random.random()获取[0,1)范围内的小数,不包含1
6random.random()*10获取1-10中的小数
7random.randrange(1,10)随机获取范围内的整数
8random.shuffle(序列)打乱原列表,不能直接打印,
3.path模块(数学模块): import path
序号操作含义
1math.fabs(序列)求绝对值,返回结果是浮点类型,与内置函数abs()的区别是abs返回是原有数据类型
2math.pow(底数,指数)求幂数,返回为浮点类型,与内置函数pow()的区别是pow返回是原有数据类型
3math.fsum(序列)求和,无精度损失求和,sum()是有精度损失求和
4math.ceil(小数)向上取整,获取当前数据大一点的整数
5math.floor(小数)向下取整,获取当前数据小一点的整数
6math.factorial(数据)求阶乘
7math.pi圆周率的值
8math.log(指数,底数)求对数,
math.log2(数据):求以2为底数的对数
math.log10(数据):求以10为底数的对数
9math.sin(数据)三角函数,math.cos(数据)等等
10math.radians(角度)
math.degrees(弧度)
math.radians(角度):角度转弧度
math.degrees(弧度):弧度转角度
4.calendar模块(日历模块): import calendar
序号操作含义
1calendar.calendar(年)获取某年的日历
2calendar.mouth(年,月)获取某年某月的日历
3calendar.isleap(年)获取某年是否为闰年
4calendar.leapdays(起始年,结束年)获取两个年份之间所有闰年的个数,不包含结束年份
5calendar.weekday(年,月,日)获取某年某与某日是星期几,星期范围是0-6,0标识星期一
6calendar.monthrange(年,月)获取某年某月有多少天,返回是一个元组,(这个月的第一天是星期几,这个月的天数)
5.time时间模块:import time
序号操作含义
1time.sleep(秒)休眠几秒
2time.time()返回时间戳,从1970年到现在的所有的秒数
3time.localtime(时间)获取指定时间的时间元组,不写时间返回现在的时间元组。time.struct_time(
tm_year=2023,:年
tm_mon=10, :月
tm_mday=13, :日
tm_hour=14,: 时
tm_min=6, :分
tm_sec=46, :秒
tm_wday=4,:星期几
tm_yday=286,:这一年的第多少天
tm_isdst=0:是不是夏令时)

获取其中某一个值:time.localtime().tm_yday
4time.strftime(格式化后的展现形式,时间元组)时间格式化
%Y:年,%m:月,%d:天,
%H:时、24小时制的小时,
%I:时、12小时制的小时
%p:am上午、pm下午
%M:分,%S:秒,%w:星期
%j:这一天是这一年的第多少天
5time.strptime(字符串形式的时间,解析后的格式)时间反格式化,将字符串时间转化成时间元组。解析的时候要和字符串形式的时间要一一对应
time.strptime(字符串形式的时间,解析后的格式).tm_yday:获取知道时间在这一年的第多少天
6time.mktime(时间)获取这个时间对应的时间戳
6.datetime时间模块:import datetime
序号操作含义
1datetime.date(年,月,日)构造时间:年月日
2datetime.time(时,分,秒)构造时间:时分秒
3datetime.datetime(年,月,日,时,分,秒)构造时间,年月日时分秒
4datetime.datetime.now()获取当前时间:年月日 时分秒
5t=datetime.datetime.now()t.year:获取当前年份
t.month:获取当前的月份
t.day:获取当前的天
t.hour:获取当前的时
t.minute:获取当前的分
t.second:获取当前的秒
t.date():获取年月日
t.weekday():获取星期几
6时间.strftime(格式化模版)时间格式化
7datetime.datetime.strptime(字符串时间,格式化模版)时间反格式化
8时间.timestamp()返回时间戳
9datetime.datetime.fromtimestamp(时间戳)返回时间戳对应的时间对象,年月日 时分秒
10time1 = datetime.datetime(2023,10,10,10,10,10) time2 = datetime.datetime(2023,8,9,9,9,9) time3 = time1-time2求时间差,返回是天数和时分秒
time3.days获取天数
time3.seconds获取除天数以外的秒数有多少
time3.total_seconds
获取某年某月的的第多少天:time3.days + 1
11时间+datetime.timedelta(dats=天数,hours=小时数,weeks=周数)获取几天后或者几周后的时间
7.os操作系统模块:import os
序号操作含义
1os.getcwd()获取当前的绝对路径
2os.mkdir(路径)创建文件夹,如果路径不存在则创建,如果存在则报错
在实际应用中首先判断是否不存在:
if not(os.path.exists(路径)):
os.mkdir(路径)
3os.rmdir(路径)删除文件夹,文件夹下无文件且存在则删除,否则报错
4os.makedir(路径)连续创建多级文件夹,如果存在则报错,不存在则创建
5os.removedirs(路径)如果不存在则报错,存在则删除
6os.remove(文件路径)删除文件
7open(路径,操作方式,编译方式)创建文件
8os.rename(原名称,新名称)重命名
9os.rename(‘文件’,’文件路径到文件名‘)剪切
10os.listdir(路径)获取该路径下的所有文件和文件夹的名称,返回列表
8.os.path操作路径模块:import os
序号操作含义
1os.path.exists(路径)判断该路径是否存在
2os.path.isfile(路径)判断该路径是否是文件
3os.path.isdir(路径)判断该路径是否是文件夹
4os.path.isabs(路径)判断该路径是否是绝对路径
5os.path.abspath(相对路径)获取该相对路径对应的绝对路径
6os.path.basename(绝对路径)获取最后一级的路径名
7os.path.dirname(绝对路径)获取除最后一级外的路径
8os.path.splitext(绝对路径)返回一个二元组,元素1表示路径名字,元素2表示文件后缀名
9os.path.join(原路径,新路径)路径拼接

5.1.2 包

比较特殊的文件夹,和普通文件夹的区别,包中自带一个文件__ init __.py 文件。

__ init __.py 的作用:

  1. 分类管理

  2. 增大命名空间

  3. 包的调用

    ------文件
    import1.2.3.模块名 as 别名
    # 使用
    别名.操作
    

6. 迭代器

6.1 可迭代对象

Iterable:可迭代,可遍历的。能够使用for-in循环遍历的就是可迭代对象,实现了一个可迭代协议__ iter __;容器性数据都是可迭代对象

6.2 迭代器

Iterator,一定是可迭代对象,但是可迭代对象不一定是迭代器。不仅实现了可迭代协议__ iter __ ,而且实现了__ next __ 协议,只能往后遍历,取完为止,不能重复遍历;map,zip,filter构建的是迭代器

  1. 可迭代对象变成迭代器

    iter(可迭代对象)

    l = [12,13,41,51]
    iter(l)
    
  2. 判断

    判断是否是可迭代课迭代器类型数据

    from collections.abc import Iterable,Iterator
    # 判断是否是指定数据类型 isinstance(序列,类型)
    print(isinstance('abc',Iterable)) #True 
    
  3. 特点:可以通过遍历取数据,也可以通过next()取数

6.3 生成器

批量生成数据,类似于列表推导式,区别在于,生成器使用一个产生一个,列表推导式是全部产生,生成器省内存。生成器一定是迭代器,但是迭代器不一定是生成器

  1. 生成器的产生
    1. 使用生成器表达式:(i for i in 容器 if 判断条件)

    2. 函数构造,在函数体内使用yield关键字,作用是暂停函数,产出结果。当使用yield关键字后,调用函数,不会进入函数,而是产生一个生成器对象,获取生成器中的数据的时候,才会进去函数,执行函数中的内容,当函数执行到yield的时候,暂停并把数据返回,下一次获取的时候,从上一次暂停的位置开始执行,到yield的暂停返回数据。

      def get_num():
          for i in range(1, 3):
              # return i  # 结束函数 并把结果返回到调用位置
              yield i  # 暂停函数  产出的i
              print('-------------------------')
      print(get_num())
      

7. 面向对象

python语言其中的一个特点:面向对象的编程语言,这是一种更符合人类特点的思维方式。

7.1 类和对象的定义

  1. 类:是一中抽象的概念,同一类对象的特点和行为进行归纳,形成一个抽象的概念

  2. 对象:是具体的实例,在该类的特征下,实实在在存在的实体。

    str:字符串 --- 数据类型 ------ 使用一对引号包含
    'abc'     --- 对象 --- 具体实物
    
    

7.2 构建类

  1. 把对象的共同的特征和行为进行提取,对这些信息进行描述

  2. 特征:称为对象的属性。

  3. 行为:称为对象的方法。

    狗:
    	特性:有名字,年龄,性别
    	行为:看家,旺旺叫,拆家
    
  4. 构建类的语法:

    class 类名:
    	# 定位属性,初始化,作用:给对象进行初始化特征并赋值
    	def __init__(self,参数1,参数2):
    		self.特征名 = 参数1
            self.特征名 = 参数2
        # 定义行为,方法
        def 函数名(self,参数1,参数2)pass
    
  5. 注意事项:

    calss : 定义类的关键字
    类名 : 满足变量命名规范,建议使用大驼峰
    
    __init__ : 初始化方法,给类的对象进行初始化特征并赋值
    
    self : 指的是调用该方法的对象,默认self,可以改成其他,建议使用self,使用时:self.特征 = 参数
    
    def 函数名 : 定位的行为
    

7.3 类属性和对象属性

  1. 对象属性:

    定义在__ init __ 函数中,对象属性和对象是一起建立的,对象被销毁时,对象属性一起被销毁;对象之间相互独立,互不干扰;对象属性只能对象调用。

  2. 类属性:

    直接定义在类中,被所有对象所共有,是该类对象的共同特征,类属性可以类来调用,也可以对象来调用,但是只能类来修改

7.4 常见的魔术方法

不需要手动调用,在适当的时机进行自动调用,魔术方法使用__ 内容 __,系统内置好的

方法作用
__ new __构造方法,实例化对象的
__ init __初始化方法,对对象进行初始化特征并赋值,
执行时机在__ new __ 之后
__ del __析构方法,没有任何引用的时候,内存的释放和回收
__ str __字符串展现方法
__ repr __在列表中展现方法

7.5 类和类之间的关系

7.5.1 聚合关系

一个类中包含了另一个类的实例对象,体现的是整体和部分的关系

  1. 教室类

    属性:教室名字,学生们

    行为:添加学生,删除学生,根据学生成绩排序,获取学生信息

  2. 学生类

    属性:姓名,年龄,性别,分数

7.5.2 继承关系

将两个或多个普通类的共同的属性和行为提取,提取到一个共同类的中,使用继承的语法,使用共同类中的信息,这个过程称为继承

class 类名(父类名): -- 继承语法
	pass

父类和子类的关系:子类继承自父类的关系,父类派生子类

  1. 子类中的方法的重写

    一般情况下,父类的内容子类可以直接使用,当子类和父类有部分或者完全不一样就要重写方法,子类重写方法后,将父类的进行掩盖,子类调用的时候只能调用到重新写后的

  2. 多继承:

    属性默认继承第一个父类的属性,如果想同时集成多个父类的属性,需要书写

    class F1:
        def __init__(self,name,id):
            self.name = name
            self.id = id
         def eat(self):
            print('吃')
    class F2:
        def __init__(self,gender):
            self.gender = gender
        def sleep:
            print('睡')
    class S(F1,F2):
        def __init__(self,name,id,gender):
            F1.__init__(self,name,id)
            F2.__init__(self,gender)
    s1 = S('小红'1'女') # 这样建立的s1对象就可以使用两个父类 属性了 
    

7.6 面相对象的三大特征

  1. 封装

    在类中,对属性或者方法进行隐藏,在外部进行实例化对象之后,不能通过对象.属性或者对象.方法进行获取或者调用,但是流出对应的封装好的访问接口

    class Fu:
    	age = 10
    	def __init__(self,name):
            self.name = name
    # 建立一个对象可以调用类属性age,还可以通过类调用来修改,如果隐藏,这样写,把要隐藏的属性,前面加上两个下划线
    class Fu:
    	__age = 10
    	def __init__(self,name):
            self.name = name
        # 建立一个修改age的接口
        # 第一个参数是类:cls
        # 需要一个装饰器装饰一下
        @classmethod
        def get_age(cls,new_age)
        	cls.__age = new_age
            retunr cls.__age
    
  2. 继承

    简化代码

  3. 多态

    在继承的基础上,数据呈现出来的不同形态

    实现多态的条件,第一是攒竹集成关系,第二在子类中重写了父类的方法

8. 异常捕获

语法:
try:
	放可能报错的代码
except Exception as 别名: Exception错误类型的父类
    如果出现的错误在内执行的代码
    pass
else:
    不出错执行的代码
finally:
    不管出不出错都执行的代码

9. 正则表达式

9.1 概念及特点

  1. 概念:一种特殊的字符串,有独立的语法,用于对字符串的判断、验证、提取
  2. 特点:有独立的引擎,语法适用于所有语言,正则表达式处理效率要比内置的字符串的效率低,当字符串方式处理不了的时候可以使用正则

9.2 构建及操作

​ 导入re模块:import re

  1. 构建:

    re.complie(正则表达式,匹配内容):将这规则表达式编译成正则对象

  2. 操作:

    正则对象.操作:查询:正则对象.match(匹配对象)

    可以简写为match(正则表达式,匹配对象)

    操作有如下:

    操作含义
    match(正则,字符串)在匹配对象中查找内容,查找最左边,如果最左边有则返回内容,如果没有返回None,不往后查找
    search(正则,字符串)在匹配对象中查找,查询满足条件的第一个,没有的话返回None
    fullmatch(正则,字符串)在字符串中查找,要求字符串全部满足正在表达式

| findall(正则,字符串) | 在字符串中查找所有满足正则表达式的结果,返回一个列表,没有满足的返回一个空列表 |
| split(切割符,字符串) | 将字符串按照切割符号进行切割 |
| sub(正则,新内容,字符串) | 在字符串中将正则表达式匹配的内容替换成新内容 |

  1. 匹配单个字符

    字符含义
    .匹配任意字符,除\n外
    [字符列表]表示这个位置上的符号可以是字符列表里面的任意一个字符
    [A-Z]如果ASCII码连续,可以使用-连接
    [A-Z]:A-Z的任意一个字符
    [a-z]: a-z的任意一个字符
    [0-9]: 0-9中的任意一个字符
    [a-zA-Z0-9]:所有英文字母和数据
    ^表示非的意思,取反,['^'0-9](不带引号)表示除数字外的字符
    \d所有的数字
    \D除数字外的字符
    \w数字,字母,下划线,不仅指英文字母
    \W非数字,字母,下划线
    \s空白字符
    \S非空白字符
    |存在并列情况,用|分隔,比如手机号以189或者178开头,表示为(189|178),用小括号包含
    ()把想要的内容放进去,
    ^x限定开头
    x$限定结尾
  2. 数量词

    符号含义
    *符号前面的内容出现任意次,>=0
    +符号前面的内容出现至少一次,>=1
    ?符号前面的内容出现0-1次
    {m}固定出现m次
    {n,m}出现n-m次
    {m,}至少出现m次
  3. 易错点:

    验证邮箱时,后缀有 . ,此时的 . 就是其本身,不代表任何字符,所以以[.]的形式表示

  4. 贪婪模式:

    没有上线的数量词,只有后面有就能匹配,就是贪婪匹配;缺点就是取到的结果不合适,所以在数量词后加?就取消了贪婪模式。

  5. 匹配标记

    re.S:匹配所有:当字符串中有换行符是,. 是不能匹配对应的内容的,所以使用findall函数不能查到,re.findall(正则表达式,字符串,re.S),这样就可以匹配到了

    re.I:忽略大小写,re.findall(正则表达式,字符串,re.I)

    re.S|re.I:既能忽略大小写又能匹配所有

10. 数据采集

10.1 概念

10.1.1 爬虫

通过编写程序,模拟浏览器上网,批量采集数据,数据是即有数据,网站上提供的数据

10.1.2 爬虫的合法性

爬虫本身不被法律禁止,但是具有违法风险,爬虫不要干扰网站的正常运营,不要采集关于个人信息相关的内容

10.1.3 robots协议 – 君子协议

每一个网站在robots协议中写好了那些内容可爬取,但是君子协议不会对爬虫程序做出阻止的效果

10.1.4 反爬机制

网站设定了相关的机制,防止爬虫程序对数据的获取或访问

10.1.5 反反爬策略

爬虫程序中可以制定相关的策略和解决方案,破解网站中的反爬机制,进而获取数据,比如添加请求头

10.2 客服端和服务器端

10.2.1 客户端

供用户使用的一端

10.2.2 服务器端

给用户提供服务的,客户端显示的内容是来源于服务器的,数据信息存储在服务器端的,客户端需要什么数据,向服务传递信息,服务器接受到信息之后,进行数据查找,把找到的数据返回给客户端。

10.2.3 请求和响应

请求:客户端把数据信息发送给服务器,这个过程称为请求

响应:服务器接受到请求,查找数据,把响应的数据返回给客户端,这个过程称为响应

10.2.4 请求方式
  1. get请求:请求的参数直接拼接在网址上,拼接方式:?key=value

    参数直接暴露在网址上,所以不安全

  2. post请求:请求参数不会直接暴露在网址上,安全性比较高,客户端向服务器端提交信息,需要服务器进行验证,这种请求一般用post

10.2.5 url–网址

统一资源定位符,通过url定位获取相应的资源

格式:协议//域名或者ip:端口/资源路径?key=value&key=value

协议:http或者https,数据传输需要遵守一定的规则,规则就是协议

域名或者ip:定位服务器的地址

资源路径:内容在服务器的那个位置

?key=value&key=value 请求参数,客户端向服务器传递的数据信息

10.2.6 http协议

超文本传输协议,是网络数据通信的基础

  1. http 不仅限于文本,视频、图片等其他

  2. https 在http的基础上做了ssl加密,数据传输更安全

  3. 又称请求(request)响应(response)协议,客户端和服务器端请求和响应的标准

    请求:向服务器传递信息,称为请求

    报文:客户端向服务器传递信息,携带一个请求报文

    格式:请求行,请求头,空行,请求体

  4. 服务器将数据返回给客户端的时候,携带响应报文

    响应行,响应头,空行,响应体

    响应行:协议/版本号 响应状态码 响应状态的描述信息

    常见的状态码:

    ​ 200 – 表示响应成功

    ​ 403 – 被禁止访问

    ​ 404 – 资源找不到

    ​ 418 – 检测到非正常请求

    ​ 5** – 服务器异常

    响应头:

    ​ Set-Cookie: BDSVRTM=32; path=/

    ​ http协议是无记忆协议,需要cookie进行辅助记忆

    空行:作用 分隔响应头和响应体

    响应体 :响应内容

10.3 爬虫流程

  1. 指定url

  2. 发送请求,接收响应,获取响应数据,两种发送请求方式

    requests selenium

  3. 解析数据,把向获取的内容解析出来,正则解析、css器解析、xpath解析

  4. 存储数据,txt文件,excel文件,csv文件,图片、视频、音频、数据库

# 1.创建url
url = 网址
# 2.发送请求
# 先做请求头
head = {设备名:地址} # 必须以键值对的形式存在
res = requests.get(url=url,headers=head)
# 3.解析数据
# 注意:如果解析的过程出现了乱码,那就是编码方式和内容编码方式不一样导致的
# 解决方案一:res.encoding = res.apparent_encodinf
# 解决方案二:上网页直接看实际的内容编码方式,res.encoding=实际的编码方式
data = res.text
html_data = BeautifulSoup(data,'html.parser')
# 4.保存数据
with open 语句

10.4 python操作excel,csv

10.4.1 python操作excel

使用openpyxl模块,操作的是后缀为xlsx的excel文件:import openpyxl

  1. 创建工作簿:wb = openpyxl.Workbook() 注意:W大写,小括号要手写,不会弹出

  2. 创建工作簿:

    1. 创建工作簿时自带一个工作表:Sheet
    2. 新增工作表:wb.create_sheet(表明,index=索引):索引是从0开始,是工作表放的位置
  3. 选择工作表:写入信息时要先选择工作表:

    1. 工作簿.active:选择的是活跃工作表,操作时有提示
    2. 工作簿[表名]:可以制定表名,但是操作时没有提示

    注意:可以同时使用两种方式:

    1. ws = wb.active
    2. ws = wb['表1']
    可以先注释掉2,用活跃工作表提示操作
    操作结束后再解开2,注释掉1
    
  4. 写入数据

    1. 单元格写入:

      确定单元格,行和列同时确定:ws.cell(行,列,内容):不建议使用此方式

    2. 一行一行添加:

      ws.append(序列):追加内容一定是一个序列

  5. 保存数据

    保存的是工作簿:wb.save(‘路径’)

10.4.2 python操作csv
  1. csv文件:逗号分隔值文件,可以使用excel打开,后缀.csv

  2. 导包:import csv

  3. 文件操作

    with open(路径,'w') as fp:
    	# 写入文件,创建对象
        f = csv.witer(fp)
        f.writerrow(内容用逗号分隔):写入一行
        f.writerrows(二维数组,newline=''):写入多行
    with open(路径,'w') as fp:
        # 创建对象
        csv.DictWriter(fp,fieldnames=[表头内容用逗号分隔])
        # 写数据之前,把表头写进去
        f.writerheader()
        # 写入数据
        f.writerow(字典)
    
10.4.3 python在已有excel文件中写入数据
  1. 打开工作簿:wb = openpyxl.load_workbook(‘路径’)
  2. 查看有那些工作表:wb.sheet.names(可以打印出来)
  3. 选择工作表:ws = wb[表名]
  4. 然后写入数据,保存工作表,和之前一样的操作。
10.4.3 python获取excel中的单元格数据
  1. 导包:import openpyxl
  2. 打开工作簿:wb = openpyxl.load_workbook(路径)
  3. 选择工作表:ws = wb[表名]
  4. 获取单元格数据:ws.cell(行,列).value
  5. 可以通过这个过程修改值或者写入值:ws.cell(行,列).value = 值

10.5 HTML

超文本标记语言,由标签组成

10.5.1 标签组成

单标签:<标签名称 属性名=值 属性名=值 属性名=值 /> /可以省略

双标签:<标签名称 属性名=值 属性名=值 属性名=值> 内容(标签、文本数据) </标签名称>

10.5.2 标签之间的关系

根据标签的嵌套关系,标签之间的关系分为三种:

  1. 父子关系:直接包含和被保护
  2. 兄弟关系:被同一个父标签包含
  3. 先辈与后辈关系:直接或间接包含与被保护(直接、间接)
10.5.2 html的结构
<!DOCTYPE html>--声明这是一个html文档
<html lang="en">--双标签,lang='en',属性名=值的,声明english
    <head> --双标签,head标签中,文件的引用,设置
        <meta charset="UTF-8">--单标签,设置编码方式
        <title>Title</title>--双标签,展示窗口的显示信息
    </head>
    <body> -- 双标签,网页上显示的内容,来源于body
    </body>
</html>
10.5.3 css选择器查找
  1. id选择器:值标签有有一个属性名为id,唯一选择器,在html文档中 id=’值‘是唯一的;< div id = ‘hahh’ > 使用方法:#属性值,比如#hahh – 标识定位的具有 id=hahh 的标签

  2. class选择器:类选择器—值标签有一个属性名为class;< div class = ‘hahh’ >结合 cyrl 查找,根据查找确定数量,使用方法:.属性值,比如 . hahh—表示定位的,具有class=hahh的标签

    < div class=‘hahh huhu’ >多个值之间使用空格分隔,如果使用类选择器查找,只能选择其中一个属性值使用,那个少用那个

  3. 标签选择器:元素选择器,指标签名称为指定内容的< a href=’ ’ >,使用方法:使用标签名称,比如a,div,li,img,span

  4. 包含选择器:

    1. 父子关系:父选择器>子选择器(>)

    2. 先辈与后辈关系:先辈选择器 后辈选择器(空格)

      < div class=‘hahh’ >

      ​ < span>开心啊< /span >

      < div >

      使用方法:div>span 或者 .hahh>span

  5. 兄弟选择器:

    1. A~B:指A标签后的所有的B标签
    2. A+B:指A标签后的紧跟的一个B标签
  6. 属性选择器:

    id class 也是属性选择器,也可以用作属性选择器,只是比较特殊被单独赋予了两个符号

    使用方法:

    1. 选择器[属性名] – 表示的是具体的某个属性的
    2. 选择器[书名名] – 表示的是具体某个属性名且对应的值为指定内容的
    3. 选择器[属性名^=指定内容] – 表示的是具体某个属性名且对应的值以指定内容开头的
    4. 选择器[属性名$=指定内容] – 表示的是具体某个属性名且对应的值以指定内容结尾的
    5. 选择器[属性名*=指定内容] – 表示的是某个具体的属性名且对应的值的包含指定内容的
  7. 结构选择器:

    1. 父选择器>子选择器:first-child 第一个

    2. 父选择器>子选择器:last-child 最后一个

    3. 父选择器>子选择器:nth-child(n) 第n个

    4. 父选择器>子选择器:nth-child(1) 第一个

    5. 父选择器>子选择器:nth-last-child(n) 倒数第n个

      注意:::查找顺序是先找到父选择器中的第n个,验证是否满足子选择器

10.5.4 css选择器使用流程
  1. 使用bs4模块:from bs4 import beautifulsoup4

  2. 创建 Beautifulsoup的对象,将文本数据解析成html数据html_data = Beautifulsoup(获取的网页的源码数据,response.text,解析器:‘html.parser’)

  3. 定位标签,获取属性值或者标签之间的内容

    html_data.select(‘css语法’),获取满足条件的所有,返回结果是列表

    . select_one() 获取满足条件的第一个,返回一个标签

10.5.4 图片下载的流程

img-- 来源 src的属性对应的值,就是图片的链接

  1. 获取图片的链接
  2. 对图片链接发请求。接响应,获取数据(字节数据)。注意:网站编码方式,要修改指定编码方式,否则是乱码
  3. 把字节数据写入文件就是保存为图片
10.5.6 图片下载的流程
  1. img – 来源 src 属性对应的值,就是图片链接

    • 获取图片的链接
    • 对图片链接发送请求,接收响应,获取图片数据【字节数据】
    • 把字节数据写入文件就是保存图片
  2. wget方法

    import wget
    wget.download(url,'路径')
    

10.6 序列化与反序列化

json 数据是一种轻量级的数据,使用场景:作为网络传输的一种数据格式,数据要进行网络传输,最终都要变成文本数据或者二进制,有规定格式的字符串。比如:

  1. {k:v,k:vk:[{},{},{}},外层是字典,内存还有列表,列表内存是字典
  2. [{k:v},{k:v}],外层是列表,内存是一个个的字典

python中提供的与json数据进行数据交互的模块是 json

序列化:将程序中的对象转化为有特定格式字符串的过程,就是序列化

  1. json.dump
  2. json.dumps

反序列化:将程序中的字符串转化为程序中的对象的过程,就是反序列化

  1. json.load
  2. json.loads

注意:不带s的这一组和文件有关

10.7 xpath解析

html 是超文本标记语言,组成是标签,标签分单标签和多标签,标签名字是提供好的,不支持自定义

xml 是可拓展标记语言,组成是双标签,标签名字支持自定义

xpath 解析原理

  1. 实例化 etree 对象,导入方式:from lxml import etree,把获取的源码数据加载到该对象中

    from lxml import etree 
    # 数据的来源
    # 1.如果数据来源于本地文件
    tree = etree.parse(文件路径)
    # 2.如果来源于互联网
    data = response.text
    tree = etree.HTML(data)
    
  2. 利用xpath语法,实现标签的定位与内容的获取

    tree = xpath(xpath语法)

  3. xpath语法:路径解析法

    1. 路径查询:

      / – 从根节点开始查找,html文档:html - head、body–根节点是html

      // – 全局查找

    2. 谓语查找

      1. 具有某个属性的标签,路径[@属性]

        booknames = tree.xpath('//book[@type]/title/text()')
        
      2. 具有某个属性且对应值为指定内容:路径[@属性=指定内容]

        booknames = tree.xpath('//book[@type="foreign"]/title/text()')
        
      3. 具有某个属性且对应的值以指定内容开头:路径[starts-with(@属性,指定内容)]

        booknames = tree.xpath('//book[starts-with(@type,"for")]/title/text()')
        
      4. 具有某个属性且对应的值包含指定内容:路径[contains(@属性,指定内容)]

      5. 查找第几个标签:路径[数字]

        booknames = tree.xpath('//book[2]/title/text()')
        
      6. 查找最后一个:路径[last()]

        booknames = tree.xpath('//book[last()]/title/text()')
        
      7. 倒数第n个:路径[last()-(n-1)]

        # 倒数第2个
        booknames = tree.xpath('//book[last()-1]/title/text()')
        
      8. 获取前几个:路径[position()<4]:获取前三个

        # 获取前3个book
        booknames = tree.xpath('//book[position()<4]/title/text()')
        
      9. 使用子元素进行查找

        # 通过子元素筛选
        booknames = tree.xpath('//book[price>15]/title/text()')
        print(booknames)
        
        booknames = tree.xpath('//book[price>15 and price<30]/title/text()')
        print(booknames)
        
        
      10. 筛选满足条件的多个属性

        # 第一个book 或者 book1    |
        booknames =tree.xpath('//book[1]/title/text()|//book1/title/text()')
        
      11. 获取属性值:@属性名

        value = tree.xpath('//book[1]/@type')
        

10.8 selenium 操作方式

10.8.1 导包:from selenium import webdriver
10.8.2 操作流程
  1. 创建浏览器对象,两个注意:一是大写的C,而是加小括号

    browser = webdriver.Chrome :使用的是谷歌浏览器

  2. 对指定的 url 发送请求

    browser.get(url)

  3. 设置休眠时间,等待时间加载页面

    time.sleep(1)

  4. 获取操作

    1. 定位解析
    2. 获取内容
  5. 关闭浏览器

    browser.close()

10.8.3 网页操作
  1. 页面前进:browser.forward()
  2. 页面后退:browser.back()
  3. 页面刷新:browser.refresh()
  4. 点击:标签.click()
  5. 窗口最大化:browser.maximize_window()
  6. 获取当前打开的所有的窗口,返回的是列表,新打开的窗口追加在列表的最后一个:browser.window_handles
  7. 切换窗口:browser.switch_to.window(窗口)
  8. 定位输入信息窗口:定位到输入窗口的标签
  9. 输入信息:定位到输入窗口的标签.send_keys(内容)
10.8.4 窗口的切换

王者荣耀点击一个英雄进入详情页会多开一个网页,针对这种情况,解决方案:

  1. 先获取当前所有的窗口列表,定位列表的最后一个就是当前窗口,browser.switch_to.window(browser.window_handles[-1])
  2. 内容获取完后要返回第一个页面定位标签,窗口列表索引为0的就是一个窗口:browser.switch_to.window(browser.window_handles[0])
  3. 获取完详情页信息可以关闭当前页:browser.close() 写在循环里面
10.8.5 内嵌窗口的切换

QQ邮箱的登录页面第一显示的是二维码登录,想要切换成账号密码登录,不能直接定位到切换点击的标签;因为有多层内嵌窗口,需要先切换窗口,从最外层窗口开始定位

  1. 定位外层窗口:iframe标签,然后切换窗口
# 定位第一层:ifrmae1
ifrmae1 = browser.find_element(by=By.CSS_SELECTOR,value='.QQMailSdkTool_login_loginBox_qq_iframe')
# 切换窗口:
browser.switch_to(iframe1)
  1. 有多少层定位多少层,然后切换
10.8.6 验证码的识别
10.8.6.1 简单验证码的识别
  1. 导包:import ddddocr,这是个三方模块,需要下载:pip install ddddocr
  2. 创建对象:ocr = ddddocr.DdddOcr(show_ad=False)
  3. 识别:因为识别的是图片的字节数据,所以要读字节数据
# 读出来图片的字节数据
with open (路径,'rb') as fp:
    img_data = fp.read()
# 解析图片,获取结果
res = ocr.classification(img_data)
  1. 获取图片字节数据的快捷方法
# 图片标签.screenshot_as_png
# 1.定位到图片
img = browser.find_element(by=By.CLASS_NAME,value='code')
# 2.获取图片的字节数据
data = img.screenshot_as_png
10.8.6.2 复杂验证码的识别
  1. 导包:from chaojiying import Chaojiying_Client,超级鹰是个网站上下载的文件,付费识别操作
  2. 创建超级鹰对象:cjy = Chaojiying_Client(账号,密码,软件id)
  3. 识别图片的字节数据:img_data = open(路径,'rb').read()
  4. 使用超级鹰对象识别图片的字节数据:res = cjy.PostPic(img_data,收费标准对应的值),返回的是一个字典在通过字典的获取方式获取对应的验证码:res['pic_str']
10.8.6.3 点字识别验证
  1. 获取图片的位置

  2. 在根据位置获取图片的区域:img.rect返回的是左上角的坐标,宽和高 {'height': 446, 'width': 348, 'x': 318.65093994140625, 'y': 156.6934051513672}

  3. 根据定位获取字节数据:img_data = img.screenshot_as_png

  4. 再使用超级鹰验证识别坐标:超级鹰对象.PostPic(img_data,9004),9004是识别字体的方式码,返回的是字典,然后在获取pic_str对应的值,pic_str值是一个字的坐标们,切割获取每一个字的坐标,在eval获取每一个坐标的数值,生成一个新列表,这是列表里面的元素是元组,元组里面是坐标

  5. 遍历列表,获取每一个汉字的坐标,在使用操作链对象偏移定位汉字:

    action.move_to_element_with_offsct(整体图片的位置,x-(1/2)*图片位置的坐标.get('width'),y-(1/2)*图片位置的坐标.get('height')).click().perform()偏移后的位置并点击

10.8.7 动作链操作
10.8.7.1 鼠标操作
  1. 导包:from selenium.webdriver import ActionChains

  2. 创建动作链对象:action = ActionChains(browser)

  3. 操作:

    操作含义
    action.mov_to_element(定位).perform()鼠标移动到定位
    action.click(定位).perform()鼠标点击定位
    rect = browser.find_element(by=By.ID,value=‘J_goodsList’).rect获取商品的区域
    action.scroll_by_amount(delta_x=横向移动,delta_y=纵向移动).perform()滚轮滑动,返回的结果是,区域的的宽,高,x,y。其中x,y是左上角的坐标
    action.scroll_by_amount(delta_x=0,delta_y=int(rect.get(‘y’)+rect.get(‘height’))).perform()把滚轮划到最下面,把页面加载全
10.8.7.2 键盘操作
  1. 导包:from selenium.webdriver.common.keys import Keys
  2. 操作:定位.send_keys(Keys.RETURN)
  3. 还有其他操作,在keys后面找
10.8.8 cookie捕获和登录
  1. 思路:手动登录后捕获cookie,获取写入到文件;以后读取文件进行cookie登录,可使用多次

  2. 获取:all_cookie = browser.get_cookies()

  3. 写入文件:

    import json
    with open('路径','w',encoding='utf-8') as fp:
        json.dump(all_cookie,fp)
    
  4. 登录:先读cookie文件

    with open('路径','r',encoding='utf-8') as fp:
        all_cookie = json.load(fp)
    
  5. 遍历cookies,一个一个添加cookie,

    for i in all_cookie:
        browser.add_cookie(i)
    
  6. 添加完后就登录了,刷新页面

二、MySql 数据库

1. 数据库的介绍

1.1 数据库:

DataBase ,简称 DB,长期存在电脑上,安装有组织的方式存储的数据集合,保存数据的容器

1.2 数据库管理系统:

DataBase Manngement System, 简称DBMS ,用来管理和操作数据的软件,用来建议、维护、使用数据库,对数据库进行管理,用户可以使用数据库管理系统访问数据库中的数据。

1.3 数据库的分类

1.3.1 关系型数据库:存储的数据之间存在关系,数据之间可以建立联系
关系解释
一对一关系
一对多关系A表中的一条数据可能关联着B表中的多条数据,比如班级表和学生表
多对多关心A表中的一条数据关联B表中的多条数据,同时B表中的一条数据关联A表中的多条数据,多对多关系的秒数需要建立一张中间表
1.3.2 常见的数据库
  1. 关系型数据库

    Oracle常用于大型公司

    MySqlOracle收购,常用于中小型公司,免费的

    SQLServer 是微软推出的产品

  2. 非关系型数据库

    数据之间不能建立联系,每个数据是独立的。常见的有MongoDB Redis,存储类似于字典的形式的数据,读写效率快,常做缓存处理。

1.4 数据库的卸载和安装

1.4.1 卸载
  1. 先停数据库服务

    操作步骤:此电脑–右键–管理–服务和应用程序–服务–mysql80–右键–停止

  2. 卸载程序:控制面板–MySQL server

  3. 删除文件

    操作步骤:c盘下–program files – program files(x86)–隐藏文件夹–查看–显示–隐藏的项目–programdata–找mysql 删除

  4. 查看注册表删除mysql相关内容

操作步骤:win + r–regedit–计算机\HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Services\EventLog\Application

  1. 重启电脑
1.4.2 安装
  1. 安装MySQL是数据库管理系统,是一个多用户系统,安装之后,自带一个超级管理员用户root,密码在安装时设置,123456
  2. 安装的内容:
    1. MySQL server 数据库的服务,自己的电脑相当于是服务器
    2. MySQL workbench 是MySQL官方自带的可视化软件,安装后自带一个root链接,忘记密码可以在里面修改,修改指令:alter user 'root'@'localhost' identified by '123456';
    3. ODBC 和 NET
  3. 测试
    1. 配置环境变量:此电脑–右键–属性–高级系统设置–环境变量–用户变量–新建–粘贴–确定点下去
    2. cmd调用指令测试:mysql - uroot - p 回车,输入密码123456返回结构后表示成功

2. 数据类型、运算符、约束

2.1 数据类型

2.1.1 帮助文档

cmd输入mysql再数据?数据类型,会出现该数据类型的介绍和数据范围

2.1.2 整数类型
类型解析
tinyint微整型数据,数值范围:
无符号:0-255
有符号:-128-127
1个字节(8位)
samllint小整型,2个字节
mediumint中等整型,3个字节
int整型数据,4个字节
bigint大整型,8个字节
2.1.3 小数类型
类型解析
float单精度浮点型,4个字节,有效小数位是8位
double双精度浮点型,8个字节,有效小数的位数是16位
decimal任意精度,精确度比float和double更高
2.1.4 文本类型
类型解析
char(m)定长字符串,数值范围:0-255
varchar(m)变长字符串,数值范围:0-65535。常用
tinytext文本形式的短字符串,存储字符个数和char一样
text文本形式的长字符串,存储字符个数和varchar一样
mediumtext文本形式的中等字符串,存储字符的个数2^24-1个
longtext文本形式的极长字符串,存储字符的个数2^32-1个
2.1.5 时间类型
类型解析
date年月日,传递数据格式为 yyyy-mm-dd
time时分秒,传递数据格式为 HH:MM:SS
datetime年月日 时分秒,传递数据格式为 yyyy-mm-dd HH:MM:SS
timestamp时间戳类型,1970-1-1到现在的秒数,存储的时候建议使用长整型bigint

2.2 运算符

用法同python

2.2.1 比较运算符
运算符作用
>字段值大于条件值
>=字段值大于等于条件值
<字段值小于条件值
<=字段值小于等于条件值
=字段值等于条件值
<>字段值不等于条件值
!=字段值不等于条件值
2.2.2 逻辑运算
运算符作用
and逻辑与
or逻辑或
逻辑非
2.2.3 成员运算
运算符作用
in字段 in (包含多个数据)
not in字段 not in (包含多个数据)
2.3.4 区间运算
运算符作用
between A and B字段 between A and B,字段的值介于A到B之间包含B
not between A and B字段 not between A and B,字段的值在A到B之外
2.3.5 非空判断
运算符作用
is null字段 is null,判断是否是空值
is not null字段 is null,判断是否不是空值
2.3.6 模糊判断
运算符作用
like字段 like ‘指定内容’,查找字段像指定内容的值
通配符作用
_匹配任意一个字符
%匹配任意多个字符
2.3.7 正则筛选
运算符作用
字段 regexp ‘正则表达式’查找符合正则表达式的内容,和python正则表达式一样。注意这里的表达式如果没有限定开头和结尾,找的是包含对应内容的值。所以一般加上限定开头结尾。

2.3. 约束

2.3.1 默认约束

定位字段的时候,给一个默认值,添加数据时如果不给这个字段传值,使用的就是默认值,如果给字段传值,使用的是新值。

字段名 数据类型 default 默认值;
2.3.2 非空约束

表示对于这个字段必须传值,不允许为空,not null

字段名 数据类型 not null;
2.3.3 唯一约束

表示这个字段在传值时不允许有重复的值;唯一约束会将该字段设置为索引

字段名 数据名 unique;
2.4.4 检查约束

检查该字段的传值是否满足要求

字段名 数据类型,check(性别=男 or 性别=女)
2.5.5 主键约束

表示该字段是数据表中的唯一标识,not null 和 unique 的结合体,一张表中只有一个主键,主键可以是由一个字段完成,也可以是多个字段组合起来的,称为联合主键。主键会把该字段设置成索引。

-- 单字段主键
字段名 数据类型 primary key
-- 多阶段主键
create table if not exists 表名(
字段1 数据类型 约束,
字段2 数据类型 约束,
primary key(字段名)
2.6.6 外键约束

建立表与表之间的关系,用来绑定关系的就是外键约束。

比如班级表和学生表,一个班级由多个学生,一个血行只能在一个班级,存在一对多关系;
学生表是多的一方,学生表中添加的班级编号字段来源于班级表,所以班级表式主表,主表是数据的来源方
学生表使用班级表中的班级编号字段,所以学生表是从表,从表是数据的依赖方
-- 外键设置在从表中,并放在语句的最后
constraint 外键约束名 foreign key(字段名) references 主表表名(字段名)
-- 解读为对这个字段创建外键约束参照主表的指定字段
2.7.7 注解约束

解释字段的含义

字段名 数据类型 comment 解释内容
2.8.8 自增长约束

结合整数类型的主键使用,达到主键的自增长

-- 如果不给这个字段赋值,从1开始,逐渐加1
字段名 数据类型(intprimary key auto_increment 

3. SQL结构化查询语言

3.1 介绍

通过SQL语言与数据库进行交互,进行增删改查等操作

映射:

​ 数据库–excel工作簿

​ 数据表–excel工作表

​ 字段名–sheet表头

​ 记录 --excel中的一行数据

根据功能的不同可能分为四类

3.2 DDL

3.2.1 概念及定义

Data Define Language,数据定义语言,关羽数据库、数据表结构的操作,比如创建数据库,删除数据库,修改数据库;创建表结构,删除表结构,修改表结构。

3.2.2 核心指令
  1. 数据库相关
指令作用
create database if not exists 数据库名 charset utf8mb4;创建数据库
show databases;展示所有数据库
alter database 数据库名 charset utf8mb4;修改数据库的编码方式
drop database if exists 数据库名;删除数据库
show create database 数据库名;展示数据库的创建信息
use 数据库名;使用数据库
  1. 数据表相关
指令作用
create table if not exists 表名(字段1 数据类型 约束,字段2数据类型 约束);创建数据表
drop table if exists 表名;删除数据表
desc 表名;查看数据表结构
alter table 表名 add 字段名 数据类型 约束;增加字段,默认增加在最后
alter table 表名 add 字段名 数据类型 约束 after 指定字段名;在指定字段名后增加字段
alter table 表名 add 字段名 数据类型 约束 first;在第一列增加字段
alter table 表名 drop 字段名;删除字段
alter table 表名 change 原字段名 新字段名 数据类型 约束;修改字段
alter table 表名 drop primary key;删除主键约束
alter table 表名 add primary key(z字段名);对指定字段添加逐渐约束
alter table 表名 drop froeign key 外键名称;删除外键约束
alter table 表名 add constraint 外键名称 foreign key(字段名) references 表名(字段名)添加外键
alter table 表名 add unique(字段名);添加唯一约束
alter table 表名 drop index 字段名;删除唯一约束

3.3 DML

3.3.1 概念及定义

Data Mainpulation Language,数据操作语言,向数据表中添加、删除、修改数据。

3.3.2 核心指令
指令作业
insert into 表名(字段1,字段2···)value(对应内容1,对应内容2···);添加一行数据
insert into 表名(字段1,字段2···)values(对应内容1,对应内容2),(对应内容1,对应内容2)···;多行添加数据
drop table 表名;删除表
update 表名 set 字段=值 where 筛选条件;修改表中数据
delete from 表名 where 筛选条件;只能删除表中数据,不能删除表结构

3.4 DQL

3.4.1 概念及定义

Data Query Language,数据查询语言,从数据库中查询想要的数据

3.4.2 简单筛选查询指令
指令作用
select * from 表名;查询指定表的所有信息
select *|字段 from 表名 where 条件;
3.4.3 分组查询
指令作用
select 字段 ,聚合函数(字段名)from 表名 group by 分组字段|聚合函数根据分组字段进行分组,分组后的目的是获取描述性统计结果
group_concat(字段)把分组后的指定字段拼接起来显示
聚合函数作用
count()计数
max()最大值
min()最小值
sum()求和
avg()求平均

​ 举例:

-- 书写顺序
select 字段,聚合操作
		from 表名
		where 分组之前的筛选条件
		group by 分组字段(不一定唯一)
		having 分组之后的筛选
-- 执行顺序
from > where > group by > having > select
3.4.4 子查询

把一个查询结果应用在另一个sql语句中,称为子查询

应用场景:判断条件一个变动的值,需要通过查询得到结果。

-- 注意案例:使用子查询修改数据
-- 列表的易错点:遍历的同时对数据进行修改,会漏掉元素的检验,所以需要赋值一份出来解决问题
-- 表的复制:(select * from 表名) as 别名 
-- 如果把一个查询的结果作为一张表,这张表必须起别名

-- 把马小兰的班级编号修改成和小强一样,里层查询小强的班级编号,不能直接使用tb_student表,所以使用复制的表
update tb_student
	set class_no = (select class_no 
                    from (select * from tb_student) as t 
                    where t.stu_name = '小强')
    where stu_name = '马小兰'-- 可以通过with语句快速复制一张表,是根据查询结果定义的一张临时表,查询结果结束后这张表也就不存在了
with 新表名 as (select * from tb_student)
update tb_student
	set class_no = (select class_no 
                    from t 
                    where t.stu_name = '小强')
    where stu_name = '马小兰'
3.4.5 连接查询
3.4.5.1 内连接

查找的是双方表都复合条件的内容

写法1:不建议
select 字段 from 表1,表2 where 连接条件 and 筛选条件 group by 分组字段 having 分组后的筛选 order by 排序字段 排序规则
写法2:建议
select 字段 from 表1 join 表2 on 连接条件 where 筛选条件 group by 分组字段 having 分组后的筛选 order by 排序字段 排序规则


执行顺序:from > jion > where > group by > having > select > order by
3.4.5.2 外连接
  1. 左外连接:查找的是左表的做优信息及复合条件的右表信息

    select 字段 from 表1 left join 表2 on 连接条件 where 筛选条件 group by 分组字段 having 分组后的筛选 order by 排序字段 排序规则
    
  2. 右外连接:查找的是右表的做优信息及复合条件的左表信息

    select 字段 from 表1 right join 表2 on 连接条件 where 筛选条件 group by 分组字段 having 分组后的筛选 order by 排序字段 排序规则
    
  3. 执行顺序:from > join > where > group by > having > select > order by

3.4.5.3 全连接

全连接是full,mysql数据库不支持。可以使用左外连接 + 右外连接 + union 的方式实现。

union 纵向合并结果集,展现的是去重后的结果

union all 展现的是不去重的结果

语法:
左外连接
union
右外连接;
3.4.5.4 自连接

自己连接自己,当需要的信息豆来源于同一张表时,需要和自己连接

-- 案例1:
-- 查询1号课程比22号课程成绩好的同学,首先先查找出来1号课程的学生信息和成绩,其次查询出2号课程的学生信息和成绩
-- 两者信息来源于同一张表,注意自连接时必须起别名
select * from 成绩表 as t1 join 成绩表 as t2 on t1.学号=t2.学号 and t1.课程号=1 and t2.课程号=2 where t1.成绩>t2.成绩;
3.4.6 排序查询
书写顺序:
select distinct 字段 from 表1 join 表2 on 连接条件 where 筛选条件 group by 分组字段 having 分组后的排序字段 order by 排序字段 排序方式


执行顺序:
from > join > where > group by > having > select > distinct > order by
3.4.7 分页查询
limit 数字:取前几条数据
limit m offset n:偏移n条取m条数据,前n不取,从n+1开始取m条
limit x,y:偏移x条取y条数据,前x不取,从x+1开始取y条
书写顺序:
select distinct 字段 from 表1 join 表2 on 连接条件 where 筛选条件 group by 分组字段 having 分组后的筛选字段 order by 排序字段 排序方式 limit 数字 

执行顺序:
from > join > where > group by > having > select > distinct > order by > limit 
3.4.8 窗口函数
3.4.8.1 mysql8.0版本以后新值的功能,是处理数据和分析数据的高级方法。
语法:
窗口函数() overpartition by 分组字段
	[order by 排序字段 排序方式]
	[frame 子窗口]

​ 相较group by而言partition by不会影响总行数。

3.4.8.2 窗口函数分类
  1. 聚合函数和排名函数
聚合函数作用
sum(字段)over()求和
min(字段)over()求最小值
max(字段)over()求最大值
avg(字段)over()求平均值
count(字段)over()统计
排名函数作用
rank()over()出现并列,并列会占用名次
dense_rank()over()出现并列,并列不会占用名次
row_number()over()不会出现并列
  1. 排名函数的对比
rankdense_rankrow_number
90111
90112
80323
70434
  1. 偏移函数:用来处理同比、环比、定比等问题
函数作用
lag(字段,偏移个数)over()向下偏移
lead(字段,偏移个数)over()向上偏移
first_value() over()窗口的第一个
last_value() over()窗口的最后一个
3.4.8.3 子窗口
  1. 语法:

    窗口函数 over(partition by 分组字段 [order by 排序字段 子窗口])

  2. 指定子窗口的两种方式

    rows : 根据行指定

    range : 根据范围指定

  3. 定位行的五种方式

    current row : 当前行

    n preceding : 前n行

    n following : 后n行

    ​ : 窗口的第一行

    unbounded following : 窗口的最后一行

  4. 使用场景:

    求年内累计销量

    大窗口是年,子窗口就是从每年的第一行到当前行

    -- 会根据表中月份的排序来计算
    select *,sum(sales) over(
        partition by yearly 
        order by monthly 
        rows between unbounded preceding and current row) as '累计销量'
        from ym_sales;
    -- 当表中的月份不完整,确实几个月时,如果想按具体月计算,这时要使用range
    select *,sum(sales) over(
        partition by yearly 
        order by monthly 
        range between unbounded preceding and current row) as '累计销量'
        from ym_sales;
    
3.4.9 常用函数
3.4.9. 数值函数
函数作用
abs()求绝对值
pow(底数,指数)求幂数
pow(底数,指数(1/2))
sqrt(36)
求指数,案例求开方后的值
开方
log(3,27)=9、log10(100)=10求底数,以3为;求以10为低,100为值的指数
ceil(19.1)=20向上取整
floor(20.9)=20向下取整
round(89.887,1)=89.9四舍五入,保留指定位数,案例中是保留一位
mod(10,3)=1取余数
rand()取0-1的随机小数
pi()圆周率
format(数值,保留位数)千位分隔
max(),min()求最大,求最小
greatest(字段1,字段2···)求行最大值
3.4.9.2 文本函数
函数作用
char_length(字段名)获取字符串的字符长度
length(字段名)获取字符串的字节长度
left(字段,数量)取字符串左边的指定数量的字符
right(字段,数量)取字符串右边的指定数量的字符
mid(字段,开始位置,数量)取指定位置取指定数量的字符
substr(字段,开始位置,数量)取指定位置取指定数量的字符
substring_index(字符串,分隔符,序号)在字符串中获取指定序号的分隔符之前的字符串,正数序号表达从左开始,从1开始,负数从又开始
concat(字符1,字符2)拼接字符串,用逗号分隔
concat_ws(指定分隔符号,字符1,字符2)拼接字符串,使用指定分隔符分隔
instr(字符串1,字符串2)判断字符串1是否包含字符串2,返回的是字符串2在字符串1中的位置,如果没有返回0
replace(字符串,旧内容,新内容)把字符串中的旧内容替换成新内容
insert(字符串,起始位置,结束位置,新内容)把字符串中从指定开始位置到结束位置的这个长度的内容替换为新内容
find_in_set(‘b’,‘a,b,c’)在逗号分隔的数据中,查看指定数据在字符串中的第几个,返回位置,没有返回0
upper(字符串)、lower(字符串)转大写、转小写
trim(字符串)去除两边的空白字符
ltrim(字符串)去除左边的空白字符
rtrim(字符串)去除右边的空白字符
3.4.9.3 日期函数
函数作用
now()获取当前时间的年月日时分秒
current_timestamp()获取当前时间的年月日时分秒
curdate()获取当前时间的年月日
curtime()获取当前时间的时分秒
current_time()获取当前时间的时分秒
year(时间)提取时间的年份
month(时间)提取时间的月份
monthname(时间)获取时间的月份的英文名称
day(时间)提取时间的天
hour(时间)提取时间的时
minute(时间)提取时间的分
second(时间)提取时间的秒
date(时间)提取时间的年月日
dayofweek(时间)提取时间是星期几
dayname(时间)提起时间是星期几的英文
dayofyear(时间)提取时间是这一年的第几天
quarter(时间)提取时间是第几季度
week(时间,指定周几是开始)提取时间是这一年的第几周,默认是周天开始
extract(获取时间类型[年、月、日等时间单位] from 时间)万能公式,提取指定时间中的时间类型,比如年月等等
last_day(时间)获取时间所属月份的最后一天
  1. 1时间偏移
-- date_add 向未来偏移
-- date_sub 向过去偏移
-- 使用方法:date_add(时间,interval 偏移量的表达式 偏移类型)
-- 			date_add(时间,interval 偏移量的表达式 偏移类型)
-- 偏移类型:偏移量的表达式
	day		interval 10 day  向后或向前偏移10
  1. 2时间差
-- 求两个时间差的天数
datediff(大时间,小时间) -- 返回天数
-- 其他差值类型
timestampdiff(差值类型,小时间,大时间) -- 差值类型:年、月、日、时、分、秒
  1. 3时间戳
-- 获取当前的时间戳
unix_timestamp()
-- 将时间转化成时间戳
unix_timestamp(时间)
-- 将时间戳转化成时间
from_unixtime(时间戳)
  1. 4时间格式化与反格式化
-- 时间格式化
date_format(时间,文本格式) -- datefomat('2020-10-10','%Y/%m%d %H:%i:%s')
-- 时间反格式化
str_to_date(文本时间,'%Y/%m/%d %H:%i:%s')
3.4.9.4 分支函数
  1. 双分支

    -- 双分支
    if(判断条件,条件成立执行的内容,条件不成立执行的内容)
    -- if嵌套实现多分支,可以嵌套多层
    if(判断条件,条件成立执行的内容,if(判断条件1,条件成立执行的内容1,条件不成立执行的内容1))
    
  2. 多分支

    -- else可不写,但是必须写end
    case
    	when 条件1 then 执行操作1
    	when 条件2 then 执行操作2
    	when 条件3 then 执行操作3
    	else 执行操作3
    end
    
3.4.10 透视与逆透视
3.4.10.1 透视
-- 就是长表变宽表
-- 成绩表案例,把表设置成一下格式展示
/*
学生编号	学生姓名	围棋成绩	跳绳成绩	手游成绩	
*/

select tb_student.stu_no,tb_student.stu_name,
        sum(if(course_name='围棋',score,0)) as '围棋',
        sum(if(course_name='跳绳',score,0)) as '跳绳',
        sum(if(course_name='手游',score,0)) as '手游'
    from tb_student join tb_score
    on tb_student.stu_no = tb_score.stu_no
    join tb_course
    on tb_score.course_no = tb_course.course_no
    group by tb_student.stu_no;
-- 在这里除了使用sum还可以使用max
3.4.10.2 逆透视
-- 就是宽表变长表
-- 经过透视后的表,转回长表,上表为t
/*
学生编号	学生姓名	科目	
*/

-- 选围棋的
-- '围棋' as '科目'  围棋  列填充固定值  围棋
-- `围棋` as '成绩'  在t中获取的围棋列的值
with t2 as(
select stu_no,stu_name,'围棋' as '科目' ,围棋 as '成绩'  from t
union
select stu_no,stu_name,'跳绳' as '科目' ,跳绳 as '成绩'  from t
union
select stu_no,stu_name,'手游' as '科目' ,手游 as '成绩'  from t)
-- 把三个表合并起来,然后把成绩为0的去调就是想要的表,筛选大于0的
select * from t2 where 成绩 > 0
3.4.11 其他函数
函数作用
cast(值1 as 数据类型)数据类型转换
ifnull(字段,指定值)如果字段为空,填充为指定值
nullif(数据1,数据2)判断两个数据是否一样,一样返回nul,不一样返回第一个值
coalesce(null,null,17,18,null)返回第一个非空数据

3.5 DCL

3.5.1 概念及定义

Data Control Language,数据控制语言,给用户分配权限,收回权限。

3.5.2 核心指令
-- 1.创建用户
create user 用户名@ip地址 identified by 密码;
-- ip地址表示用户可以在那个ip地址进行登录
-- 如果想让用户在任意位置登录,ip地址写成 %

-- 2.用户登录,在cmd里面演示
mysql -u 用户名 -h ip地址 -P端口号 -p回车输入密码

-- 3.删除用户
drop user 用户名@ip地址;

-- 4.给用户分配权限
grant 权限1,权限2 on 数据库名.表名 to '用户名'@'ip地址'-- select是查询权限,update是修改权限
-- 将所有的权限分给用户
grant all on *.* to '用户名'@'ip地址'-- 5.撤销权限
remove update on 数据库名.表名 from '用户名'@'ip地址'-- 撤销所有权限
remove update on *.* from '用户名'@'ip地址'

4. 其他操作

4.1 数据库的备份与恢复

4.1.1 备份
cmd在指令窗口执行
-- 1.备份操作
mysqldump -u 用户名 -h ip地址 -P 数据库名 > 存储位置
-- 案例
mysqldump -u root -p homework > C:\Users\mac\Documents\Python2304\数据库\Day05\代码\homework.sql
-- 备份的是表的创建、添加数据的代码指令
4.1.2 恢复
-- 1.首先创建数据库,可以在软件里创建
-- 2.然后在cmd在指令窗口执行
mysql -u 用户名  -h ip地址 -P 端口 -p 新建的数据库名称 <  存储位置
-- 案例
mysql -u root -p homework1 <  C:\Users\mac\Documents\Python2304\数据库\Day05\代码\homework.sql

4.2 事务 transaction

4.2.1 概念

​ 事务就是表示sql语句要执行的任务,默认一条sql语句就是一个事务,但是有些场景下需要多条sql语句执行才能完成一个任务,多条sql组成一个事务。

4.2.2 特征

原子性:事务是一个整体,里面的内容要么全部完成,要么全部不完成。

一致性:事务一旦完成,整个数据处于一致的状态

隔离性:多个事务之间互相隔离

持久性:数据结果一旦提交,持久化保存

4.2.3 分类

隐式事务:没有明显的开始和结束的标记,语句完成自动提交结果

显示事务:有明显的开始和结束,多个sql语句组成一个逻辑单元

开启事务:begin 或者 start transaction

结束事务:事务中的sql语句没有问题,提交结果使用commit;事务中的 sql语句存在问题,回滚到开启事务之前使用rollback

4.3 索引 index

4.3.1 创建索引

目的提高查询的效率

  1. 查看索引:

    能够形成索引的还是主键约束,外键约束,唯一约束能自动形成索引

    指令:show index from 表名;

  2. 查看sql查询语句的执行效率

    指令:explain 查询语句

    如果在查询的过程中使用了索引,查询效率要比没有使用索引要高

  3. 解读

    索引相当于一本书的目录,用空间换区时间,在小范围内查找。

  4. 创建索引:选择使用频次高的字段创建索引

    指令:create index 索引名字(建议使用idx_开头) on 表名(字段名);

  5. 前缀索引:比如名字的姓

    指令:create index 索引名字(建议使用idx_开头) on 表名(字段(前缀长度));

    比如:create index inx_xing on tb_student(stu_name(1));

4.4 视图 View

将查询的结果作为一张快照,保存起来形成临时表。

  1. 作用:

    1. 减少频繁的表连接操作
    2. 控制用户访问列的权限
  2. 创建视图

    create view 视图名字(建议v_开头) as 查询语句;

  3. 删除视图

    drop view 视图名字(建议v_开头) as 查询语句;

  4. 修改视图

    alter view 视图名字(建议v_开头) as 查询语句;

4.5 封装函数

-- 使用sql封装函数,首先要修改结束标记,在sql语句中,一个语句的结束使用;封装函数的功能也是使用;,所以要修改一下
delimiter $$
-- 创建函数
create function 函数名(形参 参数数据类型) returns 返回值的数据类型
begin
	函数的功能体
end 语句结束标记
return 返回想要的数据
-- 函数结束后修改回来结束标记
delimiter ;

4.6 存储过程 procedure

将sql语句封装成存储过程之后,比单独执行sql语句效率要高,调用call 存储过程名字()

-- 创建存储过程
delimiter $$
create procedure 存储过程名称(形参)
begin
	sql语句
end
delimiter ;
-- 函数有且只有一个返回值,通常作为查询语句的一部分来执行,存储过程可以没有返回值,通常作为一个独立的部分来使用。

4.7 python连接数据库

在任何语言中都不能直接连接数据库,都需要连接池

  1. 查找
# pymysql是一个三方包,需要先下载,pip install pymysql
# 导包
import pymysql
# 1.创建连接对象
connect = pymysql.Connection(user=用户名,password=密码,port=端口号,host=ip地址,charset='utf8mb4',database=数据库名)
print(connect) # 如果返回一个地址,表示连接成功
# 2.创建游标对象,用作操作数据库
cursor = connect.cursor()
# 3.使用游标对象操作sql语句
cursor.execute('sql语句')
# 显示的数据是一个迭代器,以元组的形式展现,一行数据是一个元组
data = cursor.fetchone() # 显示一行
data = cursor.fetchall() # 显示所有
data = cursor.fetchmany(size=指定行数) # 显示指定行数
print(data) 
# 4.关闭连接
connect.close()
  1. 增删改
# 导包
import pymysql
# 1.创建连接对象
connect = pymysql.Connection(user=用户名,password=密码,port=端口号,host=ip地址,charset='utf8mb4',database=数据库名)
print(connect) # 如果返回一个地址,表示连接成功
# 2.创建游标对象,用作操作数据库
cursor = connect.cursor()
# 3.定义sql语句
sql = 'insert into 表名 value(字段1,字段2···);'  # 增加数据
sql = 'delete from 表名 where 筛选条件;' # 删除数据
sql = 'update 表名 set 字段=值 where 筛选条件;' # 修改数据
# 4.使用游标对象操作sql语句
# 如果操作无误,提交,如果操作有问题回滚
try:
	cursor.execute(sql)
    # 提交结果
    connect.commit()
except Exception as e:
    print(e)
    # 回滚数据
    connect.rollback()
    
# 5.关闭连接
connect.close()
  1. sql注入
# sql语句在执行之前,把结构和数据都定义好了,输入的内容会影响这条sql语句
select_sql = f"select * from tb_user where username = '{username}' and password = '{password}';"
select_sql = "select * from tb_user where username ='abc' or 1=1 -- nd password = '{password}'"
# 原因   sql语句执行之前 把结构和数据都定义好了  输入的内容会影响这条sql语句

# 解决方案:参数化查询,将原有的输入数据的部分改成占位
select_sql = "select * from tb_user where username = %s and password = %s;"
# 执行  传递数据 只对这两个空的位置的数据进行验证 影响不到原有的sql语句
cursor.execute(select_sql,args=(username,password))
# 这样会按占位去验证

三、数据分析三剑客

1. Numpy

1.1 概念及性质

  • N维数组
  • 数据科学计算的基础
  • import numpy as np
  • 功能强大,性能高

1.2 数组操作

1.2.1 数组相关函数
函数用法
np.array([1,2,3])构建数组
np.arange(开始数字,结束数字,步长)生成一维数组
reshape(行,列)重塑数组,生成指定格式的数组,生成新数组
resize(行,列)重塑数组,生成指定格式的数组,修改的是原数组
np.arange(15).reshape(3,5)生成一个3行5列的二维数组,数据范围[0,15)
数组.shape查看数组格式,几行几列
数据.ndim查看数组维度
数组.dtype查看数组的数据类型
数组.data查看数组的地址
数组.size查看数组的元素数量
np.ones((行,列))生成指定行列数据为1的数组
np.zeros((行,列))生成指定行列数据为0的数组
np.eye((行,列))生成指定行列的单位矩阵(对角线为1其余为0)
np.linspace(起始值,终止值,数量)生成指定范围内指定数量的浮点类型的数组
数组.reshape((1,1,3,5))有几个数字就是几维数组,第一个1是四维数组里面的元素有1个,第二个1是三维数组里面有一个元素,(3,5)代表二维数组是3行5列的数组
数组.astype(数据类型)修改数组的数据类型
all()全为真则为真
any()一个为真则为真
where(判断条件,正确的值,错误的值)
数组.sum(axis=)
数组.view()等同于浅拷贝
数组.copy()深拷贝
1.2.2 数组基本操作
操作含义
数组1-+*/数组2同位置上的元素加减乘除
数组 ** 2数组里面的元素的平方
数组 > 值返回一个布尔类型的数组
数组1 += 数组2在数组1的基础上加上数组2,返回一个新数组,改变的是原数组1,不产生新数组
数组.cumsum(axis=0或1)按行累加,或者按列累加
数组.cumprod(axis=0或1)按行累乘,或者按列累乘
1.2.3 矩阵乘法
  1. 数组1 @ 数组2
  2. 数组1.dot(数组2)
  3. 计算规则,数组1的行数据 * 数组2的列数据 的和,返回行为数组1的行数,列为数组2的列数的矩阵(m * p p * n )
1.2.4 随机数
函数用法
np.random.random(数字)生成指定数字长度的0-1之间的小数一维数组
np.random.random((x,y))生成x,y的0-1之间的小数二维数组
np.random.randint(起始值,终止值,数组格式)生成[起始值,终止值)的指定数组格式的整数数组
np.random.randn(数字)生成指定长度数字的0-1的浮点类型的一维数组
np.random.choice([‘男’,‘女’],size=())随机生成内容为男或女的指定长度的数组
1.2.5 切割和拼接
拼接函数作用
np.concatenate((数组1,数组2···),axis=)可以指定轴向合并两个数组,常用
np.vstack(数组1,数组2)纵向拼接
np.c_((数组1,数组2))纵向拼接
np.hatack((数组1,数组2))横向拼接
np.r_((数组1,数组2))横向拼接
拆分函数作用
np.hsplit(数组,数量)按数量等分
np.hsplit(数组,[索引1,索引2···])根据指定索引切割

1.3 索引和切片

1.3.1 一维数组
  1. 和Python基础中的有序序列的操作类。语法:数组[索引]
  2. 通过索引获取与修改:数组[索引]=值(有正向和负向,和列表的操作一样)
  3. 切片:数组[起始位置:结束位置:步长](有正向和负向,和列表的操作一样)
1.3.2 二维数组
  1. 语法:数组[行索引,列索引]
  2. 修改:赋值时一定要和获取的格式一致,或者为单位格式的数据
  3. 反转:[::-1]

1.4 花式索引

  1. 一维数据,一维索引
表达式含义
数组[[::2]]隔一行获取一行数据
数组[[1,2,3]]获取索引为1,2,3的行数据,索引是什么顺序,结果就是什么顺序
数组[:.[1,2,3]]获取索引为1,2,3的列数据
数组[[1,2],[1,2]]获取索引为1,2行和索引为1,2列对应的数据
  1. 多维数组,多维索引
表达式含义
数组1=[1,2,3,4,5,6,7,8,9],数组2=[[0,1]
,[2,3]],
数组3=[[0,1]
,[1,0]],
数组1[数组2]=[[1,2],
[3,4]]
索引是二维数组,返回结果和索引数组的结构一致
数组2[数组3]=[[[1,2],[3,4]],
[[3,4],[1,2]]]
只要索引里面没有跟:已经后面的索引,那就是获取行数
布尔索引,数组4=数组1>5,
数组1中的所有元素和5所比较,返回布尔值,和数组1的机构一致
数组1[数组2]返回满足判断条件的所有的值

1.5 真图解析

# 导入画图工具包
from matplotlib import pyplot as plt 
# 读取图片,返回的是三维数组,高,宽,颜色数组[R红,G绿,B蓝]
aa = plt.imread('./aa.jpg')
# 展示图片
plt.imshow(aa)
# 图片左右方向反转,操作三维数据的花式索引
aa[:,::-1]
# 上下左右方向反转,操作三维数据的花式索引
aa[::-1,::-1]
# 颜色反转
aa[:,:,::-1]

2. Pandas

2.1 Series

  1. 由一维数组和对应的索引构成,也可以使用指定索引构成
  2. 语法:pd.Series(数组,index=索引数组)

2.3 DataFrame

2.3.1 概念及数据标识
  1. 二维数组,可以指定行列索引,类似数据库的表,也类似Excel的表
  2. 构造语法:pd.DataFrame(二维数组,index=行索引数组,columns=列索引数组)
  3. 行索引:index, 查看行索引语法:数组.index
  4. 列索引:columns,查看列索引语法:数组.columns
  5. 一行代表一条数据
  6. 一列代表一个属性,一个字段,一个维度
  7. 这里的维度和numpy里的维度有区别,numpy是指由几层数组,这里是几列
2.3.2 常用函数(API)
函数作用
df.describe()返回所有统计性描述,包括count,mean,std(标准差),min,max,四分位数据
df.dropna(axis=)指定轴向删除nan数据
df.dropna(sub=[字段名称])表示把指定字段内的有nan的值,删除这一行
df.drop(index=索引,columns=索引)可以根据索引来删除指定行和列
df.drop_duplicates(可以指定参数)删除重复行
df.T转置
df.sort_values(by=字段,ascending=False/True)按指定字段排序,ascending=True是升序,False是降序
df.sum(axis=)按指定轴向求和
df.mean(axis=)按指定轴向求平均
df.min(axis=)按指定轴向求最小值
df.max(axis=)按指定轴向求最大值
df.head(数字)查看头部数据,默认是5行,也可以指定数字
df.tail(数字)查看尾部数据,默认是5行,也可以指定数据
df.sample(数字)随机查看数据,可以指定数字,查看行记录
df.index查看行索引
df.columns查看列索引
df.dtypes查看数据类型
df.info查看数组信息
df.shape查看数组形状
df.sort_values(by=排序字段,ascending=True/False)按照指定字段指定方式给文件排序
df.reset_index()重置索引
nunique()去重统计
2.3.3 索引和切片
函数作用
df.index.name=名称给索引名字
df.loc[行名称,列名称]查找指定行列对应的数据,使用的是显示索引
df.loc[[行数组],[列数组]]可以是数组,获取制定行列的数组
df.iloc[行索引,列索引]根据隐式索引获取对应的数据
df.iloc[行切片,列切片]可以是切片或是指定索引的数组
df.at(行名称,列名称)通过显式索引获取数据
df.iat(行索引,列索引)通过隐式索引获取数据
df[字段名称]获取整列的数据
布尔索引用法
df[df[字段]>数据]获取满足指定条件的数据
df[df>数据]显示满足条件的数据,不满足条件的显示nan
df[字段].isin(数组)判断表中某个字段中的数据是否在指定数组中,返回布尔值
df.query(‘字段1>条件 and 字段2<条件’)筛选符合条件的行
df.isna()判断是否是nan,返回布尔值
df.isin(数组)判断数据是否在数组内,返回布尔值
df.isnull()判断数据是否为空
df.notnull()判断数据是否不为空
df[字段].str.contains(包含内容)判断字段中是否包含指定内容,返回布尔值
2.3.4 修改数据
操作含义
df[字段]=数组把指定字段的数据修改为新数组内的数据,要求数组和整个字段的长度一致
df[字段]=数字把整个字段的数据都修改为指定数字,具有广播机制
df[df>数字]=-df满足条件的值变成负数,不满足条件的保持
df.fillna(value=df.mean())把空值填充为平均值,也可以填充为其他值,比如中位数,众数等
df[字段]=np.nan把某个字段的值填充为nan
df.fillna(method=‘backfill’,[axis=])
df.fillna(method=‘bfill’,[axis=])
使用后面的一个数值填充
df.fillna(method=‘ffill’,[axis=])
df.fillna(method=‘pad’,[axis=])
使用前面的一个数值填充
df.replace(to_replace={‘原内容1’:‘新内容1’,‘原内容2’:‘新内容2’})把指定内内容替换为指定新内容
df[字段].str.split(expand=True)按照空白字符切割,生成一个df,可以指定切割内容切割
2.3.5 Apply函数的应用
  1. 语法:df.apply(函数)
  2. 可以是np的函数
  3. 也可以是开发的函数
2.3.6 切割、拼接、分组
  1. 切割

    行切割:使用切片,比如:df[:3],df[3:5],df[5:]

    按指定行切割为几个不同的表

  2. 合并或连接

    函数用法
    pd.concat([表1,表2···],[axis=])序列是必传,轴向默认是纵向
    pd.merge(表1,表2,how=‘left/right/inner/outer’,left_on=左表字段,right_on=右表字段)可是指定连接方式进行连接,可以指定字段作为连接条件,作用机制参考数据库的连接
    df.append(df1,[ignore_index=True])在后面追加,要求字段一样
  3. 分组

    函数用法
    df.groupby(by=[字段1,字段2])支持多字段分组
    df.groupby(by=[字段1,字段2]).sum()分组后聚合,可以跟其他聚合函数
    df.groupby(by=[字段1,字段2]).agg({聚合字段:[sum,min,max,mean···]})使用agg可以达到多聚合函数的效果
2.3.7 IO和数据库的读入和读出
  • I是input,读入数据

  • 读取语法:pd.read_csv(路径)pd.read_excel(路径)pd.read_sql(路径)··· 还可以读取其他格式的文件

  • O是outer,输出数据

    • 输入语法:df.to_csv(路径)df.to_excel(路径)df.to_sql(路径)···还可以输出为其他格式的文件
  • 操作数据库:

    • 读数据:

      # 导包
      import pymysql
      import pandas as pd
      # 连接数据库
      con = pymysql.connect(user='root',password='123456',host='127.0.0.1',port=3306,database='python2302',charset='utf8mb4')
      # 查询语句
      sql = 'select * from playerlogin'
      # 读取数据
      pd.read_sql(sql,con)
      
    • 写入数据库:

      # 导包
      from sqlalchemy import create_engine
      # 连接数据库
      engine = create_engine('mysql+pymysql://root:123456@127.0.0.1:3306/python2302?charset=utf8')
      # 写入数据库
      df.to_sql('table',engine,schema='python2302',if_exists='replace',index=True,chunksize=None,dtype=None)
      # if_exists()参数:
      				# fail:添加,如果表存在,不再进行操作
          			# replace:替换,如果存在,替换原有
              		# append:更新,如果存在,在后面追加内容
      # index是索引,可是视情况而定
      # chunksize是对数据进行分块,百万级一下不用
      # dtype是指定数据对象,可视情况而定
      
2.3.8 堆叠解压及数据透视表
  • 堆叠和解压
    • 堆叠:相当于逆透视,把多列数组转化为一维多个行索引的数组,df.stack()
    • 解压:堆叠的反过程,df.unstack(索引),代表把那个行索引拆分为列索引,也可以回个数组,如果是-1表示恢复压缩前的格式,也可以是系统自适应。
  • 数据透视表
    • pd.pivot_table(df,values='字段',index='作为行标题的字段',columns='作为列标题的字段',aggfunc='聚合方式')
  • inplace参数
    • 大部分API里面都有一个参数:inplace,意思是替换原数据,当数据修改无误交付时可以使用。指定inplace=True,可以节约内存空间。
  • 分类
    • np.select([条件区域],[赋值区域])
    • 使用场景:按分数分为几段,>85是优秀,>70是良好,>=60是及格,<60是不及格
    • np.select([df['成绩']>85,df['成绩']>70,df['成绩']>=60,df['成绩']<85],['优秀','良好','及格','不及格']),分类好可以添加为新字段,作为透视表的字段
  • 数据切分函数:cut
    • 等距分箱:pd.cut(df['成绩'],bins=10), 10个为一组,可以指定其他数量,
    • 指定区间分箱:pd.cut(df['成绩'],bins=[60,80,100,120],right=False,labels=['不及格','及格','良好','优秀']), 按照指定的区间分组,right这个参数表示[分段),左闭右开,label表示返回的分组内容
2.3.9 时间函数
  • import datetime
    • 相差天数:(df[‘时间1’]-df[‘时间2’]).dt.days
    • 相差秒数:(df[‘时间1’]-df[‘时间2’]).dt.seconds
    • 可以指定其他单位
    • 年月日:df[‘时间’].dt.date
    • 时分秒:df[‘时间’].dt.time
    • 年:df[‘时间’].dt.year
    • 可以指定其他单位
  • 时间格式化:日期格式符号参考数据库
    • df[‘时间’].dt.strftime(%Y-%m-%d)
  • 时间反格式化:
    • pd.to_datetime(df[‘时间’])
2.3.10 数据格式化和映射
  • 数据格式化
    • df[字段].round(2):保留两位小时,可以指定位数
    • df.round({字段1:保留位数,字段2:保留位数···}
  • 映射
    • df[字段].map(lamda ele : f’{ele: . 保留百分位数%}'),map和apply只能指定单字段
    • df[[字段1,字段2]].applymap(lamda ele:f’{ele: . 保留百分位数%}'),可以指定多字段
2.3.11 偏移和窗口函数
  • 偏移

  • df[字段].shift(数字):向下偏移或向上偏移,默认是1,向下偏移一个,负数是向上偏移,可以指定数字

  • 窗口函数

    • rolling函数:df.rolling(window=数字,min_periods=数字) ,window是窗口大小,min_periods是不足窗口时,按几个进行计算

    • 计算近三天的平均值,也可以指定其他聚合方式

      # 计算近三天的平均值,前天,昨天,今天
      df.rolling(window=3,min_periods=1).mean()
      # 如果时间不连续,会计算连续的三个行
      # 为了保证时间的连续,可以制造一个时间序列表,和df连接
      pd.date_range('起始时间','结束时间',freq='1D') # 也可以指定其他时间单位,或者几天
      

3. Matplotlib和Pyecharts

3.1 matplotlib

3.2 pyecharts

重点使用,

四、Bi和Excel

五、机器学习

1. 回归

1.1 一元线性回归

# 1.导包
from sklearn.linear_model import LinearRegression
# 2.构建对象
lr = LinearRegression()
# 3.训练
lr.fit(x,y)
# 4.验证
lr.predict(x)
# 可以得到一个y值,可以验证原来x对应的y是否和模型得出来的y一样

# 获取系数
lr.coef_
# 获取截距
lr.intercept_

# 注意:x一定是一个二维的数组

1.2 一元二次及多次线性回归

# 1.导包
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures # 用来提升数据维度
# 2.数据预处理
# 需要先将x构造成2次方的数组,所以提升数据维度

# 2.1构建数据预处理对象
p = PolynomialFeatures(degree=2)
# 2.2提升x的数据维度,生成是【x的0次方,x的1次方,x的2次方】
x_ = p.fit_transform(x) 
# 3.构建模型对象
lr = LinearRegression()
# 4.训练
lr.fit(x_,y)
# 5.使用模型获取x_对应的y_
y_ = lr.predict(x_)
# 6.绘图,使用新的x_,y_绘图
plt.scatter(x,y)
plt.plot(x,y_)

1.3 线性回归模型评估

核心指标:R-squared 或者Adj.R-squared,P>|t|

说明:这两个R指标越高,说明模型的拟合程度越高;如果P值越低,说明该特征变量的显著性越高,相关性越高

# 1.导包
from statsmodels import api as sm
# 2.将x进行转换
x1 = sm.add_constant(x)
# 3.评估
result = sm.OLS(y,x1).fit()
# 4.打印评估摘要
result.summary()

2. 分类

2.1 KNN近邻算法

寻找离样本最近的k个数据,如果k个点上的大多数属于一个类别,则该样本也属于这个类别。

2.2 使用

# 提前准备好数据
# 导包
from sklearn.neighbors import KNeighborsClassifier as KNN
# 构建模型对象
knn = KNN(n_neighbors=3) # 可设置其他数值
# 训练
knn.fit(x,y)
# 测试
knn.predict(x)

2.3 数据预处理 – 数据归一化

  • min - max 标准化:把原始数据转换到[0,1]区间内

    • 公式:x* = (X - Xmin) / (Xmax - Xmin)

    • 代码实现:

      # 导包
      from sklearn.preprocessing import MinMaxScaler
      # 数据预处理
      df = pd.read_excel('葡萄酒.xlsx')
      x = df.iloc[:,1:3]
      y = df['分类']
      # 数据归一化
      m = MinMaxScaler()
      new_x = m.fit_transform(x)
      
  • Z - score标准化:均值归一化,归一化后的数据符合标准正态分布

    • 公式:x* = (X - mean) / 标准差(std)

    • 代码实现

      # 导包
      from sklearn.preprocessong import StandardScaler
      # 数据预处理
      s = StandardScaler()
      new_x = s.fit_transform(x)
      

2.4 案例:手写数据识别

2.4.1 图片读取
  • 使用matplotlib展示图片
# 导包
from matplotlib import pyplot as plt
# 读取图片信息
num4 = plt.imread('数字4.png')
# 展示图片
plt.imshow(num4)
  • 使用Image展示图片
# 导包
from PIL import Image
# 读取图片
num4 = Image.open('数字4.png')
# 可以直接展示
2.4.2 案例实战
  • 一个数字识别的
# 导包
import pandas as pd
import numpy as np
from PIL import Image
# 读取图片
num4 = Image.open('数字4.png')
# 压缩图片,不会改变数字的内容
num4_1 = num4.resize((32,32))
# 把文字的颜色转化成黑白色,就是灰度图,对数字本身没有影响
num4_2 = num4_1.convert('L')
# 图像颜色数值化,二值化,使用0和1表示
num4_3 = num4.point(lambda x: 1 if x < 128 else 0)
# 转化成数组
data = np.array(num4_3) 
# 可以遍历data,排列出来的是数字的样子
# 把数组转化成一维的,就是x
x = data.reshape(-1)
y = [4]
# 可以使用次数据训练模型
  • 5000个数字图片的处理
# 把单个操作的步骤封装成函数
def get_number(path):
    n = Image.open(path)
    n1 = n.resize((32,32))
    n2 = n1.convert('L')
    n3 = n2.point(lambda x: 1 if x<128 else 0)
    data = np.array(n3)
    return data.reshape(-1)
X = []
y = []
for i in range(10):
    for j in range(1,501):
        path = f'./data/{i}/{i}_{j}.bmp'
        x = get_number(path)
        X.append(x)
        y.append(i)
        
# 循环结束后,把所有x构建成二维表格
df = pd.DataFrame(X)
# 添加一行y列
df['y'] = y
# 可以保存一下
df.to_excel('5000图片数据集.xlsx')
# ============================================
# 把数据读出来
df1 = pd.read_excel('5000图片数据集.xlsx')
# 准备数据
x = df1.iloc[:,1:-1]
y = df1['y']
# 把数据拆分,80%做训练集,20%做测试集
from sklearn.model_selection import train_test_split
# 返回结果有四个,训练组:x,y和测试组:x,y,用四个变量接
x_train,x_test,y_train,y_test = train_test_split(x,y,train_size=0.8)
# 构建模型对象
from sklearn.neighbors import KNeighborsClassifier as KNN
knn = KNN(n_neighbors=5)
# 训练
knn.fit(x_train,y_train)
# 测试
y_cs = knn.predict(x_test)
# 把测试组的y_test和得到的y_cs做对比
df2 = pd.DataFrame()
df2['真实值'] = y_test
df2['测试值'] = y_cs
# 得到准确率
sum(df2['测试值'] == df2['真实值'])/1000 # 0.917

# 模型自带测试工具
knn.score(x_test,y_test) # 0.917

3. 逻辑回归

3.1 介绍及作用

  • 逻辑回归是分类模型,和回归模型的区别是其预测的变量不是连续的,而是离散的,常见的是二分类,比如预测一个人是否会违约,客户流失等等。
  • 常见的分类值
    • 二分类:0和1 -1和1
    • 三分类:-1、 0、 1

3.2 二分类使用

x = [[1,0],[5,1],[6,4],[4,2],[3,2]]
y = [0,1,1,0,0]
# 导包
from sklearn.linear_model import LogisticRegression
# 构建模型对象
lr = LogisticRegression()
# 训练对象
lr.fit(x,y)
# 预测
lr.predict(x)
# array([0, 1, 1, 0, 0])
# 查看预测的各个分类的概率
y_pre = lr.predict_proba(x) # 返回一个二维数组
# 构造一个二维表格查看
pd.DataFrame(y_pre,columns=['像0的概率','像1的概率'])
​```
	像0的概率	像1的概率
0	0.973449	0.026551
1	0.390720	0.609280
2	0.179910	0.820090
3	0.631679	0.368321
4	0.824245	0.175755
​```
# ======================================
# 获取系数
lr.coef_
# array([[1.00595248, 0.02223835]])
# 获取截距
lr.itercept_
# array([-4.60771284])

3.3 三分类使用

x = [[1,0],[5,1],[6,4],[4,2],[3,2]]
y = [-1,0,1,1,1]
# 导包
from sklearn.linear_model import LogisticRegression
# 构造模型对象
lr = LogisticRegression()
# 训练模型
lr.fit(x,y)
# 预测
lr.predict(x)
# array([-1,  0,  1,  1,  1])
# 查看预测的各个分类的概率
lr1.preict_proba(x)
# array([[0.77989964, 0.06951891, 0.15058145],
#       [0.05153899, 0.57448034, 0.37398067],
#       [0.00189703, 0.06588155, 0.93222142],
#       [0.05742265, 0.18534914, 0.75722821],
#       [0.1092406 , 0.10476833, 0.78599106]])

#============================================
# 获取系数
lr1.coef_
# array([[-6.06478987e-01, -3.92490839e-01],
#       [ 6.07124722e-01, -4.18211939e-01],
#       [-6.45735217e-04,  8.10702778e-01]])
# 获取截距
lr1.intercept_
# array([ 1.96055486, -1.67061533, -0.28993953])

3.4 案例:股票客户流失预警

# 读取表格
df = pd.read_excel('股票客户流失.xlsx')
# 构造数据
y = df['是否流失']
x = df.drop(columns='是否流失')
# 切分数据:分为训练组和测试组
from sklearn.model_selection import train_test_split
x_train,x_test,y_train,y_test = train_test_split(x,y,test_size=0.2,random_state=110)
# 构造模型对象
lr = LogisticRegression()
# 训练模型
lr.fit(x_train,y_train)
# 查看预测的准确率
lr.score(x_test,y_test)
# 0.7757274662881476
# 查看各类型的概率,构造一个二维表格查看
pd.DataFrame(lr.predict_proba(x_test),columns=['不流失概率','流失概率'])
​```
	不流失概率	流失概率
0	0.695059	0.304941
1	0.917619	0.082381
2	0.960047	0.039953
3	0.869448	0.130552
4	0.913229	0.086771
...	...	...
1404	0.752608	0.247392
1405	0.221987	0.778013
1406	0.752541	0.247459
1407	0.946135	0.053865
1408	0.478933	0.521067
1409 rows × 2 columns
​```

3.5 ROC曲线和KS曲线

  1. 混淆矩阵

    1(预测流失)0(预测不流失)合计
    1(实际流失)True Positive(TP) 正确肯定False Negative(FN)漏报TP + FN
    0(实际不流失)False Positive(FP) 虚报True Negative(TN) 正确否定FP + TN
    合计TP + FPFN + TN

    真正率:TPR = TP / (TP + FN)

    假正率:FPR = FP / (FP + TN)

  2. 代码查看混淆矩阵

    from sklearn.metrics import confusion_matrix
    data = confusion_matrix(y_test,lr2.predict(x_test))
    pd.DataFrame(data,columns=['预测0','预测1'],index=['实际0','实际1']).sort_index(ascending=False).sort_index(axis=1,ascending=False)
    # 		预测1		预测0
    # 实际1	164		225
    # 实际0	91		929
    
  3. 分类报告

    # 自带的计算真正率和假正率
    from sklearn.metrics import classification_report
    print(classification_report(y_test,lr2.predict(x_test)))
    
    

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  4. ROC曲线

    from sklearn.metrics import roc_curve
    fpr,tpr,thre = roc_curve(y_test,lr2.predict_proba(x_test)[:,1])
    # 绘制曲线:fpr是x轴,tpr是y轴,曲线越陡越好
    plt.plot(fpr[1:],tpr[1:])
    # 曲线与x轴形成的面积较AUC,面积越大越好, 曲线下的面积,阈值0.75就可以使用了,0.85就是很不错的模型了
    
  5. KS曲线

    # 以上述数据构造二维表格
    df5 = pd.DataFrame({
        '阈值':thre,
        'fpr':fpr,
        'tpr':tpr
    })
    # 添加列:两者的差值ks值形成的曲线,
    df5['tpr-fpr'] = df5['tpr'] - df5['fpr']
    # 绘制
    plt.plot(df5['阈值'][1:],df5['tpr'][1:])
    plt.plot(df5['阈值'][1:],df5['fpr'][1:])
    plt.plot(df5['阈值'][1:],df5['tpr-fpr'][1:])
    plt.gca().invert_xaxis() # 反转横轴
    
    # 获取ks值最大的点
    df5['tpr-fpr'].max() # 最大值
    # 获取最大ks值对应的信息
    df5[df5['tpr-fpr'].max() == df5['tpr-fpr']]
    # 查看阈值
    		阈值		fpr			tpr			tpr-fpr
    330	0.175211	0.377451	0.820051	0.4426
    - KS值小于0.2,一般认为模型区分能力较弱。
    - KS值在[0.2,0.3]区间内,模型具有一定区分能力。
    - KS值在[0.3,0.5]区间内,模型具有较强的区分能力。
    

4.决策树

4.1 分类

  • 主要用来分类,也可以用来做回归

4.2 分类决策树使用

# 导包
from sklearn.tree import DecisionTreeClassifier
# 数据准备
x = [[1,2],[3,4],[5,6],[7,8],[9,10]]
y = [1,0,0,1,1]
# 构建模型对象,参数是随机种子
d = DecisionTreeClassifier(random_state=0) 
# 训练
d.fit(x,y)
# 预测
d.predict([[5,5]])
# array([0])

4.3 回归决策数的使用

# 导包
from sklearn.tree import DecisionTreeRegressor
# 数据准备
x = [[1,2],[3,4],[5,6],[7,8],[9,10]]
y = [1,2,3,4,5]
# 构建模型对象,参数是随机种子,和最大深度
d = DecisionTreeClassifier(random_state=0,max_depth=2) 
# 训练
d.fit(x,y)
# 预测
d.predict([[9,9]])
# array([4.5])

4.4 案例:员工离职预警

# 读取数据
import pandas as pd
df = pd.read_excel('员工离职预测模型.xlsx')
y = df['离职']
x = df.drop(columns='离职')
# 拆分数据,分为训练集和测试集
from sklearn.model_selection import train_test_split
x_tr,x_te,y_tr,y_te = train_test_split(x,y,random_state=110,test_size=0.2)
# 构建树模型
from sklearn.tree import DecisionTreeClassifier
d = DecisionTreeClassifier(random_state=110,max_depth=3)
# 训练模型
d.fit(x_tr,y_tr)
# 查看预测的准确率
d,score(x_te,y_te)	# 0.948
# 查看特征重要性
tz = d.feature_importances_
pd.DataFrame({
    '特征':x.columns,
    '重要性':tz
})

# 	特征	重要性
0	工资	0.000000
1	满意度	0.590981
2	考核得分	0.144819
3	工程数量	0.104355
4	月工时	0.005114
5	工龄	0.154730

4.5 绘制ROC曲线

# 查询fpr值,tpr值,阈值
from sklearn.metrics import roc_curve
fpr,tpr,thre = roc_cunrve(y_te,d.predict_proba(x_te)[:,1])
# 绘制ROC,x轴是fpr,y轴是tpr
from matplotlib import pyplot as plt
plt.plot(fpr[1:],tpr[1:])

4.6 查看树模型(学习使用,工作不常用)

# 导包
import graphviz
from sklearn.tree import export_graphviz
# 构造文本
data = export_graphviz(d,feature_names=x_tr.columns)
# 查看树模型
graphviz.Source(data)
# 也可以导出为pdf
tt = graphviz.Source(data)
tt.render('地址')

4.7 参数调优

4.7.1 交叉验证
  • 简介

    ​ 在机器学习中,因为训练集和测试集划分数据是随机的,所以我们有时会重复的使用数据,来更好的评估模型的有效性,并选出最好的模型,该做法称之为交叉验证。

    ​ 具体而言就是对原始样本数据进行切分,然后组合成为多组不同的训练集和测试集,用训练集训练模型,用测试集评估模型。

    ​ 某次的训练集可能是下次的测试集,固尔称作交叉验证。

  • 分类

    • 简单交叉验证,K折交叉验证,留一交叉验证

    • k折交叉验证应用最为广泛,通常来说,如果训练集数据较小,则增大K值,这样在迭代的过程中会有更多的数据用于模型训练,同时算法时间延长;如果训练集数据较大,则减小K值,这样降低模型在不同的数据数据块上进行重复拟合的性能评估的计算成本,在平均性能的基础上获得模型的准确评估。

  • K折交叉验证:目的是在多个模型存在的时候,能够选择出最好的模型

    # 导包
    from sklearn.mod_selection import cross_val_score
    # 构建模型对象
    d = DecisionTreeClassifier(max_depth=3,random_state=110)
    # 进行K折交叉验证
    acc = cross_val_score(d,x,y,cv=5) # cv=5表示k值为5
    # 查看模型的平均评分
    acc.mean()
    # 0.9524666666666667 略高于上述评分
    
4.7.2 网格搜索调优
  • 简介

    • 网格搜索是一种穷举搜索的调参手段:遍历所有的候选参数,循环建立模型并对模型的有效性和准确进行评估,选取表现最好的参数作为最终的结果。
    • 多参数调优
  • 操作:

    • 单参数调优

      # 导包
      from sklearn.model_selection import GridSearchCV
      # 参数对象
      grid_par = {'max_depth':[1,3,5,7,9]}
      # 模型对象
      d = DecisionTreeClassifier()
      # 调参,cv=5表示k值为5,使用的是k折交叉的知识
      gs = GridSearchCv(d,grid_par,cv=5)
      # 训练模型
      gs.fit(x_tr,y_tr)
      # 查看模型的最佳参数
      gs.bast_params_
      # 查看准确率
      gs.best_score_
      
    • 多参数调优

      # 导包
      from sklearn.model_selection import GridSearchCV
      # 参数对象
      grid_par = {'max_depth':[1,3,5,7,9],'min_samples_split':[5,10,15,20]}
      # 模型对象
      d = DecisionTreeClassifier()
      # 调参,cv=5表示k值为5,使用的是k折交叉的知识
      gs = GridSearchCv(d,grid_par,cv=5)
      # 训练模型
      gs.fit(x_tr,y_tr)
      # 查看模型的最佳参数
      gs.bast_params_
      # 查看准确率
      gs.best_score_
      
  • 模型调优的方式:

    • 1.调数据 2.调参数 3.换模型
    • 参数调优的时候,逐步步进

    假正率:FPR = FP / (FP + TN)

  1. 代码查看混淆矩阵

    from sklearn.metrics import confusion_matrix
    data = confusion_matrix(y_test,lr2.predict(x_test))
    pd.DataFrame(data,columns=['预测0','预测1'],index=['实际0','实际1']).sort_index(ascending=False).sort_index(axis=1,ascending=False)
    # 		预测1		预测0
    # 实际1	164		225
    # 实际0	91		929
    
  2. 分类报告

    # 自带的计算真正率和假正率
    from sklearn.metrics import classification_report
    print(classification_report(y_test,lr2.predict(x_test)))
    
    

    [外链图片转存中…(img-373FKSNA-1705304209551)]

  3. ROC曲线

    from sklearn.metrics import roc_curve
    fpr,tpr,thre = roc_curve(y_test,lr2.predict_proba(x_test)[:,1])
    # 绘制曲线:fpr是x轴,tpr是y轴,曲线越陡越好
    plt.plot(fpr[1:],tpr[1:])
    # 曲线与x轴形成的面积较AUC,面积越大越好, 曲线下的面积,阈值0.75就可以使用了,0.85就是很不错的模型了
    
  4. KS曲线

    # 以上述数据构造二维表格
    df5 = pd.DataFrame({
        '阈值':thre,
        'fpr':fpr,
        'tpr':tpr
    })
    # 添加列:两者的差值ks值形成的曲线,
    df5['tpr-fpr'] = df5['tpr'] - df5['fpr']
    # 绘制
    plt.plot(df5['阈值'][1:],df5['tpr'][1:])
    plt.plot(df5['阈值'][1:],df5['fpr'][1:])
    plt.plot(df5['阈值'][1:],df5['tpr-fpr'][1:])
    plt.gca().invert_xaxis() # 反转横轴
    
    # 获取ks值最大的点
    df5['tpr-fpr'].max() # 最大值
    # 获取最大ks值对应的信息
    df5[df5['tpr-fpr'].max() == df5['tpr-fpr']]
    # 查看阈值
    		阈值		fpr			tpr			tpr-fpr
    330	0.175211	0.377451	0.820051	0.4426
    - KS值小于0.2,一般认为模型区分能力较弱。
    - KS值在[0.2,0.3]区间内,模型具有一定区分能力。
    - KS值在[0.3,0.5]区间内,模型具有较强的区分能力。
    

4.决策树

4.1 分类

  • 主要用来分类,也可以用来做回归

4.2 分类决策树使用

# 导包
from sklearn.tree import DecisionTreeClassifier
# 数据准备
x = [[1,2],[3,4],[5,6],[7,8],[9,10]]
y = [1,0,0,1,1]
# 构建模型对象,参数是随机种子
d = DecisionTreeClassifier(random_state=0) 
# 训练
d.fit(x,y)
# 预测
d.predict([[5,5]])
# array([0])

4.3 回归决策数的使用

# 导包
from sklearn.tree import DecisionTreeRegressor
# 数据准备
x = [[1,2],[3,4],[5,6],[7,8],[9,10]]
y = [1,2,3,4,5]
# 构建模型对象,参数是随机种子,和最大深度
d = DecisionTreeClassifier(random_state=0,max_depth=2) 
# 训练
d.fit(x,y)
# 预测
d.predict([[9,9]])
# array([4.5])

4.4 案例:员工离职预警

# 读取数据
import pandas as pd
df = pd.read_excel('员工离职预测模型.xlsx')
y = df['离职']
x = df.drop(columns='离职')
# 拆分数据,分为训练集和测试集
from sklearn.model_selection import train_test_split
x_tr,x_te,y_tr,y_te = train_test_split(x,y,random_state=110,test_size=0.2)
# 构建树模型
from sklearn.tree import DecisionTreeClassifier
d = DecisionTreeClassifier(random_state=110,max_depth=3)
# 训练模型
d.fit(x_tr,y_tr)
# 查看预测的准确率
d,score(x_te,y_te)	# 0.948
# 查看特征重要性
tz = d.feature_importances_
pd.DataFrame({
    '特征':x.columns,
    '重要性':tz
})

# 	特征	重要性
0	工资	0.000000
1	满意度	0.590981
2	考核得分	0.144819
3	工程数量	0.104355
4	月工时	0.005114
5	工龄	0.154730

4.5 绘制ROC曲线

# 查询fpr值,tpr值,阈值
from sklearn.metrics import roc_curve
fpr,tpr,thre = roc_cunrve(y_te,d.predict_proba(x_te)[:,1])
# 绘制ROC,x轴是fpr,y轴是tpr
from matplotlib import pyplot as plt
plt.plot(fpr[1:],tpr[1:])

4.6 查看树模型(学习使用,工作不常用)

# 导包
import graphviz
from sklearn.tree import export_graphviz
# 构造文本
data = export_graphviz(d,feature_names=x_tr.columns)
# 查看树模型
graphviz.Source(data)
# 也可以导出为pdf
tt = graphviz.Source(data)
tt.render('地址')

4.7 参数调优

4.7.1 交叉验证
  • 简介

    ​ 在机器学习中,因为训练集和测试集划分数据是随机的,所以我们有时会重复的使用数据,来更好的评估模型的有效性,并选出最好的模型,该做法称之为交叉验证。

    ​ 具体而言就是对原始样本数据进行切分,然后组合成为多组不同的训练集和测试集,用训练集训练模型,用测试集评估模型。

    ​ 某次的训练集可能是下次的测试集,固尔称作交叉验证。

  • 分类

    • 简单交叉验证,K折交叉验证,留一交叉验证

    • k折交叉验证应用最为广泛,通常来说,如果训练集数据较小,则增大K值,这样在迭代的过程中会有更多的数据用于模型训练,同时算法时间延长;如果训练集数据较大,则减小K值,这样降低模型在不同的数据数据块上进行重复拟合的性能评估的计算成本,在平均性能的基础上获得模型的准确评估。

  • K折交叉验证:目的是在多个模型存在的时候,能够选择出最好的模型

    # 导包
    from sklearn.mod_selection import cross_val_score
    # 构建模型对象
    d = DecisionTreeClassifier(max_depth=3,random_state=110)
    # 进行K折交叉验证
    acc = cross_val_score(d,x,y,cv=5) # cv=5表示k值为5
    # 查看模型的平均评分
    acc.mean()
    # 0.9524666666666667 略高于上述评分
    
4.7.2 网格搜索调优
  • 简介

    • 网格搜索是一种穷举搜索的调参手段:遍历所有的候选参数,循环建立模型并对模型的有效性和准确进行评估,选取表现最好的参数作为最终的结果。
    • 多参数调优
  • 操作:

    • 单参数调优

      # 导包
      from sklearn.model_selection import GridSearchCV
      # 参数对象
      grid_par = {'max_depth':[1,3,5,7,9]}
      # 模型对象
      d = DecisionTreeClassifier()
      # 调参,cv=5表示k值为5,使用的是k折交叉的知识
      gs = GridSearchCv(d,grid_par,cv=5)
      # 训练模型
      gs.fit(x_tr,y_tr)
      # 查看模型的最佳参数
      gs.bast_params_
      # 查看准确率
      gs.best_score_
      
    • 多参数调优

      # 导包
      from sklearn.model_selection import GridSearchCV
      # 参数对象
      grid_par = {'max_depth':[1,3,5,7,9],'min_samples_split':[5,10,15,20]}
      # 模型对象
      d = DecisionTreeClassifier()
      # 调参,cv=5表示k值为5,使用的是k折交叉的知识
      gs = GridSearchCv(d,grid_par,cv=5)
      # 训练模型
      gs.fit(x_tr,y_tr)
      # 查看模型的最佳参数
      gs.bast_params_
      # 查看准确率
      gs.best_score_
      
  • 模型调优的方式:

    • 1.调数据 2.调参数 3.换模型
    • 参数调优的时候,逐步步进
  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值