Python基础

一、计算机硬件基础及进制转换:

  • cpu:中央处理器,相当于人的大脑,运算中心,控制中心。
  • 内存:临时数据存储。优点:读取速度快。缺点:容量小,造价高,断电数据即消失。
  • 硬盘:长期存储数据,优点:容量大,造价相对低,断电数据不会消失。缺点:读取速度慢。
  • 操作系统:统一管理计算机软件和硬件资源的程序。

1、计算机大小单位转换:

b = bit 位(比特);B = Byte 字节

1Byte = 8bit;1KB = 1024B;1MB = 1024KB;1GB = 1024MB;1TB = 1024GB;1PB = 1024TB;1EB = 1024PB

2、计算机进制转换:

二进制:0b表示 0b101 = 1*2^0 + 0*2^1 + 1*2^2

八进制:0o表示 0o127 = 7*8^0 + 2*8^1 + 1*8^2

十进制:正常显示

十六进制:0x显示 0xFF = 15*16^0 + 15*16^1 = 255

二进制转换八进制:(三位原则:指二进制的每三位对应一位八进制数)

二进制与八进制对应关系:
八进制    二进制
0        000
1        001
2        010
3        011
4        100
5        101
6        110
7        111

二进制转换十六进制:(四位原则:指二进制的每四位对应一位十六进制数)

二进制与十六禁止的对应关系:
十六进制    二进制
0          0000
1          0001
2          0010
3          0011
4          0100
5          0101
6          0110
7          0111
8          1000
9          1001
a          1010
b          1011
c          1100
d          1101
e          1110
f          1111

八进制转换十六进制:

先转换二进制然后通过二进制转换

3、原码,反码,补码:

原码:二进制的表现形式

​ 原码特点:第一位是1
​ 0000... 1 表达数字正1
​ 1000... 1 表达数字负1

反码:二进制码0与1互相转化

补码:二进制的存储形式

​ 补码特点:高位都是1
​ 0000... 1 表达数字正1
​ 1111... 1 表达数字负1

注:原码与补码转化为反码时首位符号位不变

运算的顺序:补码 —> 原码 —> 显示的内容

转换规律:

​ 正数的原码,反码补码相同;

​ 负数的原码与补码之间是互为取反加1的关系:

​ 原码 = 补码取反加1

​ 补码 = 原码取反加1

注:进制转换的时候需要先把内存存储的补码拿出来变成原码在进行转换输出

二、Python软件的介绍

​ Python是一种跨平台的计算机程序设计语言。 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。最初被设计用于编写自动化脚本(shell),随着版本的不断更新和语言新功能的添加,越多被用于独立的、大型项目的开发。

1、python版本

python 2.X 版本,官方在2020年停止支持,原码不规范,重复较多

python 3.X 版本,功能更强,修复了很多bug,原码清晰。

2、编译型语言和解释型语言的区别:

编译型:一次性把所有代码编译成可识别的二进制代码,再执行

如:C, C++,java

解释型:代码从上到下一行一行的解释并运行

如:python,javascript,php

3、python的解释器:

(1)Cpython(官方推荐,也是最常用的):是把python代码转化成C语言可识别的二进制代码

(2)Jpython:是把python代码转化成Java语言可识别的二进制代码

(3)其他语言解释器:把python转化成其它语言能识别的二进制码

(4)PyPy:将所有代码一次性编译成二进制码,加快执行效率(模仿编译型语言的解释器)

三、Python的数据类型

1、变量

1.1 变量的概念

​ 可以改变的量,实际指代的是内存中的空间

rujia_305 = "王闻"
rujia_305 = "主胜"
print(rujia_305)

1.2 变量的声明

第一种命名方式
    a = 100
    b = 200
    print(a)
    print(b)
第二种命名方式
    a,b = 100,200
    print(a,b)
第三种命名方式
    a = b = 500
    print(a,b)

1.3 变量的命名规则

​ 字母数字下划线,首字符不能为数字,严格区分大小写,不能使用关键字,变量命名有意义,且不能使用中文名命。

1.4 关键字

#import 引入 keyword模块
import keyword
print(keyword.kwlist)
"""
'False', 'None', 'True', 'and', 
'as', 'assert', 'break', 'class',
'continue', 'def', 'del', 'elif', 
'else', 'except', 'finally', 'for', 
'from', 'global', 'if', 'import', 
'in', 'is', 'lambda', 'nonlocal', 
'not','or', 'pass', 'raise', 'return', 
'try', 'while', 'with', 'yield'
"""

注:变量的名命不能使用关键字;Python 支持中文,但是不能使用中文

1.5 编码

​ Python 支持中文,但严禁使用中文(容易出现乱码占用空间)

​ utf-8 国际标准编码(可变长的unicode编码)中文字符占用3个字节,英文数字特殊符号占用1个字节

​ gbk 国标编码,中文字符占用2个字节,英文或者符号占用1个字节

1.6 变量的转换

# 通用写法
a = 20
b = 21
temp = a
a = b
b = temp
print(a,b)
# python特有
a = 23
b = 24
a,b = b,a
print(a,b)

1.7 常量的定义:约定俗称全都变成大写

​ CHANGLIANG = “nepenthe”

2、六大数据类型

2.1 type 获取数据类型的方法:type()
2.2 id 获取值在内存中的地址的方法:id()
intvar = 25
print(type(intvar))
res = id(intvar)
print(res)
2.3 Number类型(int,float,bool,complex)

2.3.1 int 类型

intvar = 25
print(type(intvar))

# 二进制整型
inttowB = 0b10
print(inttowB)
print(type(inttowB))
print(id(inttowB))

# 八进制整型
inteightO = 0o127
print(inteightO)
print(type(inteightO))
print(id(inteightO))

# 十六进制整型
intsixthX = 0xA
print(intsixthX)
print(type(intsixthX))
print(id(intsixthX))

2.3.2 float 浮点型(小数)

# 表示方式一
floatvar = 3.14
print(floatvar)
print(type(floatvar))

# 表达方式二
floatvar = 5.1e4
print(floatvar)
print(type(floatvar))

2.3.3 bool 布尔类型; True 真 False 假;只有这两个值。

2.3.4 complex 复数:形如a+bj

# 复数表达方式一
complexvar = 3-10j
complexvar = 20j
print(complexvar)
print(type(complexvar))

# 表达方式二
complexvar = complex(3,-10)
print(complexvar)
print(type(complexvar))
2.4 容器数据类型

2.4.1 字符串:用引号引起来的就是字符串

(1)单引号字符串 ‘ ’
(2)双引号字符串 “ ”
(3)三引号字符串 ‘’‘ ’‘’ “”“ ”“” (三引号会保留原始格式)
(4)元字符串 r" " 不转义字符,原型化打印字符串
(5)格式化字符串 :“ 字符串 ” %(值1,值2,值3)

  • %d 整型占位符

    %2d 占两位 原字符串默认居右

    %-2d 占两位 原字符串默认居左

  • %f 浮点占位符(存在四舍五入的情况)

    %.2f 小数点保留两位小数

  • %s 字符串占位符

    2.4.1.1 转义字符: \ + 字符
    (1)\ 可将有意义的字符变成无意义
    (2)\ 可将无意义的字符变成有意义
    (3)\n, \r\n 换行;
    (4)\t 缩进 水平制表符(一般是四个空格的距离)
    (5)\r 将\r后面的字符直接拉到当前行行首

2.4.2 list列表(有序,可修改,可获取)

定义空列表:listvar = [] 或 listvar = list()

(1)索引

​ 正向索引:正向索引以 0 开始 0 1 2 3 4

​ 逆向索引:逆向索引以 -1 开始 -1 -2 -3 -4

(2)获取列表中的最后的元素:直接使用listvar [-1](python特有)
其他语言通用写法 通过len方法获取列表长度获取最后元素值 listvar [len(listvar )]

(3)修改列表元素值 listvar [0] = 赋值

2.4.3 tuple 元组(有序,不可修改,可获取)

定义空元组:tuplevar = () 或 tuplevar = tuple()

单纯的()不能代表元组类型,逗号才是元组的标识符:
tuplevar = 2,也是个元组
注:tuplevar =(1)不是元组,tuplevar = (1,)才是

(1)索引

​ 正向索引:正向索引以 0 开始 0 1 2 3 4

​ 逆向索引:逆向索引以 -1 开始 -1 -2 -3 -4

2.4.4 str字符串(有序,不可修改,可获取)

字符串的定义:strvar = “哈哈哈”

获取字符串中的元素:strvar[1]

元字符串:r“nepenthe ”;使用r声明的字符串是元字符串

2.4.5 set集合类型(作用:交叉并补;特点:无序,自动去重, )

# 定义集合
setvar = {"刘德华","郭富城","张学友","黎明"}
# 自动去重
setvar = {"刘德华","郭富城","张学友","黎明","刘德华","郭富城","张学友","黎明"}
print(setvar)
# 定义空集合
setvar = {}
setvar = set()
print(setvar, type(setvar))

2.4.6 dict字典类型(特点:以键值对存储数据,表面上有序,实际上无序)

​ 语法:divtvar = {键1:值1,键2:值2,. . . }

"""
字典的键:推荐用变量命名的字符串进行使用
字典的键和集合的值有数据类型上的要求
可哈希的数据类型可以做字典的键:Number(int,float,bool,complex),str,tuple
可变类型,不可哈希(不可被应用在set中和字典的键):list dict set
哈希算法都是典型的无序特征,目的:为了更加均匀的把数据分配到内存中,随机,无序,底层用了取模类似的算法。

python3.6版本,对字典做了优化,存储数据的时候用的哈希算法
但是在拿出数据的时候,重新按照定义的顺序做了排序,所以看起来有序,实际是无序的
"""

# 定义空字典
dictvar = {}
dictvar = dict()

# 定义普通字典
dictvar = dict{"name":"eric", "sex":"男", "age":"22", "hobby":"无"}

# 获取字典值
dictvar['name']

# 修改字典值
dictvar['name'] = "jack"

# 字典的键有一定要求
dictvar = {1:'asdf', False:'asd', 1+2j:'asd', 3.14:'asd', (1,2):"asd", "中文":"asd"}
2.5 可哈希,不可哈希

可哈希的都不可变,不可哈希的都可变

Number、String字符串、tuple元组是可哈希数据类型

dict字典、set集合、list列表是不可哈希数据类型

数值型数据类型:number、string、tuple

引用型:list、set、dict

3、 变量的缓存机制(仅对python3.6有效)

1. Number(int,float,bool,complex)类型
整型:-5 ~ 正无穷范围内的值相同  id 值一致
var1 = 1
var2 = 1

浮点数:非负数范围内的两个值相同时 id 值一致
var1 = 1.0
var2 = 1.0

布尔值:值相同的情况下,id一致
var1 = True
var2 = False
print(id(var1),id(var2))

复数对于 实数+虚数 这种结构中永不相同(只有虚数的情况例外)
intvar1 = 1+2j
intvar2 = 1+2j
intvar3 = 1j
intvar4 = 1j
print(id(intvar1),id(intvar2),id(intvar3),id(intvar4))
2. 容器类型
字符串和空元组相同的情况下,地址相同
var1 = "你"
var2 = "你"
var3 = ()
var4 = ()
print(id(var1),id(var2),id(var3),id(var4))

列表,元组,字典,集合无论什么情况id值都不同(空元组例外)
listvar1 = [1,2,3]
listvar2 = [1,2,3]
setvar3 = {1,2,3}
setvar4 = {1,2,3}
dictvar5 = {1:'2'}
dictvar6 = {1:'2'}
print(id())

4、 数据类型转换

1. Number(int,bool,float,complex)强制类型转换
类型转换方法:int(),bool(),float(),complex()

强制转换整型(纯数字类型字符串,浮点型,布尔型,整型)
var1 = int(1.0)
var2 = int('12')
var3 = int(False)
var4 = int(12)

强制转换浮点型(纯数字类型字符串,浮点型,布尔型,整型)
var1 = float(1.1)
var2 = float('12')
var3 = float(False)
var4 = float(12)

强制转换布尔型(纯数字类型字符串,浮点型,布尔型,整型)
var1 = bool(1.1)
var2 = bool('12')
var3 = bool(False)
var4 = bool(12)
强制转化布尔型为假的十种情况
0 0.0 False 0j () [] {} set() '' None

强制转换成复数(纯数字字符串,浮点型,布尔型,整形)
var1 = complex(1.1)
var2 = complex('12')
var3 = complex(False)
var4 = complex(12)
2. 自动类型转换Number(int float bool complex)

​ 转换原则:数据精度默认从低到高转换:bool int float complex

bool + int     布尔类型 + int整型 = 整型
ret = True + 12

bool + float   布尔类型 + float浮点型 = 浮点型
ret = True + 1.2 

bool + complex 布尔类型 + complex复数 = 复数
ret = True + 1+2j 

int + float    int整型 + float浮点型 = 浮点型
ret = 1 + 1.1 

int + complex  int整型 + complex复数 = 复数
ret = 1 + 1+2j 

float + complex float浮点型 + complex复数 = 复数
ret = 1.1 + 1+2j
3. 容器类型的强制转换(str tuple list set dict)
强制转化成str字符串(Number类型数据和容器类型数据都可以转化)
ret = str([1,2])

强制转化成元组(自身转化无变化)
ret = tuple("sfaf")    字符串强制转化成元组会拆分成单个字符 ("s","f","a","f")
ret = tuple([1,2,3])   列表强制转化成元组表面上看就是换了外面的括号
ret = tuple({1,2,3})   集合强制转化成元组表面上看就是换了外面的括号 
ret = tuple({1:'1',2:'2'})  字典强制转化成元组只会保留字典的键以元组的方式呈现
Number数据类型不能强制转化成元组

强制转化成列表(自身转化无变化)
ret = list("sfaf")     字符串强制转化成列表会拆分成单个字符 ("s","f","a","f")
ret = list((1,2,3))    元组强制转化成列表从表面上看就是换了外面的括号
ret = list({1,2,3})    集合强制转化成列表从表面上看就是换了外面的括号 
ret = list({1:'1',2:'2'})   字典强制转化成列表只会保留字典的键以元组的方式呈现
Number数据类型不能强制转化成列表

强制转化成集合(自身转化无变化)
ret = set("sfaf")      字符串强制转化成集合会被拆分成单个字符并自动去重 ("s","f","a")
ret = set([1,2,3])     列表强制转化成集合会自动去重,从表面上看外层括号会变成{}
ret = set((1,2,3))     元组强制转换成集合会自动去重,从表面上看外层括号会变成{}
ret = set({1:'1',2:'2'})    字典强制转化成列表只会保留字典的键以集合的方式呈现,并会自动去重

强制转化成字典(自身转化无变化)
注:强转成字典时,必须是等长的二级容器,元素个数为2
外层可以是列表、元组或集合,里面的容器是元组或者列表
ret = [(1,2,),[1,2]]
ret = ((1,2,),[1,2])
ret = {(1,2,),[1,2]}  # 注:集合中不能存在字典
如果里面出现集合(外层不是set集合{}),语法上是允许的,但有局限性
ret = ([1,2],{1,2})
如果里面出现字符串,语法上是允许的,但有局限性,字符串的长度只能是2
ret = ([1,2],'as')
4. 二级容器
二级列表
ret = [1,[2,3]]

二级元组
ret = (1,(2,3,),)

二级集合
ret = {1,{1,2}}

二级字典
ret = {1:'1',{1:'2'}}

多级容器就是嵌套使用

四、运算符

1、算术运算符

加+、减-、乘*、除/(结果永远是小数)、地板除 //、取余%、幂运算**(优先级最高)

地板除// :如果除数或者被除数存在小数,那结果上加上 .0

取余% :

​ 81 % 11 = 4

​ -81 % 11 = -4 + 11 =7

​ 81 % -11 = 4 + ( -11 ) = -7 (正数取余的余数的正负取决于被除数的正负,正数取余的余数加上除数的结果就是真正的余数)

​ -81 % -11 = -4 (如果被除数和除数都是负数,在正常得到的结果上加上负号)

var1 = 2
var2 = 4
加+
ret1 = var1 + var2
减-
ret2 = var1 - var2
乘*
ret3 = var1 * var2
除/
ret4 = var1 / var2
地板除 //
ret5 = var1 // var2
取余%
ret6 = var1 % var2
幂运算**
ret7 = var1 ** var2
print(ret1, ret2, ret3, ret4, ret5, ret6, ret7)

2、比较运算符

> < >= <= == != 只会产生两个结果:True和False

= :一个等号是赋值,把等号右边的值赋给左边

==:两个等号是在做比较,判断左右值是否相同

!=:判断两个值是否不等

var7 = 20
var8 = 20
ret8 = var7 > var8
ret9 = var7 < var8
ret10 = var7 >= var8
ret11 = var7 <= var8
ret12 = var7 == var8
ret13 = var7 != var8
print(ret8, ret9, ret10, ret11, ret12, ret13)

3、赋值运算符

= 等号、+=加等、-=减等、*=乘等、/=除等、//=地板除等、%=取余等、**=幂等

= 等号
rew1 = 12
rew2 = 2
+=加等
rew2 += rew1
-=减等
rew2 -= rew1
*=乘等
rew2 *= rew1
/=除等
rew2 /= rew1
//=地板除等
rew2 //= rew1
%=取余等
rew2 %= rew1
**=幂等
rew2 **= rew1
print(rew2)

4、成员运算符

in 和 not in (针对于容器类型数据)

注:字符串必须是一个连续的片段

list列表 tuple元组 set集合

dict字典(in 和 not in 在字典中只判断键不能判断值)

rew3 = "asdpwdzxc"
rew4 = ['asd', 'pwd', 'zxc']

in 和 not in 字符串
rew5 = "asd" in rew3
rew6 = "dsa" in rew3
rew5 = "asd" not in rew3
rew6 = "dsa" not in rew3

in 和 not in 列表(元组,集合)
rew7 = 'ass' in rew4
rew8 = 'asd' in rew4
rew7 = 'ass' not in rew4
rew8 = 'asd' not in rew4
print(rew5, rew6)
print(rew7, rew8)

in 和 not in 字典
rew9 = {'as': '阿斯蒂', 'zs': '安中学'}
rew10 = 'as' in rew9
rew11 = 'bs' in rew9
rew10 = 'as' not in rew9
rew11 = 'bs' not in rew9
print(rew10, rew11)

5、身份运算符

is 和 is not (检测两个数据在内存中是否是相同的地址)

整型,浮点型,布尔型
qwe1 = 90
qwe2 = 90
qwe1 = -90
qwe2 = -90
qwe1 = -90.9
qwe2 = -90.9
qwe1 = True
qwe2 = False

复数类型
qwe1 = 0j
qwe2 = 0j
qwe1 = 1 + 2j
qwe2 = 1 + 2j

容器类型
qwe1 = ()
qwe2 = ()
qaq1 = qwe1 is qwe2
qaq2 = qwe1 is not qwe2
print(qaq1, qaq2)

6、逻辑运算符

and逻辑与(&&)、or逻辑或(||)、not逻辑非(!)

and逻辑与(全真即真,一假即假)

or逻辑或(一真即真,全假即假)

!逻辑非(真变假,假变真)

逻辑短路:如果出现短路效果代码就不知执行了

​ True or 表达式 真

​ False and 表达式 假

注:print是内置函数,函数内部返回的是None,功能是打印

and逻辑与(&&)
qwe3 = True and False
qwe4 = False and False
qwe5 = True and True
qwe6 = False and False

or逻辑或(||)
qwe3 = True or False
qwe4 = False or False
qwe5 = True or True
qwe6 = False or False
print(qwe3, qwe4, qwe5, qwe6)

not逻辑非(!)
qwe7 = not True
qwe8 = not False

特殊的情况  短路
qwe7 = True or True
qwe8 = True or False
qwe7 = 5 or 6
qwe8 = 0 or 6
qwe7 = 5 and 6
qwe8 = 0 and 6
print(qwe7, qwe8)
逻辑运算的优先级

() > not > and > or

示例
qwe9 = 1 > 2 or 3 < 4 and 5 > 10 or 11 < 12 and 13 > 16
qwe9 = not(5 or 6) and 7
qwe9 = (5 or 6) and 7
qwe9 = 5 or 6 and 7
print(qwe9)

7、位运算符

按位与 &、按位或|、按位非~、按位异或^、左移<<(乘法操作)、右移>>(除法操作)

按位与&:只有同为1才换算成1,否则皆为0

按位或|:有一个1就换算成1,都不为0换算成0

按位异或^:两者之间不一样返回真,一样返回假

左移<<:相当于做乘法:a << b = a*2的b次幂

右移>>:相当于做除法:a >> b = a/2的b次幂

按位非~:针对补码进行按位取反,包括符号位。公式:-(n+1)

运算级:

(1)个别

​ 优先级最高 ** 幂运算

​ 优先级最低 = 赋值运算

​ ()括号可以提升优先级

(2)整体

​ 一元运算符 > 二元运算符

​ 一元运算符:同一时间,只操作一个值

​ 二元运算符:同一时间,操作两个值

(3)同一层级

​ 逻辑:() > not > and >or

​ 算数:乘除 > 加减

​ 位运算:(<< >>) > & > ^ > |

(4)其它情况

​ 算术运算符 > 位运算符 > 比较运算符 > 身份运算符 > 成员运算符 > 逻辑运算符

​ 赋值运算符用来将算好的值赋给等号左边的变量

asd1 = 19
asd2 = 15
print(asd1 & asd2) # 与

19的二进制
000 ... 10011        使用与关系:一假即假
15的二进制
000 ... 01111

000 ... 10011
000 ... 01111
000 ... 00011

print(asd1 | asd2) # 或

19的二进制           使用或关系:一真即真
000 ... 10011
15的二进制
000 ... 01111

000 ... 10011
000 ... 01111
000	...	11111

print(asd1 ^ asd2) # 异或:两者之间不一样返回真,一样返回假

19的二进制
000 ... 10011
15的二进制
000 ... 01111

000 ... 10011
000 ... 01111
000 ... 11100

asd3 = 5 << 4      # 左移
000 ... 101
000 ...1010  移动一位
000 ..10100  移动两位

asd4 = 5 >> 3      # 右移
000 ... 101
000 ... 010 移动一位
000 ... 001 移动两位

print(asd3, asd4)
asd5 = ~19         # 非
原码:000 ... 10011
反码:000 ... 10011
补码:000 ... 10011

补码:   000 ... 10011
按位非: 111 ... 01100

给补码求原码:
补码: 111 ... 01100
反码: 100 ... 10011
原码: 100 ... 10100  -20

asd6 = ~(-19)      # 非
原码: 100 ... 10011
反码: 111 ... 01100
补码: 111 ... 01101

补码:   111 ... 01101
按位非: 000 ... 10010

正数 : 补码 = 反码 = 原码
000 ... 10010  => 18

print(asd5, asd6)

示例
asd7 = 5 + 5 << 6 // 3 is 40 and True 
asd8 = (5 + 5) << (6 // 3) is 40 and True
print(asd7, asd8)

五、流程控制

1、类型的判断

用法1:isinstance(要判断的值,要判断的类型) 返回值是真True或假False

ret1 = isinstance(5, int)
ret2 = isinstance("abc", str)
print(ret1, ret2)

用法2:isinstance(要判断的值,(可能类型1,可能类型2,...)) 如果有一个类型满足就返回真True,否则就返回假False

var = "asd"
ret3 = isinstance(var, (int, str, list))
print(ret3)

2、代码块:以冒号作为开始,用缩进来划分作用域

作用域:作用的范围

代码块含义:代表某一块作用,通过缩进(缩进是4个空格)来区分

代码块注意点:可以使用缩进和空格来区分语句,但是缩进和空格不能混合使用

3、流程控制

流程:代码执行的过程

控制:对代码执行过程的控制

三大结构:

1、顺序结构:默认代码从上到下执行

2、分支结构:单项分支,双项分支,多项分支,巢状分支

​ 单项分支:if

​ 双向分支:if ... else ... (if成立就执行if对应的代码块,if不成立就执行else对应的代码块)

​ 多项分支:if ... elif(可嵌套多层elif) ... else

​ if成立就执行if对应的代码块,if不成立就执行elif对应的代码块(elif一次类推)..........,

​ elif对应的代码块不成立就执行else后的代码块(直到 if 或 elif 达到要求即停止运行,若

​ 未达到要求就执行else后代码块,多选一结构)

qaq = input("请输入:")

单项分支
if qaq == "a":
    print('aaa')
    
双向分支
if qaq == 'b':
    print("bbb")
else:
    print("ddd")
    
多项分支
if qaq == 'aqa':
    print('aqa')
elif qaq == 'qaq':
    print('qaq')
elif qaq == 'qqq':
    print('qqq')
else:
    print('ddd')

​ 巢状分支:单项分支,双项分支,多项分支的混合使用

巢状分支
ret4 = False
ret5 = True
ret6 = False
ret7 = False
ret8 = True

if ret4 == True:
    if ret5 == True:
        if ret6 == True:
            print('is' + ret6)
        else:
            if ret7 == True:
                print("is" + ret7)
    else:
        if ret8 == True:
            print('is' + ret8)

3、循环结构:(三部曲:初始化变量;循环的判断条件;自增自减的变量值)

​ while循环:通过True和False来决定是否执行while的代码块,True就执行,False就不执行

print(  ,end=‘’)可以自定制print后面的特殊符号如默认的:\n 换行符
i = 1
while i <= 10:
    print(i, end='')
    i += 1

total = 0
while i <= 100:
    total += i
    i += 1
print(total)

while死循环:条件永远为真
while True:
	print(1)

字符串拼接知识点:直接使用字符串进行拼接
strvar1 = "kjj"
strvar2 = "njj"
strvar3 = strvar1 + strvar2
strvar3 += "pdd"
print(strvar3)

取余的规律:对n取余   余数范围 0 ~ (n-1)
任意数和n进行地板除会出现n个相同的数字
i = 0
while i < 100:
    if i // 10 % 2 == 0:
        print("*", end='')
    else:
        print("⭐", end='')
    if i % 10 == 9:
        print()
    i += 1

​ 双层while循环:(使用时应注意判断条件和自增(自减)变量的位置)

# 打印九九乘法表
i = 1
while i <= 9 :
	j = 1
	while j <= i:
		print(  "%d * %d = %2d " % (i, j, i * j)  , end = "")
		j += 1
	print()	
	i += 1

​ 多层while循环

# 百钱百鸡问题
'''
公鸡1块钱1只,母鸡3块钱一只,小鸡5毛钱一只
问: 用100块钱买100只鸡,有多少种买法?
'''
x = 0
while x <= 100:
	y = 0
	while y <= 33:
		z = 0 
		while z <= 100:
			if (x + y + z == 100) and (x + y * 3 + z * 0.5 == 100):
				print(x, y, z)
			z+=1
		y+=1
	x+=1

​ pass,break,和continue的使用

'''
pass 的使用,多数用于占位,防止if、while、for...这些代码块因无执行的语句发生报错的情况,并且不会对代码块造成任何影响
'''
if 5 == 5:
	pass

'''
break 结束循环,不管while执行到了那部分语句,只要执行到break,那么程序就直接终止当前循环,并且不会执行任何break后面的语句(只能在循环中使用)
'''
i = 1
while i <= 3:
	j = 1
	while j <= 3:
		if j == 2:
			break
		print(i, j)
		j += 1
	i += 1

'''
continue 跳出当前循环,去执行下一次的循环(只能在循环中使用)
'''
i = 1
while i <= 10:
	if i == 5:
		# 手动加1,防止跳过下面的代码,产生没有自增的情况,出现死循环
		i += 1
		continue
	print(i)
	i += 1

4、for ... in 循环

​ 遍历,循环,迭代:都是把容器中的数据一个一个获取出来

​ 如果数据是无序的,while不能够遍历数据,for循环可以

​ 使用方式:for 变量 in (容器类型数据,range对象,迭代器)

# 遍历集合:container = {""};字符串;元组;列表
container = {"a", "b", "c", "d"}
container = "abcd"
container = ("a", "b", "c", "d")
container = ["a", "b", "c", "d"]
for i in container:
	print(container)
	
# 遍历字典:默认遍历的是字典的键
container = {"a": "b", "c": "d"}
for i in container:
	print(i)
    
# 变量的解包bc(注:字典只会获取键,变量要和元素一一对应)
a, b = 1, 2
a, b = [3, 4]
a, b = {"a": 111, "b": 222}
print(a, b)
a, b, c = ('王健林', '王思聪', '王美丽')
print(a, b, c)


# 遍历不等长的二级容器(使用双层循环,同理:多级容器可以使用多层循环来遍历)
lst = [("a", "b", "c"), ("d", "e"),("f", ) ]
for i in lst:
	for j in i:
		print(j)

​ range()方法的使用

# 默认从0开始遍历
for i in range(10):
	print(i)
    
# 最大值获取不到(取左不取右原则)
for i in range(1,10):
	print(i)
    
# 默认step步长为1是升序递增,步长为负是降序递减
for i in range(10,0,1):  # 1默认情况可以不写,升序递增 
	print(i)
for i in range(10,0,-1):  # 降序 递减 
	print(i)
# step取3的时候代表每隔3取一次值,就是在前一个值基础上加3的意思
for i in range(1,10,3):
	print(i)

5、while和for使用场景的区别(多数情况是可以通用的)

​ while多用于复杂的逻辑操作

​ for 多用于数据的遍历操作

​ 多数情况下 while和for可以通用

​ while 和 for 上的continue的使用有细微的差别,while需要手动使用自增(i+=1),for则不需要

i = 1
while i <= 10:
	if i == 5:
		i += 1
		continue	
	print(i)
	i += 1

for i in range(1, 11):
	if i == 5:
		continue 
	print(i)
    

六、字符串

1、字符串的操作

1)字符串的拼接(使用 +)
strvar1 = 'asd'
strvar2 = 'asd'
ret = strvar1 + strvar2
print(ret)
2)字符串的重复(使用 *)
strvar = 'qwe' * 2
print(strvar)
3)字符串的跨行拼接(使用 \)
strvar = 'sdfsdfasdfasfaasdfasfd' \
'asfdasfasfdasf'
print(strvar)
4)字符串的索引
strvar = 'qwertyuiopasdfghjkl'
print(strvar[0])
5)字符串的切片(取左不取右)
strvar = 'qwertyuiopasdfghjkl'

# 只有开始索引,结束索引默认到最后的下一位(结束索引-1)
ret1 = strvar[3:]
print(ret1)

# 只有结束索引,开始索引默认为0
ret2 = strvar[:6]
print(ret2)

# 从开始索引到结束索引(取左不取右)
ret3 = strvar[2:6]
print(ret3)

# 开始索引,结束索引,间隔值;以间隔值获取从开始索引到结束索引范围中的值
# 注:间隔值为负是降序递减截取,反向截取,默认是升序递增正向截取
ret4 = strvar[2:6:3]
print(ret4)

# 特殊情况:[:]或[::]会获取所有字符串
print(strvar[:])
print(strvar[::])

2、字符串的格式化format

1)顺序传参
strvar1 = "{}和{}"
ret = strvar1.format('1', '2')
print(ret)
2)索引传参
strvar1 = "{1}和{0}"
ret = strvar1.format('3', '4')
print(ret)
3)关键字传参
strvar1 = "{one}和{two}"
ret = strvar1.format(one='1', two='2')
print(ret)
4)容器类型传参
strvar1 = "{0[0]}和{1[0]}"
ret = strvar1.format(['1', '2'], ['3', '4'])
print(ret)

strvar1 = "{one[1]}和{two[1]}"
ret = strvar1.format(one=['1', '2'], two=['3', '4'])
print(ret)

# 注:字典使用的时候索引中不需要引号
strvar1 = "{one[a]}和{two[1]}"
ret = strvar1.format(one={'a': '2'}, two=('3', '4'))
print(ret)
5)format的填充符号的使用(^ > <)

​ ^ 原字符串居中

​ > 原字符串居右

​ < 源字符串居左

strvar = "{who:你^10}在长春长生公司{do:@>10},感觉{feel:!<10}"
res = strvar.format(who="吐血1号",do="扎疫苗",feel="血巢被掏空")
print(res)
6)进制转换符

​ %d 整型占位符(要求必须是整型,否则直接报错)

​ %f 浮点型占位符(要求必须是浮点型,否则直接报错)

​ %s 字符串占位符(要求必须是字符串,否则直接报错)

​ %, 金钱占位符(默认每三位一个逗号隔开从右向左)

# :d 占俩位右对齐
strvar = "母亲节,给自己的老妈,买了{:2d}个康乃馨"
res = strvar.format(1)
print(res)


# :f 保留两位小数,默认保留六位小数
strvar = "买花一共花了{:.2f}元"
res = strvar.format(9.999)
print(res)

# :s
strvar = "{:s}"
res = strvar.format("中国疫情正在渐渐的变好,但是美国的疫情一天不如一天")
print(res)

# :,
strvar = "{:,}"
res = strvar.format(12345)
print(res)

# 综合案例
strvar = "{:s}看好了一辆兰博基尼,价格是{:.1f}元,打算买{:d}个"
res = strvar.format('阿斯蒂',9.9,10)
print(res)

3、字符串的相关函数

3.1 基本函数:capitalize、title、upper、lower、swapcase、len、count、find、index、startswith、endswith

(1)capitalize字符串首字母大写

(2)title字符串每个单词首字母大写

(3)upper字符串所有字母都变成大写

(4)lower字符串所有字母都变成小写

(5)swapcase大小写互换

(6)len获取字符串长度

(7)count获取字符串中某一字符或某一段片段(连续)的数量。count(字符,开始,结尾)

(8)find查询字符串中某一字符或某一段片段(连续)的索引值,没有返回结果-1

(9)index查询字符串中某一字符或某一段片段(连续)的索引值,没有返回会报错

(10)startswith判断字符串是否是以某一个字符或某段字符串开始的,返回值为True或False

(11)endswith 判断字符串是否是以某一个字符或某段字符串结尾的,返回值为True或False

strvar2 = "how old are you"
# capitalize
ret5 = strvar2.capitalize()
print(ret5)

# title
ret6 = strvar2.title()
print(ret6)

# upper
ret7 = strvar2.upper()
print(ret7)

# lower
ret8 = strvar2.lower()
print(ret8)

# swapcase
strvar3 = "AbCdEf"
ret9 = strvar3.swapcase()
print(ret9)

# count
strvar4 = "你猜我猜你猜不猜你猜"
ret10 = strvar4.count("猜")
print(ret10)

# find
strvar3 = "AbCdEfafcfafbfrfefsfc"
ret11 = strvar3.find("A")
ret12 = strvar3.find("g")
ret13 = strvar3.find("fa")
ret13 = strvar3.find("fc")
ret13 = strvar3.find("fa", 6, 9)
print(ret11, ret12, ret13)

# index
# ret14 = strvar3.index('m')

# startswith(字符串, 开始, 结尾)
# endswith(字符串, 开始, 结尾)
strvar3 = "AbCdEfafcfafbfrfefsfc"
ret15 = strvar3.startswith("Ab")
ret16 = strvar3.endswith("fc")
print(ret15, ret16)

3.2 is相关函数(打印结果值为True或False)

(1)istitle 判断字符串是否每个单词都首字母大写

(2)isupper判断字符串所有字母是否都变成大写

(3)islower判断字符串所有字母是否都变成小写

(4)isalpha 判断字符串是否由字母和文字组成

(5)isdigit 检测字符串数是数字组成 接受二进制字节流

(6)isdecimal 检测字符串是否以数字组成 必须是纯数字

3.3 重要函数

(1)split:字符串分割方法,默认是按照空格进行分割;rsplit从右侧向左进行分割,可设置分割次数

(2)join:字符串拼接方法,可以把容器类型数据以某字符或某段字符串拼接成字符串

(3)center:填充字符串,原字符居中(默认填充空格)使用:cente(填充个数,填充的字符)

(4)strip:默认去掉首位两边的空白符;也可单侧去除,lstrip仅左侧,rstrip仅右侧

(5)replace:字符串替换

# split
strvar = "you can you up no can no bb"
lst = strvar.split()
strvar = "you-can-you-up-no-can-no-bb"
# 按照字符-分割
lst = strvar.split("-")
# 分割次数
lst = strvar.split("-",2) 
# 反向分割
lst = strvar.rsplit("-",2)
print(lst)

# join
lst = ['you', 'can', 'you', 'up', 'no', 'can', 'no', 'bb']
strvar = "&".join(lst)
print(strvar)

# center
strvar = "nepenthe"
# 默认填充空格
res = strvar.center(10)
# 指定填充 *
res = strvar.center(10,"*")
print(res)

# strip
strvar = " 	nepenthe    "
print(strvar)
# 默认去除两边空格
res = strvar.strip()
print(res)
# 去除指定字符*
strvar = "***nepenthe*****"
res = strvar.strip("*")
# 仅去除左边
res1 = strvar.lstrip("*")
# 仅去除右边
res2 = strvar.rstrip("*")
print(res, res1, res2)

# replace
# 替换空格
strvar = " 	nepenthe    "
print(strvar.replace(' ',''))

七、列表

1、列表的基本操作

(1)列表拼接(使用 +) 注:都为元组才执行可操作

ret1 = [1,2]
ret2 = [3,4]
ret = ret1 + ret2
print(ret)

(2)列表重复(使用 *) 注:都为元组才可执行操作

ret3 = [5, 6]
print(ret3*3)

(3)列表切片

listvar = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']

# 只有开始索引,结束索引默认到最后的下一位(结束索引-1)
ret1 =listvar[3:]
print(ret1)

# 只有结束索引,开始索引默认为0
ret2 = listvar[:6]
print(ret2)

# 从开始索引到结束索引(取左不取右)
ret3 = listvar[2:6]
print(ret3)

# 开始索引,结束索引,间隔值;以间隔值获取从开始索引到结束索引范围中的值
# 注:间隔值为负是降序递减截取,反向截取,默认是升序递增正向截取
ret4 = listvar[2:6:3]
print(ret4)

# 特殊情况:[:]或[::]会获取列表所有元素
print(listvar[:])
print(listvar[::])

(4)列表获取:形如listvar[0]

(5)列表修改:形如listvar[0] = 'asd'

(6)列表删除:

# 形如del lst[0]删除某一元素,形如del lst[0:2]删除某一段
# 注意点: res是变量 del 删除的是res 不是列表(改变了内存地址)
ret = ["猪八戒","唐僧","孙悟空","沙僧","白龙马"]
res = ret[1:4]
del res
print(lst)

2、列表的相关函数

append、insert、extend、pop、remove、clear、index、count、sort、reverse、del

(1)增:有三个方法

  • append 向列表末尾添加新的元素
  • insert 向指定索引前添加新的元素,必须设置参数,否则会报错;当插入的列表为空时,参数为0也可执行插入操作
  • extend 迭代追加所有元素(迭代追加字典类型只会获取到键添加进去)

(2)删:有4个方法

  • pop 通过索引删除指定元素(无参数默认删除最后一个,可获取已删元素内容);注:删除元素后列表的索引会发生变化
  • remove 通过赋予的值来删除,如果有多个,默认删除第一个(不可获取已删元素内容);不可同时删除多个,删除的元素不在列表中时程序报错,无默认删除使用remove必须设置参数
  • clear 清空列表,清除所有元素
  • del 可一次性删除多个

(3)改、查和排序:有4个方法

  • index 获取列表中值的索引
  • count 获取列表中某个元素的数量
  • sort 列表排序,对数字排序,默认从小到大,可使用参数 reverse = True;对字母排序,根据ASCII码表编码进行比较;可以对中文进行排序,但是无规律可循。注:当出现数字字符串,字母字符串,文字字符串混合时会出现特殊情况:数字字符串永远在 > 字母字符串 > 文字字符串
  • reverse 列表反转,就是反转,从后往前调换
# append
listvar = ['asd']
listvar.append('zsd')
listvar.append(['pas'])
listvar.append(('ple'))
listvar.append({'plp'})
listvar.append({'a': 'b'})
listvar.append(1)
print(listvar)

# insert
listvar = ['asd']
listvar.insert(1, 'pdd')
listvar.insert(1, ['pas'])
listvar.insert(1, ('ple'))
listvar.insert(1, {'plp'})
listvar.insert(1, {'a': 'b'})
listvar.insert(1, 1)
print(listvar)

# extend
listvar = ['asd']
tup1 = ['a', 'b', 'c']
tup2 = ('a', 'b', 'c')
tup3 = {'a', 'b', 'c'}
tup4 = {'a': 'b', 'd': 'd'}
tup5 = 'asd'
listvar.extend(tup5)
print(listvar)

# pop
lst = ['阿凡达', '阿迪达斯', '史莱姆']
res = lst.pop()
ret = lst.pop(1)
print(res, ret, lst)

# remove
lst = ['阿凡达', '阿迪达斯', '史莱姆']
lst.remove('阿凡达')
lst.remove('阿迪达斯')
print(lst)

# clear
lst = ['阿凡达', '阿迪达斯', '史莱姆']
lst.clear()
print(lst)

# index
lst = ['阿凡达', '阿迪达斯', '史莱姆']
ret1 = lst.index('阿迪达斯')
ret2 = lst.index('阿凡达')
print(ret1, ret2)

# count
lst = ['阿凡达', '阿迪达斯', '史莱姆', '阿迪达斯', '阿迪达斯',]
ret = lst.count('阿迪达斯')
print(ret)

# sort:纯数字
lst = [100, 20, -1, -30, 0, 10]
lst.sort()
lst.sort(reverse=True)
print(lst)

# sort:纯字母
lst = ['part', 'nepenthe', 'forget', 'me', 'not']
lst.sort()
print(lst)

# sort:数字字符串,字母字符串,文字字符串混合使用时:数字字符串>字母字符串>文字字符串
lst = ['王闻', '周杰伦', '罗志祥', 'me', 'not', 'afas', '12', '2', '30']
lst.sort()
print(lst)

# reverse就是反转
lst = [100, 20, -1, -30, 0, 10]
lst.reverse()
print(lst)

3、深浅拷贝

浅拷贝和深拷贝

# 未使用深浅拷贝的状态
lst1 = [1, 2, 3]
lst2 = lst1
lst1.append(4)
print(lst2)

浅拷贝:

​ 方法一:copy方法只针对列表

# copy()
lst = [1, 2, 3]
lst2 = lst.copy()
lst.append(5)
print(lst)
print(lst2)

​ 方法二:引入模块,copy.copy(任意类型)

​ 浅拷贝只拷贝一级所有的元素

# copy.copy()
import copy
lst = [1, 2, 3]
lst2 = copy.copy(lst)
lst.append(5)
print(lst)
print(lst2)

深拷贝:

​ 引入copy模块,使用copy.deepcopy()可将所有层级的元素都拷贝一份,不会被拷贝之前的数据影响,单独开辟的空间;copy和deepcopy,copy更快一些,也就是浅拷贝效率高;深拷贝也可以应用在字典中

​ (地址:原来不可变只是暂时的指向,可变的数据独立的开辟空间)

import copy
lst = [1, 2, 3, [4, 5, 6]]
lst2 = copy.deepcopy(lst)
lst[-1].append(7)
print(lst)
print(lst2)

4、元组

特点:可查询,不可修改,有序

元组只有两个函数:count和 index,和列表中的使用方式相同

del可直接删除元组释放空间。

八、字典

1、字典相关函数

fromkeys、append、pop、popitem、clear、del、update、get、key、values、items

(1)增:三种方式(实质上是两种,append是列表的方法)

  • 直接定义赋值:直接使用类似 dic['dic']='dic' 的方式

  • fromkeys:使用一组键和默认值创建字典,注:添加值的时候会自动给所有键对应的值都添加上

  • append:要先创建一个未赋值的键值对并且值是一个列表,才可使用append进行赋值操作,否则将会报错

(2)删:有三个方法

  • pop:通过键删除键值对(可设置默认值,避免删除的键值对不存在时报错),可以字符串的形式获取到删除的键值对
  • popitem:删除最后一个键值对,可以元组的形式获取到删除的键值对;空字典会报错
  • clear:清空字典,只剩一组括号
  • del:del 字典 彻底删除,释放掉字典,del 字典[键] 删除某个键值对(不是字典内置的方法)

(3)改

  • update:可批量更新(当出现不存在的键值对时会默认添加进去)

    有三种书写方式:直接在update中写入字典;先定义字典一个字典,再在update中传入定义的字典;通过键=值 的方式直接写入update中

(4)查:四个方法

  • get:通过键获取值,当没有获取到的键会返回None,也可设置默认值(未获取到键就返回设置的默认值)
  • key:将字典的键组成新的可迭代对象,注:组成的新的可迭代对象的数据类型不是list列表,而是dict_keys数据类型
  • values:将字典的值组成新的可迭代对象,注:组成的新的可迭代对象的数据类型不是list列表,而是dict_values数据类型
  • items:将字典的键值对凑成一个个元组,组成新的可迭代对象,注:组成的新的可迭代对象的数据类型不是tuple元组,而是dict_values数据类型
# 直接定义赋值
dic = {}
dic['alex'] = 'old boy'
dic['xboyww'] = 'young boy'
print(dic)

# fromkeys
lst = ['a', 'b', 'c']
dic = {}.fromkeys(lst, [1, 2, 3])
dic = {}.fromkeys(lst, None)
print(dic)

lst = ['a', 'b', 'c']
dic = {}.fromkeys(lst, [1, 2, 3])
dic['a'].append(4)
print(dic)

# append 只针对值是列表的类型
dic = {}
dic["a"] = []
dic["b"] = []
dic["a"].append(1)
dic['b'].append(2)
print(dic)

# pop
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
ret = dic.pop('top')
rets = dic.pop('down', '无此键值对')
print(type(ret), dic, type(rets))

# popitem
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
res = dic.popitem()
print(type(res), dic)

# clear
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
dic.clear()
print(dic, type(dic))

# update 第一种写法
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
dic.update({'top': 'all right'})
dic.update({'down': 'all right', 'downs': 'all right'})
dics = {'down': 'all right', 'downs': 'all right'}
dic.update(dic)
print(dic)

# update 第二种写法
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
dic.update(top='all right', down='all right')
print(dic)

# get
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
ret = dic.get('top')
res = dic.get('tops')
rets = dic.get('tops', '此键不存在')
print(ret, res, rets)

# keys
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
ret = dic.keys()
print(ret, type(ret))

# values
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
res = dic.values()
print(res, type(res))
dic = {'alex': 'good', 'ww': 'very good', 'top': 'all fake'}
ret = dic.items()
print(ret, type(ret))

# 变量的解包
for k, v in ret:
    print(k, v)

九、集合

1、集合的相关操作(交叉并补)

交集、差集、并集、对称差集、补集(包含关系才有补集)、

(1)交集 intersection()简写 &

(2)差集 difference()简写 -

(3)并集 union()简写 |

(4)对称差集 symmetric_difference()简写 ^ (包含补集的情况)

(5)子集 issubset()简写 <

(6)父集 issuperset()简写 >

(7)isdisjoint()检测两个集合是否不相交 不相交返回True,相交False

# 交集
lset1 = {'a', 'b', 'c', 'all fake'}
lset2 = {'good', 'very good', 'all fake'}
ret1 = lset1 & lset2
ret2 = lset1.intersection(lset2)
print(ret1)
print(ret2)

# 差集
lset1 = {'a', 'b', 'c', 'all fake'}
lset2 = {'good', 'very good', 'all fake'}
ret1 = lset1 - lset2
ret2 = lset1.difference(lset2)
print(ret1)
print(ret2)

# 并集
lset1 = {'a', 'b', 'c', 'all fake'}
lset2 = {'good', 'very good', 'all fake'}
ret1 = lset1 | lset2
ret2 = lset1.union(lset2)
print(ret1)
print(ret2)

# 对称差集
lset1 = {'a', 'b', 'c', 'all fake'}
lset2 = {'good', 'very good', 'all fake'}
ret1 = lset1 ^ lset2
ret2 = lset1.symmetric_difference(lset2)
print(ret1)
print(ret2)

# 子集
lset1 = {'a', 'b', 'c', 'all fake'}
lset2 = {'good', 'very good', 'all fake'}
ret1 = lset1 < lset2
ret2 = lset1.issubset(lset2)
print(ret1)
print(ret2)

# 父集
lset1 = {'a', 'b', 'c', 'all fake'}
lset2 = {'good', 'very good', 'all fake'}
ret1 = lset1 > lset2
ret2 = lset1.issuperset(lset2)
print(ret1)
print(ret2)

#检测两个集合是否不相交
lset1 = {'a', 'b', 'c', 'all fake'}
lset2 = {'good', 'very good', 'all fake'}
ret2 = lset1.isdisjoint(lset2)
print(ret1)
print(ret2)

2、集合相关的函数

add、update(迭代增加)、clear、pop(集合中随机删除)、remove()、discard()

(1)增:有两个方法

  • add:单个添加
  • update:迭代增加,多个添加 ;注:update中是字符串的时候,字符串会被拆分成单个字符

(2)删:有四个方法

  • clear:清空集合中的所有数据

  • pop:随机删除集合中的一个数据

  • remove:删除集合中的指定的值(不存在会报错)

  • discard:删除集合中的指定的值(不存在不会报错,不会删除,推荐使用)

    # add 
    setvar = {'zxc'}
    setvar.add("asd")
    print(setvar)
    
    # update
    setvar = {'zxc'}
    setvar.update("asd", 'pdd', 'lq')
    lst = ['azx', 'qwe']
    setvar.update(lst)
    tup = ['azx', 'qwe']
    setvar.update(tup)
    strs = 'apd'
    setvar.update(strs)
    print(setvar)
    
    # clear
    setvar = {"asd", 'pdd', 'lq'}
    setvar.clear()
    print(setvar)
    
    # pop
    setvar = {"asd", 'pdd', 'lq'}
    setvar.pop()
    print(setvar)
    
    # discard
    setvar = {"asd", 'pdd', 'lq'}
    setvar.discard('pdd')
    setvar.discard('pdds')
    print(setvar)
    
    # remove
    setvar = {"asd", 'pdd', 'lq'}
    setvar.remove('pdd')
    # setvar.remove('pdds')
    print(setvar)
    

冰冻集合:frozenset 可强转容器类型数据变成冰冻集合
特征:冰冻集合一旦创建,不能再做任何的添加或者删除操作,只能做交叉并补,可以遍历

fz = frozenset()
print(fz, type(fz))

lst = ["a", "b", "c"]
fz = frozenset(lst)
print(fz, type(fz))
for i in fz:
    print(i)

fz1 = frozenset(["a","b","c"])
fz2 = frozenset(["a","b","c","d"])
res = fz1 & fz2
res = fz2 - fz1
print(res)

十、文件操作

1、文件的基本操作

普通语法:打开文件 open('文件名', '模式', '字符编码集');关闭文件 fp.close()

with语法:可以省略close操作 with open('文件名', '模式', '字符编码集') as fp:

(1)文件的写入操作:mode = 'w'(write)模式;write()方法写入内容

(2)文件的读取操作:mode = 'r'(read)模式;read()方法读取内容

(3)字节流的转换:

​ encode()编码,将字符串转化为二进制字节流

​ decode()解码,将二进制字节流转化为字符串

(4)字节流的存储:字节流模式下,无需指定编码集

​ 写入操作:mode = 'wb' ;读取操作:mode = 'rb'

# 文件的写入操作
fp = open("ceshi.txt", mode='w', encoding="utf-8")
fp.write("把大象塞进去")
fp.close()

# 文件的读取操作
fp = open("ceshi.txt", mode='r', encoding="utf-8")
ret = fp.read()
print(ret)
fp.close()

# 字节流的转换
strvar = '我爱你'
res = strvar.encode('utf-8')  # 编码
print(res)
ret = b'\xe6\x88\x91\xe7\x88\xb1\xe4\xbd\xa0'.decode('utf-8')  # 解码
print(ret)

# 字节流写入操作
fp = open("ceshi2.txt", mode="wb")
str_bytes = "Trouble is a friend".encode("utf-8")
fp.write(str_bytes)
fp.close()

# 字节流读取操作
fp = open("ceshi2.txt",mode="rb")
res = fp.read()
fp.close()

# 图片 音频 视频这样的文件都是二进制bytes,图片的读写操作如下
fp = open('GD5.jpg', mode='rb')
GD_bytes = fp.read()
fp.close()
print(GD_bytes)

fp = open('GD6.jpg', mode='wb')
fp.write(GD_bytes)
fp.close()

2、文件的扩展模式(包含文件的读写追加操作)

utf-8编码中,默认一个中文字符占三个字节,英文或符号占用一个字节

1)r、w、a、rb、wb、ab、r+、w+、a+、rb+、wb+、ab+

(1)r读(文本文件):read读,文件不存在就会报错,文件存在就从头开始读。并且将文件指针放在文件的开头开始读取数据

(2)w写(文本文件):write写,文件不存在就默认创建文件,存在就默认从文件的开头开始写入内容,并且将文件指针放在文件的开头开始写入数据

(3)a追加(文本文件):append追加,文件不存在就默认创建文件,指针默认在文件的末尾

(4)rb读(二进制文件)、wb写(二进制文件)、ab追加(二进制文件):针对二进制文件进行操作,使用方法同上

(5)r+读写(文本文件):读取操作的时候是从当前指针位置向后读取,所以应注意指针位置要旨在文件的头部才会读取整个文件的信息

​ 有两种模式

​ 先读后写:写操作后指针在文件末尾,指针归0(seek(0))后才能读取到数据

​ 先写后读:要事先把指针移到末尾(seek(0,2)),否则在写的时候会把已存在的数据(字符)一对一替换 成后写入的数据,在完成写操作后还要对指针进行归位(seek(0)),才会读取到数据

(6)w+写读(文本文件):写读模式,每次打开都要清空重写

(7)a+追加(文本文件):追加读写模式,再写入内容是,会强制把指针放在文件末尾

(8)rb+读(二进制文件)、wb+写(二进制文件)、ab+追加(二进制文件):针对二进制文件进行操作,使用方法同上

(9)扩展模式 (配合打开模式的辅助模式,自己单独不能使用)

​ + plus 增强模式(可以让文件具有读写功能)
​ b bytes bytes模式(二进制字节流)

# r+ 先读后写
fp = open("ceshi.txt", mode='r+', encoding="utf-8")
ret = fp.read()
fp.write("789")
fp.seek(0)
ret = fp.read()
fp.close()
print(ret)

# r+ 先写后读
fp = open("ceshi.txt", mode='r+', encoding="utf-8")
fp.seek(0, 2)
fp.write("Excuse me")
fp.seek(0)
ret = fp.read()
fp.close()
print(ret)

# w+ 写读操作
fp = open("ceshi3.txt",mode="w+",encoding="utf-8")
fp.write("The life is interesting")
fp.seek(0)
res = fp.read()
print(res)
fp.close()

# a+ 追加读写操作
fp = open("ceshi3.txt",mode="a+",encoding="utf-8")
fp.write("Trouble is a frend")
fp.seek(0)
res = fp.read()
print(res)
fp.close()

2)read、seek、tell函数

(1)read:读取字符个数,通过字符

(2)seek:调整指针位置,位置是通过字节个数来设置调整的参数的,seek(0)代表指针指在文件的头部;seek(0,2)代表指针指在文件的尾部,(字节参数慎用,容易出现乱码问题)

(3)tell:获取当前光标左侧所有的字节数。注:对中文执行此操作可能会出现乱码

fp = open("ceshi2.txt",mode="r+",encoding="utf-8")
res = fp.read(6)
fp.seek(2)
res = fp.read(2)
total = fp.tell()
print(total)
print(res)

3、文件相关的函数

缓冲区(flush)的刷新:

​ 当文件关闭的时候会自动刷新缓冲区;

​ 当整个程序运行结束的时候自动刷新缓冲区

​ 当缓冲区写满时会自动刷新缓冲区

​ 手动刷新缓冲区

close、readable、writable、readline、readlines、writelines、truncate

(1)close:关闭文件,刷新缓冲区

(2)readable:查看文件是否有可读取的权限

(3)writable:查看文件是否有可写入的权限

(4)readline:只取一行内容,在下面再使用此方法就取下一行内容,可设置参数,参数是以字符为标准的;如果字符参数个数大于当前行总个数,就按照当前行读取,否则如果字符个数 < 当前行总个数 ,按照实际个数读取

(5)readlines:将文件中的内容按照换行读取到列表当中

(6)writelines:将内容是字符串的可迭代性数据写入文件中 参数:内容为字符串类型的可迭代数据

(7)truncate: 把要截取的字符串提取出来,然后清空内容将提取的字符串重新写入文件中 (字节)

(8)文件对象也是可迭代性数据,在遍历的时候默认按照一行一行进行读取

# readable readable close
fp = open("ceshi6.txt", mode="r+", encoding="utf-8")
res1 = fp.readable()
res2 = fp.writable()
print(res1, res2)
fp.close()

# readline
with open("ceshi4.txt" , mode="r+" ,encoding="utf-8") as fp:
	res = fp.readline(2)
	res = fp.readline(20)
	res = fp.readline(200)
	print(res)
# 通过循环读取文件中的所有内容
with open("ceshi4.txt" , mode="r+" ,encoding="utf-8") as fp:
	res = fp.readline()
	while res:
		print(res)
		res = fp.readline()

# readlines
lst_new = []
with open("ceshi6.txt" , mode="r+" ,encoding="utf-8") as fp:
	lst = fp.readlines()
	for i in lst:
		res = i.strip()
		lst_new.append(res)
print(lst_new)

# writelines
lst = ["床前明月光\n", "疑是地上霜\n", "举头望明月\n", "低头思故乡\n"]
with open("ceshi2.txt", mode="w+", encoding="utf-8") as fp:
	lst.insert(1, "阿斯顿发射点发生\n")
	fp.writelines(lst)

# truncate
with open("ceshi2.txt", mode="r+", encoding="utf-8") as fp:
	fp.truncate(6)

十一、函数

功能:包裹一部分代码,实现某一个功能,达成某一个目的

特点:可以反复调用,提高代码复用性,提升开发效率,便于代码维护

1、函数的定义:

​ def 函数名():

​ 代码块

​ 函数的调用:

​ 函数名()

# 函数的定义处
def cfb_99():
	for i in range(1, 10):
		for j in range(1, i+1):
			print("%d*%d=%2d " % (i, j, i*j), end="")
		print()
# 函数的调用
for i in range(10):
	cfb_99()

2、函数的名字

          函数的命名
字母数字下划线,首字符不能为数字
严格区分大小写,且不能使用关键字
函数命名有意义,且不能使用中文哦

驼峰命名法:	
	(1) 大驼峰命名法: 每个单词的首字符都大写 mycar => MyCar (面向对象当中,定义类class)
	(2) 小驼峰命名法: 除了第一个单词小写之外,剩下每个单词首字符大写 mycar => myCar (函数,变量)
命名一个函数 通常用_拼接的形式,组装不同的单词
	mycar => my_car
	symmetric_difference
																		----闻哥专属

3、函数的参数

1)参数的种类:
(1) 形参: 形式上的参数 , 在函数的定义处
(2) 实参: 实际上的参数 , 在函数的调用处

2)形参的种类:
普通(位置)形参,默认形参,普通收集参数,命名关键字参数,关键字收集参数
注:调用时,如果不给实际参数,默认使用自带的值进行调用
调用时,如果给实际参数,那么使用实参来进行调用
默认参数必须跟在普通参数的后面
调用函数时,关键字实参顺序可以任意调整的
关键字实参必须跟在普通实参的身后

​ 收集参数:

​ 普通收集参数:通过*加参数代表普通收集参数,收集多余的没人要的普通实参构成元组的形式。

​ def func(*args):
​ pass
​ args => arguments(参数)

​ 关键字收集参数:通过**加参数代表关键字收集参数,收集多余的关键字实参构成字典的形式

​ def func(**kwargs):
​ pass
​ kwargs = keyword arguments

​ 命名关键字参数:使用方式 def 函数名(参数1,参数2,*, 参数3,...)

​ 关键字参数的两种存在形式:

​ (1)在*号后面定义的形如普通参数的参数是关键字参数

​ (2)在普通收集和关键字收集参数之间的,是命名关键字参数

​ 注: 命名关键字参数必须使用关键字实参的形式调用;

3)实参的种类:
普通实参,关键字实参

4)遵循的原则:
调用参数的时,形参和实参必须一一对应,否则报错

5)区分默认形参和关键字实参

​ 默认形参:

​ 关键字形参:

6)关于 * 和 ** 的使用方法

​ 在函数的定义处,*和**用来接受数据,*将接受到的数据打包成一个元组,**将接受到的数据打包成一个字典

​ 在函数的调用处,*和**用来解包数据,*一般应用在列表或元组,**一般用在字典

​ *和**的优点:控制了参数的个数

# 普通形参
def star(hang,lie):
	i = 0
	while i<hang:
		j = 0
		while j<lie:
			print("*",end="")
			j+=1
		print()
		i+=1
# 普通实参
star(4,9)

# 默认形参
def star(hang=10,lie=10):
	i = 0
	while i<hang:
		j = 0
		while j<lie:
			print("*",end="")
			j+=1
		print()
		i+=1
star()

# 普通形参 + 默认形参
""""""
def star(hang,lie=10):
	i = 0
	while i<hang:
		j = 0
		while j<lie:
			print("*",end="")
			j+=1
		print()
		i+=1

star(20)

# 关键字实参
def star(hang,lie=10):
	i = 0
	while i<hang:
		j = 0
		while j<lie:
			print("*",end="")
			j+=1
		print()
		i+=1

# 关键字实参
star(hang=3,lie=8)
star(lie=8,hang=3)


# 普通实参 + 关键字实参
def star(hang,a,b,c,lie=10):
	i = 0
	while i<hang:
		j = 0
		while j<lie:
			print("*",end="")
			j+=1
		print()
		i+=1
star(3,3,4,5,lie=4)

# 普通收集参数
def mysum(*args):
    total = 0
    for i in args:
        total += i
    print(total)
mysum(1, 2, 3, 4)

# 关键字收集参数
def func(**kwargs):
    strvar = ""
    strvar2 = ""
    dic = {'a': 'b', 'c': 'd', 'e': 'f'}
    for k, v in kwargs.items():
        if k in dic:
            strvar += k + ":" + v + "\n"
        else:
            strvar2 += v + ","
    print(strvar.strip())
    print("吃瓜群众:" + strvar2.strip(",") + "......")
func(a = "b", c = "d")

# 命名关键字参数
# 定义方式一
def func(a, b, *, c):
    print(a, b)
    print(c)
func(1, 2, c=3)

# 定义方式二
def func(*args, dd, **kwargs):
    print(args)
    print(kwargs)
    print(dd)
func(1, 2, 3, dd=5, a=1, b=2)

# *和**
def func(a, b, *, c, d):
    print(a, b)
    print(c, d)
# *打包和**打包类似
func(1, 2, c=3, d=4)
lst = [1, 2]
# *解包
func(*lst, c=3, d=4)
# **解包
dic = {"c": 3, "d": 4}
func(1, 2, **dic)

4、return自定义返回值

​ 函数可以自定义返回值,通过return把值返回导函数的调用处

​ (1)return + 返回值:可以接六大标准数据类型、返回函数和类对象,未定义返回值,默认返回None

​ (2)函数中若执行了return,就会终止函数,return后面的代码块将不会再被执行

​ 注:未定义返回值,默认返回None,函数执行到return立刻终止函数

def func():
    return 100
ret=func()
print(ret) 

5、全局变量和局部变量

(1)局部变量:在函数内部定义的变量就是局部变量(局部命名空间)

​ nonlocal:用来修改局部变量

(2)全局变量:在函数外面定义的变量 或者 在函数内部用global关键字定义是全局变量 (全局命名空间)

​ global:函数内部定义全局变量使用global,可声明全局变量

(3)作用域:

​ 局部变量作用域:限定在本层函数的内部

​ 全局变量作用域:横跨整个文件

(4)生命周期:

​ 内置命名空间>全局命名空间>局部命名空间

​ 内置变量>全局变量>局部变量

i = 0  # i是一个全局变量
def func():
	a = 1  # a是一个局部变量
	print(a)
	a = 20
	print(a)
	# global i  # 使用global声明后才可修改全局变量i
	i = 2  # 不会修改全局变量的值
print(a)  # 不能获取局部变量的值
print(i)

6、函数名的使用

python中的函数可以像变量一样,动态创建,销毁,当参数传递,作为返回值,叫做第一类对象,其他语言不具备

1)函数名的四种使用方法

(1)函数名当作变量赋值

def func1():
	print("我是func1")
	return 111
res = func1()
print(res)

(2)函数名可以作为容器类型数据的元素

def func2():
	print("我是func2")
def func3():
	print("我是func3")
def func4():
	print("我是func4")	
	return "func4"
lst = [func2,func3,func4]
for i in lst:
	i()

(3)函数名可以作为函数的参数

def func4():
	print("我是func4")	
	return "func4"
def myfunc(f):
	res = f()
	print(res)
myfunc(func4)

(4)函数名可以作为函数的返回值

def myfunc2(f):
	return f
f2 = myfunc2(func4)
print(f2)
f2()

__doc__或者help查看文档(获取注释的内容,查看帮助文档)

def eat_big_chang(something):
	'''
	说明注释
	'''
	print("step1=>先把{}洗一洗".format(something))
res = eat_big_chang("a")
print(res)
print(eat_big_chang.__doc__)
print(help(eat_big_chang))

2)locals和globals 、nonlocal和global

locals 获取当前作用域中的所有内容,有两种情况(返回系统的字典)

​ 在函数外 ,获取打印之前的所有变量,返回字典,全局空间

​ 在函数内 ,获取调用之前的所有变量,返回字典,局部空间作用域

# 例子 1
a = 1
b = 2
res = locals()
c=3
print(res)
d = 4

# 例子2
a=1
def func():
    b = 2
    res = locals()
    c=3
    print(res)
    d = 4
func()

globals 获取全局作用域的所有内容(返回系统的字典)

​ 在函数外,获取打印之前的所有变量,全局空间作用域

​ 在函数内,获取调用之前的所有变量,全局空间作用域

# 例子3
a = 5
b = 6
res = globals()
c = 7
print(res)

# 例子4
a =10
def func():
	b = 11
	c = 12
	res = globals()
	d= 13
  	print(res)
ff = 30
func()
zz = 50

注:在函数调用之前的才会获取

nonlocal:nonlocal 专门用来修改当前作用域上一级的局部变量,如果找不到,那么继续向上寻找,全都找不到直接报错。注:只能修改局部变量,不能修改全局变量,遵循LEGB原则

不使用nonlocal修改局部变量-------》》把数据放到列表中可以进行修改

# 1
def outer():
	a = 10
	def inner():
		nonlocal a
		a = 20	
	inner()
	print(a)
outer()
# 2
def outer():
	lst = [1,2,42]
	def inner():
		lst[-1] = 52
	inner()
	print(lst)
outer()

global:修改全局变量

globals创建变量:通过系统的全局字典添加键值对,可以动态创建全局变量

变量的两种定义方式:

# 正常方式定义变量
a = "123"
# 通过系统的全局字典添加键值对,可以动态创建全局变量
dic = globals()
print(dic)
k = "wang"
dic[k] = "asf"
print(wang)
# 3.批量创建全局变量,在函数中
def func():
	dic = globals()
	for i in range(1,6):
		dic["p%d" % (i)] = i

func()
print(p1)
print(p2)
print(p3)
print(p4)
print(p5)

7、函数的嵌套

嵌套在函数的外边叫外函数;嵌套在函数的里面叫内函数

(1)内部函数不可以直接在函数外部调用

(2)调用外部函数后,内部函数也不能在函数外部调用

(3)内部函数可以在函数内部调用

(4)内部函数在函数内部调用时,有先后顺序(注:要先定义函数再调用)

LEGB原则(即就近找变量的原则)寻找变量遵循的原则是从里到外,从下向上就进找

B -- Builtin(Python):Python内置模块的命名空间 (内建作用域)
G -- Global(module):函数外部所在的命名空间 (全局作用域)
E -- Enclosing function locals:外部嵌套函数的作用域(嵌套作用域)
L -- Local(function):当前函数内的作用域(局部作用域)

def outer():
	def inner():
		def smaller():
			print(id)
			print("我是smaller函数")
		smaller()
	inner()
outer()

8、闭包函数

内部函数定义:

​ 内函数使用了外函数的局部变量

​ 外函数将内函数返回出来的过程,叫做闭包

​ 里面的内函数叫做闭包函数

判断闭包的两种方式:

(1)通过直接获取注释内容

(2)通过两个方法,获取单元格对象里面的内容:cell_contents、__closure__

闭包函数的特点:内函数使用了外函数的局部变量,该局部变量与内函数发生绑定,延长该变量的生命周期

闭包的意义:

​ 闭包可以优先使用外函数的局部变量

​ 局部变量在函数外部不能被直接使用

​ 对局部变量实现了保护的作用,外部无法访问

# 闭包函数用法
def wz_family():
	father = "王健林"
	def wzj_hobby():
		print("先定一个小目标,比如赚他一个亿,这是爸爸{}说的".format(father))
	return wzj_hobby
# res = wzj_hobby <=> res()  = wzj_hobby()
res = wz_family()
print(res)
res()

# 获取闭包函数使用的变量  __closure__ , cell_contents
tup = res.__closure__
print(tup)

9、匿名函数

lambad 表达式:只有返回值的函数,特点:简洁,高效,方便

(1)无参的lambda表达式

​ def func():
​ return "我是个诗人"

​ func = lambda : "我是个诗人"
​ res = func()
​ print(res)

(2)有参的lambda表达式

​ def func(n):
​ return type(n)
​ func = lambda n : type(n)
​ res = func(18)
​ print(res)

(3)带有判断条件的lambda表达式

​ def func(n):
​ if n % 2 == 0:
​ return "偶数"
​ else:
​ return "奇数"

三目(元)运算符:

真值 if 条件表达式 else 假值

表达式成立就返回真值,表达式不成立就返回假值

# 三目运算符
n = 20
res = "偶数" if n % 2 == 0 else "奇数"
print(res)

# 改写lambda 
func = lambda n : "偶数" if n % 2 == 0 else "奇数"
res = func(21)
print(res)

10、高阶函数

map、filter、reduct、sorted

(1)迭代器:

​ 概念:能被next()函数调用,并不断返回下一个值的对象,叫做迭代器(迭代器是对象)

​ 特征:不依赖索引,通过next指针迭代所有数据,一次只取一个,可以大大节省空间,迭代无限量的数据

  • 可迭代对象:

​ 如果成员中含有__iter__这个方法,就是可迭代对象

​ dir函数可以查看一个对象中所有的成员

(2)迭代器:

  • 迭代器的定义:

    ​ 通过iter()方法定义迭代器

    ​ 通过可迭代对象.__iter__()定义迭代器

  • 判断迭代器:如果内置成员中含有__iter__和__next__两个方法,就可判断是一个迭代器

  • 调用迭代器

    ​ 方式一:next(迭代器)

    ​ 方式二:迭代器.__next__()

    ​ 注:迭代器通过next方法调用时,是单向不可逆的过程

  • 重置迭代器

​ 重新定义:使用next调用,单项不可逆,所以需要使用iter()重新定义

  • 使用Iterator和Iterable来判断是否是迭代器

    from collections import Iterator,Iterable 使用from ... import ... 引入

    Iterator迭代器类型 Iterable 可迭代对象

  • 注:迭代器一定是可迭代对象,但可迭代对象不一定是迭代器(容器数据类型不是迭代器,是可迭代对象)

    # 判断可迭代对象
    setvar = {"a", 1, "b", 2}
    for i in setvar:
        print(i)
    res = dir(setvar)
    print(res)
    
    # 定义迭代器
    setvar = {"a", 1, "b", 2}
    it = iter(setvar)
    print(it)
    
    # 判断迭代器
    print(dir(it))
    res = "__iter__" in dir(it) and "__next__" in dir(it)
    print(res)
    
    # 调用迭代器
    res = next(it)
    print(res)
    
    # 重置迭代器
    it = iter(setvar)
    res = next(it)
    ret = iter(setvar)
    res = next(it)
    print(res)
    
    # 导入模块判断迭代器和可迭代对象
    from collections import Iterator,Iterable
    setvar = {"a", "b", "c"}
    res = isinstance(setvar, Iterator)
    print(res)
    ret = isinstance(setvar, Iterable)
    print(ret)
    
    it = iter(range(5))
    res = isinstance(it, Iterator)
    print(res)
    ret = isinstance(it, Iterable)
    print(ret)
    
    # 通过next获取迭代器中的数据
    res = next(it)
    print(res)
    
    # for循环遍历迭代器
    it = iter(range(6))
    for i in it:
        print(i)
    
    # for和next配合调用迭代器
    it = iter(range(100000))
    for i in range(10):
        res = next(it)
        print(res)
    
    for i in range(10):
        res = next(it)
        print(res)
    

(3)高阶函数:能够把函数当成参数传递的就是高阶函数 map filter reduce sorted

  • map(func,iterable)

    功能:把iterable里面的数据一个一个拿出来,放到func函数中进行处理,把处理的结果扔到迭代器中,返回迭代器。

    func:自定义函数或内置函数

    iterable:可迭代对象(容器类型数据,range(),迭代器)

    返回值:迭代器

    lst = ['1', '2', '3']
    lst_new = []
    for i in lst:
        res = int(i)
        lst_new.append(res)
    print(lst_new)
    
    # map的使用
    from collections import Iterator, Iterable
    it = map(int, lst)
    res = isinstance(it, Iterator)
    print(res)
    res = next(it)
    print(res)
    
    it = map(int, lst)
    for i in it:
        print(i)
    
    it = map(int, lst)
    for i in range(2):
        print(next(it))
    
    it = map(int, lst)
    lst = list(it)
    print(lst)
    
    
  • filter(func,iterable)

    功能:在定义的函数中,过滤数据;若返回True代表保留数据;若返回False代表舍弃该数据

    func:自定义函数

    iterable:可迭代性数据(容器类型数据,range(),迭代器)

    返回值:迭代器

    from collections import Iterator, Iterable
    lst = [1, 2, 3, 4, 5, 6, 7, 8]
    lst_new = []
    for i in lst:
        if i % 2 == 1:
            lst_new.append(i)
    print(lst_new)
    
    # filter
    def func(n):
        if n % 2 == 0:
            return False
        else:
            return True
    it = filter(func, lst)
    res = isinstance(it, Iterator)
    print(res)
    print(list(it))
    
  • reduct(func, iterable)

    功能:一次性从iterable当中取出两个值,扔到func函数中进行处理,把运算的结果再和iterable的第三个值继续扔到func中做运算,依次类推,最后返回计算的结果

    func:自定义函数

    iterable:可迭代性数据(容器类型数据,range(),迭代器)

    返回值:最后计算结果

    注:需要导入模块 from functools import reduce

    lst = [5, 4, 8, 8]
    strvar = ""
    for i in lst:
        strvar += str(i)
    print(strvar, type(strvar))
    intvar = int(strvar)
    print(intvar, type(intvar))
    
    it = iter(lst)
    num1 = next(it)
    num2 = next(it)
    print(num1)
    print(num2)
    total = num1 * 10 + num2
    print(total)
    for i in it:
        total = total * 10 + i
    print(total, type(total))
    
    from functools import reduce
    lst = [5, 4, 8, 8]
    def func(x, y):
        return x * 10 + y
    res = reduce(func, lst)
    print(res, type(res))
    
    print(reduce(lambda x, y: x * 10 + y, lst))
    
  • sorted(iterable,reverse=False, key = 函数)

    功能:排序

    iterable:可迭代性数据(容器类型数据 range对象 迭代器)

    reverse:代表是否倒叙 reverse=True 代表倒叙 从大到小 reverse = False 代表正序,从小到大

    key:自定义函数 或 内置函数

    返回值:排序后的列表(新列表)

    # sorted
    container = [100, 200, 13, -6, 0]
    container = (100, 200, 13, -6, 0)
    container = {100, 200, 13, -6, 0}
    container = "oneal"
    container = {"a": 1, "b": 2, "c": 3}
    
    lst_new = sorted(container)
    print(lst_new)
    
    # 自定义函数排序
    let = [19, 27, 35, 48]
    def func(n):
        return n % 10
    res = sorted(lst, key=func)
    res = sorted(lst, key=lambda n: n % 10)
    print(res)
    

十二、推导式

1、推导式(列表推导式)

用一行循环判断遍历出一系列数据的方式

注:推导式在使用时只能使用for循环和if判断,if判断只能是单项判断

推导式得三种形式:列表推导式、集合推导式、字典推导式

(1)普通推导式:通过 [] 和 for 形式构成

(2)带有判断的推导式:通过 [] for if 形式构成

(3)多循环推导式:通过 [] for for ... 形式构成

(4)带有判断条件的循环推导式:通过 [] for for if 的形式构成

列表推导式,获取的数据在列表中

lst = [1, 2, 3, 4, 5, 6, 7, 8, 9]
lst_new = []
for i in range(1, 51):
    lst_new.append(i)
print(lst_new)
# 基本语法
lst = [i for i in range(1, 51)]
print(lst)

# 普通推导式
lst = [1, 2, 3, 4]
lst = [i << i for i in lst]
print(lst)

lst = [1, 2, 3, 4, 5, 6, 67, 7, 8]
lst_new = []
for i in lst:
    if i % 2 == 0:
        lst_new.append(i)
print(lst_new)
# 带有判断的推导式
lst = [i for i in lst if i % 2 == 0]
print(lst)

list1 = ['a', 'b', 'c']
list2 = ['x', 'y', 'z']
lst_new = []
for i in list1:
    for j in list2:
        res = i + "♥" + j
        lst_new.append(res)
print(lst_new)
# 多循环推导式
lst = [i + "♥" + j for i in list1 for j in list2]
print(lst)

lst_new = []
for i in list1:
    for j in list2:
        if list1.index(i) == list2.index(j):
            res = i + "♥" + j
            lst_new.append(res)
print(lst_new)
# 带有判断条件的多循环推导式
lst = [i + "♥" + j for i in list1 for j in list2 if list1.index(i) == list2.index(j)]
print(lst)

2、集合推导式

集合推导式,获取的数据在集合中并自动去重

三目运算可应用在推导式中

listvar = [
    {"name": "jack", "age": 18, "money": 10000},
    {"name": "eric", "age": 19, "money": 5100},
    {"name": "pdd", "age": 20, "money": 4800},
    {"name": "asd", "age": 21, "money": 2000},
    {"name": "erick", "age": 180, "money": 20}
]

setvar = set()
for i in listvar:
    if 18 <= i["age"] <= 21 and 5000 <= i["money"] <= 5500:
        res = "尊贵VIP卡老" + i["name"][0]
    else:
        res = "黄金VIP卡老" + i["name"][0]
    setvar.add(res)
print(setvar)

setvar = {"尊贵VIP卡老" + i["name"][0] if 18 <= i["age"] <= 21 and 5000 <= i["money"] <= 5500 else "黄金VIP卡老" + i["name"][0] for i in listvar}
print(setvar)

3、字典推导式

字典推导式,获取的数据在字典

内置方法:

​ enumerate:枚举,将索引和iterable中的值,一个一个拿出来配对组成元组放入迭代器中

​ 参数:可迭代性数据;start:设置开始的索引,默认从0开始

​ 返回值:迭代器

​ zip:将多个iterable中的值,一个一个拿出来配对组成元组放入迭代器中

​ 参数:可迭代性数据;

​ 返回值:迭代器

​ 注:不能匹配多余值会被舍弃

注:三目运算可应用在推导式中

# enumerate
lst = ["a", 'b', 'c', 'd', 'e', 'f', 'g', 'h']
it = enumerate(lst)
it = enumerate(lst, start=5)
from collections import Iterator, Iterable
res = isinstance(it, Iterator)
print(res)

for i in range(3):
    res = next(it)
    print(res)

res = list(it)
print(res)

dic = {k: v for k, v in enumerate(lst, start=1)}
print(dic)

# zip
from collections import Iterator,Iterable
lst1 = ['a', 'b', 'c', 'd', 'e']
lst2 = ['1', '2', '3', '4']
lst3 = ['+', '-', '/']

it = zip(lst1, lst2)
print(isinstance(it, Iterator))
print(list(it))

dic = {k: v for k, v in zip(lst1, lst2)}
print(dic)

dic = dict(zip(lst1, lst2))
print(dic)

十三、生成器

生成器本质是迭代器,允许自定义逻辑的迭代器

迭代器和生成器的区别:迭代器本身是系统内置的,不能被重写;生成器是用户自定义的,可以重写迭代逻辑

生成器的两种创建方式:

(1)生成器表达式:里面用推导式,外面用圆括号

(2)生成器函数:用def定义,里面含有yield

1、生成器表达式

推导式 + 圆括号

from collections import Iterator
gen = (i * 2 for i in range(1, 5))
print(gen)
print(isinstance(gen, Iterator))

ret = next(gen)
print(ret)

gen = (i * 2 for i in range(1, 5))
for i in gen:
    print(i)
gen = (i * 2 for i in range(1, 5))
for i in range(2):
    ret = next(gen)
    print(ret)

res = list(gen)
print(res)

2、生成器函数

yield类似return

​ 共同点:执行到这句话都会把值返回出去

​ 不同点:yield每次返回时,会记住上次离开时执行的位置,下次在调用生成器,会从上次执行的位置往下继续执行;而return直接终止函数,每次从头调用

​ yield from : 将一个可迭代对象变成一个迭代器返回

写法:yield 6 和 yield(6) 推荐使用前者,避免记混

send方法:send 可以发送数据,发送给上一个yield

​ send与next区别:

​ next 只能取值

​ send 不但能取值,还能发送值

​ send需要注意:

​ 第一个send不能给yield传值默认只能写None

​ 最后一个yield接收不到send的发送值

初始化生成器函数 -> 生成器对象 -> 简称生成器

# 语法
from collections import Iterator
def mygen():
    print("one")
    yield 1
    print("two")
    yield 2
    print("three")
    yield 3
gen = mygen()
print(isinstance(gen, Iterator))
res = next(gen)
print(res)
res = next(gen)
print(res)
res = next(gen)
print(res)

# for + next
def mygen():
    for i in range(1, 101):
        yield "我的球衣号码是%s"%(1)
gen = mygen()
for i in range(5):
    ret = next(gen)
    print(ret)

for i in range(3):
    res = next(gen)
    print(res)

# send传值
def mygen():
    print("start")
    ret = yield 111
    print(ret)

    ret = yield 222
    print(ret)

    ret = yield 333
    print(ret)
    print("end")

gen = mygen()
val = gen.send(None)
print(val)
val = gen.send(444)
print(val)
val = gen.send(555)
print(val)

# yield from
def mygen():
    lst = ['a', 'b', 'c']
    yield from lst
gen = mygen()
print(next(gen))
print(next(gen))
print(next(gen))

十四、递归函数

1、普通递归:递去归来

递归的触发条件:

(1)当最后 一层函数全部执行结束的时候,有触底反弹的过程(回马枪),回到上层函数空间的调用处

(2)遇到return返回值,直接返回上层空间的调用处

函数在运行的时候,需要内存开辟空间,这个空间叫栈帧空间

递归:

  • 去的过程就是不停的开辟栈帧空间,在回的时候就是不停的释放栈帧空间,递归函数就是不停开辟释放内存空间的一个完整的过程

  • 回的时候,有两种触发的机制,要么是最后一层函数空间全部执行完毕,要么是遇到return,都会触底反弹(回马枪)

  • 写递归函数的时候,必须给与跳出的条件,如果递归的层数过多,不推荐使用,容易内存溢出或者蓝屏

  • 递归调用每一层都是独立的个体,独立的副本,资源不共享,可以通过return来完成值的传递

注:递归官方说法最大深度是1000层,具体要看机器配置

递归流程图,专属闻哥纯手绘:

经典一号

经典二号

# 示例
def digui(n):
	print(n,"<===1===>")
	if n > 0 :
		digui(n-1)
	print(n,"<===2===>")
digui(5)

# 实例:阶乘递归
def jiecheng(n):
    if n <= 1:
        return 1
    return n * jiecheng(n-1)
res = jiecheng(5)
print(res)

2、尾递归

自己调用自己,非表达式,把值放到参数中运算

特点:无论递归多少次都只会占用一份栈帧空间

好处:只需要考虑最后一层空间的结果是多少,就不用额外考虑回的过程了;

Cpython解释器目前不支持尾递归

# 实例:阶乘尾递归
def jiecheng(n, endval):
    if n <= 1:
        return endval
    return jiecheng(n-1, endval*n)
res = jiecheng(5, 1)
print(res)

def jiecheng(n, endval=1):
    if n <= 1:
        return endval
    return jiecheng(n-1, endval * n)
print(jiecheng(5))

十五、python内置模块

1、math数学模块

ceil、floor(获得到的是整数)、pow、sqrt、fabs、modf、copysign、fsum(获取到的是浮点型) pi

使用方法:import 导入 math 模块 import math

ceil:向上取整

floor:向下取整

pow:计算一个数的n次方(对比内置pow函数)

sqrt:开平方运算

fabs:计算一个数的绝对值

modf:将一个数拆分成整数和小数部分组成元组

copysign:将参数第二个数值的正负号拷贝给第一个

fsum:将一个容器数据中的数据进行求和运算(对比内置sum)

import math

# ceil
ret1 = math.ceil(3.01)
print(ret1)

# floor
ret2 = math.floor(4.1)
print(ret2)

# pow
ret3 = math.pow(4, 2)
print(ret3)

# sqrt
ret4 = math.sqrt(9)
print(ret4)
fudian

# fabs
ret5 = math.fabs(-9)
print(ret5)

# modf
ret6 = math.modf(-19.8)
print(ret6)

# copysign
ret7 = math.copysign(-90, 18)
print(ret7)

# fsum
lst = [1, 2, 3, 4]
ret8 = math.fsum(lst)
print(ret8)

# 圆周率pi
res = math.pi
print(res)

2、random随机数模块

random、randrange、randint、uniform、choice、sample、shuffle

使用方法:import 导入 random 模块 import random

大部分取左不取右原则

random:获取0-1之间的小数

randrange:随机获取指定范围内的整数(有间隔值,类似range)

randint:随机产生指定范围内的随机整数(可以取到最大值,脱离取左不取右原则)

uniform:获取指定范围内的随机小数

choice:随机获取数列中的值(多选一)

sample:随机获取序列中的值(多选多)

shuffle:随机打乱序列中的值(直接打乱原序列)

import random

# random 
ret1 = random.random()
print(ret1)

# randrange
ret2 = random.randrange(3)
print(ret2)
ret3 = random.randrange(3, 7)
print(ret3)
ret4 = random.randrange(3, 10, 2)
print(ret4)

# randint
ret5 = random.randint(1, 3)
print(ret5)

# uniform
ret6 = random.uniform(1, 3)
ret7 = random.uniform(3, 1)
print(ret6, ret7)

# choice
lst = ['a', 'b', 'c', 'd']
ret8 = random.choice(lst)
print(ret8)

def mychoice(lst):
    num = random.randrange(len(lst))
    return lst[num]
print(mychoice(lst))

mychoice = lambda lst : lst[random.randrange(len(lst))]
print(mychoice(lst))

# sample
ret9 = random.sample(lst, 2)
print(ret9)

# shuffle
ret10 = random.shuffle(lst)
print(lst)

3、内置函数

abs、round、sum、max、min、sorted、pow、range、bin、oct、hex、chr、ord、eval、esec、repr、hash、input

abs:绝对值函数

round:四舍五入函数(注:n.5 n为偶数则舍去,n.5 n为奇数,则进一)注:其余情况正常四舍五入

sum:计算序列的和,列表、集合、元组、字典皆可,注:字典只会取到键,所有元素必须是数字,不能是字符或其它

max:获取列表里面的最大值(高级函数)

min:获取列表里面的最小值(高级函数)

​ 列表、集合、元组、字典皆可,注:字典只会取到键,所有元素必须是数字,不能是字符或其它

sorted:找出最大值和最小值,注:字典只有键(高级函数)

pow:计算某个数值的n次方,注:两个参数是计算n次方,三个参数是计算n次方后对第三个参数取余

range:产生指定范围的可迭代对象

bin:将十进制数转化为二进制数

oct:将十进制数转化为八进制数

hex:将十进制数转化为十六进制数

chr:将ASCII编码转换为字符

ord:将字符转化为ASCII编码

eval:返回表达式计算结果;将字符串当作python代码执行

esec:将字符串当作python代码执行(功能更强大)

​ 注:慎用eval和esec尤其是在和用户交互数据的时候

repr:不转义字符输出字符串

hash:生成哈希值,两个相同的字符串,无论哈希多少次,都会产生相同的唯一值

​ hash的两个作用:给密码加密和文件的校验,比较文件内容

input:接收输入字符串

# abs
intvar = -16
ret = abs(intvar)
print(ret)

# round
fvar1 = 3.5
fvar2 = 4.5
fvar3 = 4.51
fvar4 = 4.2
fvar5 = 3.2
print(round(fvar1), round(fvar2), round(fvar3), round(fvar4), round(fvar5))

# sum
lst = (1, 2, 3, 4, 5)
lst = [1, 2, 3, 4, 5]
lst = {1, 2, 3, 4, 5}
lst = {1: 2, 3: 4}
ret = sum(lst)
print(ret)

# max,min
lst = [10, 100, -3, 40]
lst = (10, 100, -3, 40)
lst = {10, 100, -3, 40}
lst = {10: 100, -3: 40}
maxvar = max(lst)
minvar = min(lst)
print(maxvar, minvar)

# sorted
lst = [10, 100, -3, 40]
lst = (10, 100, -3, 40)
lst = {10, 100, -3, 40}
lst = {10: 100, -3: 40}
lst_new = sorted(lst)
maxvar = lst_new[-1]
minvar = lst_new[0]
print(maxvar, minvar)

lst = [("a", 25), ("b", 50), ("c", 18)]
def func(n):
    return n[-1]
ret1 = min(lst, key=func)
ret2 = max(lst, key=func)
print(ret1, ret2)

# pow
ret = pow(2, 3)
print(ret)
ret = pow(2, 3, 5)
print(ret)

# range
for i in range(1, 11, 3):
    print(i)
for i in range(10, 0, -3):
    print(i)

# bin oct hex
res = bin(5)
print(res)
res = oct(8)
print(res)
ret = hex(255)
ret = hex(16)
print(ret)

# chr
ret = chr(65)
print(ret)
ret = chr(97)
print(ret)

# eval
strvar = "print('123')"
print(strvar)
eval(strvar)

# exec
strvar = "a = 10"
strvar = '''
for i in range(10):
    print(i)
'''
exec(strvar)
# print(a)

# repr
ret = "E:\workspace3.6\text"
print(repr(ret))

# hash
strvar1 = "abccc"
strvar2 = "abccc"
print(hash(strvar1))
print(hash(strvar2))
with open("ceshi1.txt",mode="r",encoding="utf-8") as fp:
    strvar = fp.read()
    res1 = hash(strvar)

with open("ceshi2.txt",mode="r",encoding="utf-8") as fp:
    strvar = fp.read()
    res2 = hash(strvar)

print(res1,res2)

4、pickle序列化模块

(转化字节流)

dumps、loads、dump、load

文件内部进行数据序列化操作时用:dump、load

文件外部进行数据序列化操作时用:dumps、loads

使用方式:import 导入 pickle 模块 import pickle

序列化:把不能直接存储在文件中的数据变得可存储,这个过程就是序列化

反序列化:把文件中的数据取出,恢复成原来的数据类型,这个过程就是反序列化

文件中存储两种存储形式:字符串和字节流

注:pickle会存储数据的原类型,(数据类型,对象,函数等)

import pickle
# 列表
lst = [1, 2, 3]
res = pickle.dumps(lst)
print(res)

# 函数
def func():
    print("函数")
res = pickle.dumps(func)
print(res)

# 迭代器
it = iter(range(10))
res = pickle.dumps(it)
print(res)

res = pickle.loads(res)
print(res, type(res))
for i in range(4):
    res2 = next(res)
    print(res2)

# 文件S 
dic = {'a': 1, 'b': 2}
with open("ceshi3.txt", mode="wb") as fp:
    pickle.dump(dic, fp)
with open("ceshi3.txt", mode='rb') as fp:
    res = pickle.load(fp)
print(res, type(res))

5、json序列化模块

(转化字符串)

编程语言通用的序列化方式,序列化成字符串,

可序列化成字符串的类型:int、float、bool、str、list、tuple、dict、None

dumps、loads、dump、load

使用方式:import 导入 json模块 import json

load:文件中是一次性反序列化所有数据

loads:文件中是一次性反序列化一行数据

json和pickle的区别:

​ json可连续dump,不可连续load,序列化成字符串(数据交流),适用于所有语言,跨平台性

​ pickle可连续dump,可连续load,序列化成bytes(存储转换),仅限于python之间的存储传输

json的dumps两个参数:ensure_ascii=False 不通过ascii来显示内容 sort_keys=True 对字典的键来进行排序

try ... except ... 抛出异常

注:当进行多次dump操作的时候,load会出错,可以使用loads操作

import json

# 字典
dic = {"name": "eric", "age": 30, "classroom": "python30", "family": ["阿斯蒂", "阿松大", "阿萨德", "阿什顿"]}

ret = json.dumps(dic, ensure_ascii=False, sort_keys=True)
print(ret, type(ret))

dic = json.loads(ret)
print(dic, type(dic))

# 文件
with open("ceshi5.json", mode="w", encoding="utf-8") as fp:
    json.dump(dic, fp, ensure_ascii=False)

with open("ceshi5.json", mode='r', encoding='utf-8') as fp:
    ret = json.load(fp)
print(ret, type(ret))

# 多次dump json
dic1 = {'a': 1, 'b': 2}
dic2 = {'c': 3, 'd': 4}
with open("ceshi6.json", mode='w', encoding="utf-8") as fp:
    json.dump(dic1, fp)
    fp.write("\n")
    json.dump(dic2, fp)
    fp.write('\n')
with open("ceshi6.json", mode='r', encoding="utf-8") as fp:
    for i in fp:
        dic = json.loads(i)
        print(dic)

# 多次dump pickle
import pickle
dic1 = {"a": 1, "b": 2}
dic2 = {"c": 3, "d": 4}
with open("ceshi7.pkl", mode="wb") as fp:
    pickle.dump(dic1, fp)
    pickle.dump(dic2, fp)

with open("ceshi7.pkl", mode="wb") as fp:
    pickle.dump(dic1, fp)
    pickle.dump(dic2, fp)

# 方式一:
with open("ceshi7.pkl", mode="rb") as fp:
    dic = pickle.load(fp)
    print(dic)
    dic = pickle.load(fp)
    print(dic)
# 方式二:
try:
    with open("ceshi7.pkl", mode="rb") as fp:
        while True:
            dic = pickle.load(fp)
            print(dic)
except:
    pass

6、time时间模块

time(时间戳)、localtime、mktime、ctime、asctime、sleep、strftime、strptime、perf_counter

使用方式:import 导入 time 模块 import time

time:获取本地时间戳

localtime:默认返回当前时间戳对应的时间元组

mktime:通过时间元组获取时间戳(参数是时间元组)

ctime:获取本地时间字符串(参数是时间戳,默认当前)

asctime:通过时间元组获取字符串时间(参数是时间元组):缺陷不能识别今天是周几

sleep:阻塞,程序睡眠

strftime:格式化时间字符串,参数(格式化字符串,时间元组),只有格式化字符串时默认是当前系统时间

​ 注:strftime在linux操作系统下支持中文,windows系统下不支持中文

strptime:时间字符串格式化为时间元组,参数(时间字符串,格式化时间字符串格式)

perf_counter:用于计算程序运行的时间

import time
# time
ret = time.time()
print(ret)

# localtime
res = time.localtime()
res = time.localtime(123456789)
print(res)

# mktime
rep = (2020, 5, 20, 19, 39, 20, 0, 0, 0)
ret = time.mktime(rep)
print(ret)

# ctime
rec = time.ctime()
print(rec)

# asctime
rep = (2020, 5, 20, 19, 39, 20, 0, 0, 0) #倒数第三个参数是星期几,需要自行设置
ret = time.asctime(rep)
print(ret)

# sleep
# time.sleep(2)
# print(1234)

# strftime
rep = (2020, 5, 20, 19, 39, 20, 0, 0, 0)
ret1 = time.strftime("%Y-%m-%d %H-%M-%S")
ret2 = time.strftime("%Y-%m-%d %H-%M-%S", rep)
print(ret1, ret2)

# strptime
strvar = "2020年5月20号11时12分13秒,今天上街要是谁的玫瑰花那个刺扎到我了,没个3,5万,我绝对不起来"
ret = time.strptime(strvar, "%Y年%m月%d号%H时%M分%S秒,今天上街要是谁的玫瑰花那个刺扎到我了,没个3,5万,我绝对不起来")
print(ret)

# perf_counter
starttime = time.time()
for i in range(100000000):
    pass
endtime = time.time()
print(endtime - starttime)

starttime = time.perf_counter()
for i in range(100000000):
    pass
endtime = time.perf_counter()
print(endtime - starttime)

7、os模块

system、popen、listdir、getcwd、chdir、environ、name、sep、linesep、mknod、remove、mkdir、rmdir、rename、makedirs、removedirs、basename、dirname、split、join、splitext、getsize、isdir、isfile、islink、isabs、getctime、getmtime、getatime、abspath

使用方式:import 导入 os 模块 import os

(1)执行关于系统的命令

​ system、popen、listdir、getcwd、chdir、environ、name、sep、linesep

​ system:在python中执行系统命令

​ popen:执行系统命令返回对象,通过read方法读出字符串

​ listdir:获取指定文件夹中所有内容的名称列表

​ getcwd:获取当前文件所在的默认路径

​ chdir:修改当前文件工作的默认路径

​ environ:获取或修改环境变量

​ name:获取系统标识(nt代表windows操作系统,posix代表linux或者Mac系统)

​ sep:获取路径分割符号

​ linesep:获取系统换行符号

import os

# system
# os.system("calc")
# os.system("mspaint")
# os.system("ipconfig")

# popen
obj = os.popen("ipconfig")
print(obj)
print(obj.read())

# listdir
res1 = os.listdir()
res2 = os.listdir(".")
res3 = os.listdir("..")
res4 = os.listdir(r"E:\workspace3.6\text")
print(res1)
print(res2)
print(res3)
print(res4)

# getcwd
ret = os.getcwd()
print(ret)

# chdir
# os.chdir(r"E:\workspace3.6\text")
os.chdir("..")
os.system("type nul>penthe.php")

# environ
print(os.environ)
print(os.environ["PATH"])

# name
print(os.name)

# sep
print(os.sep)

# linesep
res = repr(os.linesep)
print(res)

(2)文件的创建和删除,文件和文件夹的命令

​ mknod、remove、mkdir、rmdir、rename、makedirs、removedirs

​ mknod:linux中可以创建文件,windows中不会创建文件;windows中可以使用system创建文件

​ remove:删除文件

​ mkdir:创建目录

​ rmdir:删除目录(注:只会删除空的文件夹)

​ rename:对文件,目录重命名

​ makedirs:递归创建文件夹

​ removedirs:递归删除文件夹(注:只会删除空的文件夹)

import os
os.chdir(r"E:\workspace3.6\text")

# mkmod和remove
os.mknod("123.py")
os.system('type nul>123.py')
os.remove("123.py")

# mkdir和rmdir
os.mkdir("ceshi")
os.rmdir("ceshi")

# rename
os.rename("ceshi", "ceshis")

# makedirs和removedirs
os.makedirs("ceshi/ce/shi")
os.removedirs("ceshi/ce/shi")

(3)路径模块操作命令

​ basename、dirname、split、join、splitext、getsize、isdir、isfile、islink、isabs、getctime、getmtime、getatime、abspath

​ basename:返回文件名,若有文件名就返回文件名,没有会返回最后一级目录名

​ dirname:返回路径

​ split:将路径拆分成单独的文件部分和路径部分,组成一个元组

​ join:路径拼接,可拼接多个,针对不同系统会自动增添配套的路径分割斜杠

​ splitext:将路径分割为后缀和其他部分

​ getsize:获取文件的大小,只能是文件

is相关:isdir、isfile、islink、isabs

​ isdir:检测路径是否是一个文件夹

​ isfile:检测路径是否是一个文件

​ islink:检测路径是否是一个链接

​ isabs:检测路径是否是绝对路径

时间相关:getctime、getmtime、getatime、abspath、exists

​ getctime:windows是获取文件创建的时间。linux是获取文件权限改动的时间(返回时间戳)

​ getmtime:获取最后一次修改时间(返回时间戳)

​ getatime:获取文件最后一次访问时间(返回时间戳)

​ abspath:相对路径转化为绝对路径

​ exists:检测指定路径是否存在

import os
pathvar = r"E:\workspace3.6\text\shopping.json"

# basename
ret = os.path.basename(pathvar)
print(ret)

# dirname
ret = os.path.dirname(pathvar)
print(ret)

# split
print(os.path.split(pathvar))

# join
rep1 = r'E:\workspace3.6'
rep2 = r"text\shopping.json"
rep = os.path.join(rep1, rep2)
print(rep)

# splitext
print(os.path.splitext(pathvar))

# getsize
res = os.path.getsize(pathvar)
print(res)

# isdir、isfile、islink、isabs
ret1 = os.path.isdir(pathvar)
ret2 = os.path.isfile(pathvar)
ret3 = os.path.islink(pathvar)
ret4 = os.path.isabs(pathvar)
print(ret1, ret2, ret3, ret4)

# getctime、getmtime、getatime
res1 = os.path.getctime(pathvar)
res2 = os.path.getmtime(pathvar)
res3 = os.path.getatime(pathvar)
import time
ret1 = time.ctime(res1)
ret2 = time.ctime(res2)
ret3 = time.ctime(res3)
print(ret1, ret2, ret3, ret4)

# exists
res = os.path.exists(pathvar)
print(res)

# abspath
pathvar = "."
res = os.path.isabs(".")
print(res)
if not os.path.isabs("."):
    res = os.path.abspath(pathvar)
    print(res)

8、shutil模块

copy、copytree、rmtree、move

复制剪切,文件和文件夹;shutil与os中的创建和删除功能互相配合

使用方式:import 导入 shutil 模块 import shutil

copy:复制文件权限和内容

copytree:递归拷贝拷贝文件夹里所有内容

rmtree:递归删除文件夹里面所有内容

move:移动文件或者文件夹

import shutil
# copy
shutil.copy("shopping.json", "shop.json")

# copytree和rmtree
shutil.copytree("shopping", "shop")
shutil.rmtree("shopping", "shop")

# move
shutil.move("5-18test.py", "ceshis")

9、zipfile压缩模块

使用方式:import 导入 zipfile 模块 import zipfile

r:读 w:写 a:追加

(1)创建压缩文件:zipfile.ZipFile(文件路径,“w”, 压缩方式),默认只存储打包,

​ zipfile.ZIP_STORED:默认只存储打包,不会对文件进行压缩

​ zipfile.ZIP_DEFLATED:对文件进行压缩操作

​ 写入文件:write(路径,别名),可临时创建一个文件夹

(2)打开压缩文件:zipfile.ZipFile(文件路径,“r”)

​ 解压文件:

​ extractall(文件夹)解压所有

​ extract(文件,文件夹)

(3)追加文件:zipfile.ZipFile(文件路径,“a”,压缩方式),

(4)查看压缩包文件:namelist() 返回一个列表

(5)关闭文件:close

(6)如果要操作的zip文件大小超过2G,应该将allowZip64设置为True。

import zipfile

# 创建压缩文件
zf = zipfile.ZipFile("ceshi1.zip", "w", zipfile.ZIP_DEFLATED)

# 写入到压缩包中
zf.write(r"5-20test.py")
zf.write(r"E:\workspace3.6\text\shopping.py", 'shopping.py')
zf.write(r"E:\workspace3.6\text\shopping.py", 'tmp/shopping.py')
# 关闭压缩包
zf.close()

# 从压缩包解压文件
zf = zipfile.ZipFile("ceshi1.zip", "r")
# 解压所有文件
zf.extractall("./ceshi")
# 解压单个文件
zf.extract("shopping.py", "./ceshi")
# 关闭压缩包
zf.close()

zf = zipfile.ZipFile('ceshi1.zip', 'a', zipfile.ZIP_DEFLATED)
# 追加文件
zf.write(r"E:\workspace3.6\text\shopping.json", "shopping.json")
zf.close()

with zipfile.ZipFile("ceshi1.zip", "a", zipfile.ZIP_DEFLATED) as zf:
    zf.write(r"E:\workspace3.6\text\shopping.json", "shopping.json")

# 查看文件
with zipfile.ZipFile("ceshi1.zip", "r", zipfile.ZIP_DEFLATED) as zf:
    lst = zf.namelist()
print(lst)

10、tarfile压缩模块

使用方式:import 引入 tarfile 模块 import tarfile

r 读 w 写 a追加:只能对w压缩后的进行追加

有三种压缩方式:.tar .tar.gz .tar.bz2

官方说法,.tar.bz2 的压缩算法,包的大小最小

(1)创建压缩文件:

​ tarfile.open(压缩文件包,“w”,encoding=“utf-8”)

​ tarfile.open(压缩文件包,“w:gz”,encoding=“utf-8”)

​ tarfile.open(压缩文件包,“w:bz2”,encoding=“utf-8”)

​ 写入压缩文件:add(文件路径,文件别名)

(2)解压文件:

​ extractall(路径) :解压所有文件

​ extract(文件,路径) :解压指定文件

(3)追加文件:只能针对w模式下的打包进行追加,其他模式不行

(4)查看压缩包文件:getnames()

(5)关闭文件:close()

import tarfile

# 创建tar压缩包
tf = tarfile.open("ceshi2.tar", "w", encoding="utf-8")

# 写入压缩文件
tf.add(r"E:\workspace3.6\text\shopping.json", "shopping.json")
tf.add(r"shopping.py", "shopping.py")
tf.add(r"E:\workspace3.6\text\5-20test.py", "tmp/5-20test.py")

# 关闭文件
tf.close()

# 创建tar.gz压缩包
tf = tarfile.open("ceshi3.tar.gz", "w:gz", encoding="utf-8")
tf.add(r"E:\workspace3.6\text\shopping.json", "shopping.json")
tf.add(r"shopping.py", "shopping.py")
tf.add(r"E:\workspace3.6\text\5-20test.py", "/tmp/5-20test.py")
tf.close()

# .tar.bz2压缩包
tf = tarfile.open("ceshi0515.tar.bz2", "w:bz2", encoding="utf-8")
tf.add(r"E:\workspace3.6\text\shopping.json", "shopping.json")
tf.add(r"shopping.py", "shopping.py")
tf.add(r"E:\workspace3.6\text\5-20test.py", "/tmp/5-20test.py")
tf.close()

# 解压文件
tf = tarfile.open("ceshi2.tar", "r", encoding="utf-8")
# extractall(路径)
tf.extractall("ceshi2")
# extract(文件, 路径)
tf.extract("shopping.py", "ceshi2")
tf.close()

# 追加文件
with tarfile.open("ceshi2.tar", "a", encoding="utf-8") as tf:
	tf.add(r"E:\workspace3.6\text\5-20test.py", "5-20test.py")

# 查看压缩包
with tarfile.open("ceshi2.tar", "r", encoding="utf-8") as tf:
	lst = tf.getnames()
print(lst)

(6)tarfile不能追加的文件bug优化

"""
tarfile 的解决方法:文件或者文件夹都可以通过add放到压缩包中
(1) 先解压原来的压缩包
(2) 把要追加的内容放进去
(3) 过滤数据,重新打包
"""
import os
pathvar = os.getcwd()
# 压缩包的路径
pathvar1 = os.path.join(pathvar, "ceshi2.tar.bz2")
print(pathvar1)
# 解压文件夹的路径
pathvar2 = os.path.join(pathvar, "ceshi2")
print(pathvar2)

# 1.先解压压缩包
with tarfile.open(pathvar1, "r", encoding="utf-8") as tf:
    tf.extractall(pathvar2)

# 2.把追加的内容放进来
import shutil
shutil.copy(r"E:\workspace3.6\text\5-20test.py", pathvar2)

# 3.过滤数据,重新打包
"""过滤掉5-20test.py这个文件,剩下的文件重新打包"""
lst = os.listdir(pathvar2)
print(lst)

with tarfile.open(pathvar1, "w:bz2", encoding="utf-8") as tf:
    for i in lst:
        pathnew = os.path.join(pathvar2, i)
        print(pathnew)
        # add(路径,别名)
        if i != "5-20test.py":
            tf.add(pathnew, i)

十六、模块导入

模块与包的概念:

​ 模块是一个文件

​ 包是一个文件夹

文件夹里面可以有很多的文件,就相当于包里面有很多的模块

import导入时,导入一次即可

1、模块部分

1)导入方式:import 模块名

(1)模块.变量

(2)模块.函数

(3)模块.类

2)导入任意路径下的模块:默认只能导入当前文件所在的这个文件夹下的所有模块,通过sys.path可以导入任意路径下的的模块;导入模块时,会去找sys.path这个列表里面的路径,如果找到就导入,找不到就报错,通过append可以在列表里追加路径,来实现导入的功能

​ import sys

​ sys.path

​ sys.path.append(r"路径")

​ 通过添加用户环境变量实现导入模块

3)调用方式起别名

​ import 模块 as 别名

4)from ... import ... 从...导入...

​ 导入单个:from 模块 import ...

​ 导入多个:from 模块 import ... , ... , ...

​ 导入所有:from 模块 import *

​ 起别名:from 模块 import ... as ...

​ 定制*号范围:在需要导入的模块中设置:__all__ = ["函数名、变量或类"] 中括号中是允许调用的方法

5)__name__

​ 返回模块名字的魔术属性__name__

​ 当文件是直接运行的,返回__main__

​ 当文件是间接导入的,返回当前文件名(模块名)

2、包部分

1)导入方式:import 包名

​ __init__.py文件是对包进行初始化的脚本文件,导入包的时候,系统自动调用__init__文件,对当前文件夹进行初始化,实际是对init中的文件属性进行导入

​ 包下的其它模块可以通过初始化文件间接导入

(1)导入包,通过init文件初始化,默认引入init文件中所有属性:import 包名

(2)引入包中具体的一个模块:

  • 方法一:import 包名.模块名

    ​ 起别名:import 包名.模块名 as 别名

  • 方法二:通过__init__间接导入,更加简洁

    ​ 直接在init文件中导入模块方法

(3)用from ... import 从包导入成员(落脚点在成员或者模块)

​ from 包名 import 变量

​ from 包名 import 方法

​ from 包名.模块 import 方法

​ 别名通过 as

​ from 包名 import 变量 as 别名

​ from 包名 import 方法 as 别名

​ from 包名.模块 import 方 as 别名

(4)导入所有

​ from 包名 import *

3、单入口模式(相对路径导入)

单入口模式:只通过主文件来调用分模块内容,分模块不单独执行

​ 分模块不能单独执行调用,统一由主文件main进行调用

​ 模块之间的互相嵌套导入,使用相对路径实现

​ 单入口文件必须和包在同一层级,包里面可以含有各种包和模块

from . import pkg           相对于当前路径引入模块
from .pko2 import ceshi204  相对于当前模块引入具体属性
from .. import pkg2_module1 相对于上一级引入模块
from ..pkg2_two import pkt1 相对于上一级的某个包引入模块
from ..pkg2_two.pkt2 import ceshi209 相对于上一级某个包下的某个模块引入某属性

.      当前路径
..     当前路径的上一层
...    当前路径的上一层的上一层
....   当前路径的上一层的上一层的上一层
可以一直添加上一层,无边界
运用于from ........... import   中

4、应注意__name__ == "__main__"

如果被直接执行 , 返回 : __main__

​ print("<=====================>")
​ print(__name__)
​ print("<=====================>")

防止当作模块导入时,下面的代码被执行 , 下面的代码是做测试的,不需要导入;

​ if __name__ == "__main__":

​ pass

十七、正则表达式

需要使用re模块:import re

1、预定义字符集

\d 匹配数字

\D 匹配非数字

\w 匹配字母数字或下划线(正则中支持中文的匹配,中文会被匹配到)

\W 匹配非字母数字下划线(中文不会被匹配到)

\s 匹配任意的空白符 \n \t \r

\S 匹配非空白符

\n 匹配一个换行符

2、字符组

必须匹配中括号里列举的字符[]

- 代表一个范围 [0-9] 代表范围0-9

^ 在字符组当中代表除了...... [^] 除了0-9

想要匹配 - 和 ^ 使用 \ 使转义字符失效

匹配 \ 使用 \ ,转义斜线 \

3、多个字符的匹配

(1)?匹配0个或1个

(2)+ 匹配1个或者多个

(3)* 匹配0个或多个

(4){m, n} 匹配m个至n个

4、贪婪模式与非贪婪模式

贪婪模式:默认向更多次匹配,底层用的是回溯算法

非贪婪模式:默认向更少次匹配,用一个?来进行修饰

​ ?? +? *? {}?

当想要使用?进行非贪婪匹配的时候需要使用??

. 匹配任意字符,除了\n

\ 使有意义的特殊字符失去效果,使某些无意义的字符有有特殊效果

回溯算法:从左向右进行匹配,一直到最后,直到最后再也匹配不到,就回上一级,继续寻找

5、边界符 \b ^ $

\b backspace 以什么开头或结尾 使用时需注意转义,避免出错可以先用r声明

​ \b 放在前面表示卡住左边界

​ \b 放在后面表示卡住右边界

^ 以什么开头

$ 以什么结尾

^ ... $ 一个整体的形式进行匹配

6、正则分组

()正常分组:只显示括号里面的内容

(?: )取消分组:显示所有匹配到的内容

| 代表或 a|b 匹配a或者b,难匹配的放前面,容易匹配的放后面

​ 使用时要注意,|左边的会先匹配,右边的会在左边匹配完后再在进行匹配,就是类似1212这种形式

7、search函数

findall 把所有匹配到的字符串都搜出来,返回列表;但是不能把分组内容和匹配的内容同时显示出来

search 只匹配第一次匹配到的结果,返回对象;可以把分组内容和匹配内容同时显示出来

​ 通过group()方法直接获取匹配到的内容

​ 通过groups()方法直接获取匹配到的分组中的内容

​ 注:search 在匹配不到时,返回的是None,无法使用group,如若使用group就会报错显示无此方法

8、名命分组

.*? 当不知道所匹配的字符是什么的时候可以使用此方式

(1)反向引用

​ \1代表反向引用,将第一个括号匹配的字符串,在 \1 处再引用一次

​ \1 代表第一个括号 , \2 代表第二个括号

(?P<组名>正则表达式)给这个组起一个名字

(?P=组名)引用之前组的名字,把该组匹配到的内容放到当前位置

# 方法一
strvar = "a1b2cab"
obj = re.search(r"(?P<tag1>.*?)\d(?P<tag2>.*?)\d(?P<tag3>.*?)\1\2",strvar)
print(obj.group())  # a1b2cab

# 方法二
strvar = "a1b2cab"
obj = re.search(r"(?P<tag1>.*?)\d(?P<tag2>.*?)\d(?P<tag3>.*?)(?P=tag1)(?P=tag2)",strvar)
print(obj.group())  # a1b2cab

9、正则函数

findall search match split sub subn finditer compile

findall:匹配所有符合条件的字符,并返回到列表中

search:匹配第一次匹配到的符合条件的,返回对象,通过group获取结果,通过groups获取分组结果并返回元组中

match:验证用户输入内容,search在正则表达式中加上^就和match效果一样

split:切割,比字符串的split切割功能更加强大

sub:替换,比字符串的replace替换功能更加强大(使用方式:正则表达式,替换的字符串,原字符串,[可选的替换次数])

subn:替换,和sub的使用方法一样,但返回值是一个元组

finditer:匹配字符串中相应的内容,返回迭代器

compile:指定一个统一的匹配规则;

​ 正常情况下,正则表达式执行一次,编译一次,如需要反复使用,会浪费系统资源,比如内存,cpu

​ 可以使正则编译一次,无限使用,无需反复编译

import re
# search
strvar = "1+2 3*4 "
ret = re.search("\d+(.*?)\d+", strvar)
print(ret.group(), type(ret.group()))
ret1 = ret.groups()
print(ret1, type(ret1))

# match
strvar = "s12346579"
strvar = "465879132"
ret = re.search("^\d+", strvar)
print(ret.group())
ret = re.match("\d+", strvar)
print(ret.group())

# split
strvar = "eric|dsf|sdf&sdf-asf"
ret = re.split("[|&-]", strvar)
print(ret)
strvar = "13af4534fasf33saf34afggg"
ret = re.split("\d+", strvar)
print(ret)

# sub
strvar = "asdf|faf&sadf-asf"
ret = re.sub("[|&-]", "%", strvar)
print(ret)

ret = re.sub("[|&-]", "%", strvar, 1)
print(ret)

# subn
ret = re.subn("[|&-]", "%", strvar)
print(ret)

ret = re.subn("[|&-]", "%", strvar, 1)
print(ret)

# finditer
from collections import Iterator
strvar = "sdfasasdf1234654撒法dfsa132"
it = re.finditer(r"\d+", strvar)
print(isinstance(it, Iterator))

for i in range(2):
    print(next(it).group())

for i in it:
    print(i.group())

# compile
strvar = "adsf12346学位DVD133"
pattern = re.compile("\d+")
print(pattern)

ret = pattern.search(strvar)
print(ret.group())
ret = pattern.findall(strvar)
print(ret)
ret = re.compile("\d+").findall(strvar)
print(ret)

10、修饰符

re.I re.M re.S

re.I:使匹配对大小写不敏感

re.M:使每一行都能够单独匹配(多行匹配),影响^和$

re.S:使 . 匹配包括换行\n在内的所有字符

# re.I
strvar = "<h1>123</H1>"
pattern = re.compile("<h1>(.*?)</h1>", flags=re.I)
ret = pattern.search(strvar)
print(ret)
print(ret.group())
print(ret.groups())

# re.M
strvar = """<h1>123</H1>
<p>123</p>
<div>123</div>
"""
pattern = re.compile("^<.*?>(?:.*?)<.*?>$", flags=re.M)
print(pattern.findall(strvar))
pattern = re.compile("^<.*?>(?:.*?)<.*?>$", flags=re.M).findall(strvar)
print(pattern)

# re.S
strvar = """give
123213mezdmefive
"""
pattern = re.compile("(.*?)mefive", flags=re.S)
ret = pattern.search(strvar)
print(ret.group())

十八、面向对象(oop)

面向过程编程POP(Procedure-Oriented Programming)

面向对象编程OOP(Object Oriented Programming)

(1)类的定义:

​ 第一种定义方式

​ class Car:

​ pass

​ 第二种定义方式

​ class Car():

​ pass

​ 第三种定义方式

​ class Car(object):

​ pass

(2)类的实例化

​ class Car():

​ pass

​ obj = Car()

(3)类的基本结构

​ 类中有两种成员:

​ 成员属性

​ 成员方法

(4)类的命名推荐使用大驼峰命名法,即每个单词的首字母大写

(5)面向对象编程的三大属性:封装、继承、多态

注:类中的代码可以直接执行,但是严禁使用,不要直接把代码裸漏在类中,把相应的逻辑放在成员方法中处理;

class Car:
	pass
	
class Car():
	pass

class Car(object):
	pass

obj = Car()

1、面向对象之封装

(1)类中的封装:成员属性、成员方法

(2)成员属性的定义:

​ 公有属性:成员属性的定义公有的成员属性,就是直接定义:mycolor = “1234”

​ 私有属性:成员属性的定义私有的成员属性,通过两个下划线定义:__mycolor = "1234"

​ 成员方法的定义:

​ 公有方法:成员方法的定义公有的成员属性,就是直接定义:def myFunc() :

​ 私有方法:成员方法的定义私有的成员属性,通过两个下划线定义:def __myFunc():

(3)私有成员是通过改名策略实现的

(4)封装等级

​ 公有成员既能在类外调用,也能在类内调用

​ 私有成员只能在类内进行调用,不能在类外进行调用

(5)类中的绑定方法(方法在类的内部)

​ 绑定到对象:对象调用方法时,系统自动把该对象当成参数进行传递

​ 绑定到类:对象或者类调用方法时,系统自动把该类当成参数进行传递

(6)注:对象在调用方法时,系统会自动把该对象当成参数传递到该方法中,为了保证形参和实参一一对应,在定义方法的时候,要叫形参self,这个方法也叫绑定方法(绑定到对象)

(7)实例化的对象能动态添加公有的成员属性和成员方法

​ 注:动态添加的只能是公有的属性和方法,不能是私有的属性和方法

(8)通过__dict__方法查看对象或者类当中的成员,返回一个字典

​ 对象只会返回对象中的成员属性和成员方法

​ 类中只会返回类中的成员属性和成员方法

​ 通过in和not in 可以判断某个函数是否在类中

(9)对象调用的优先级

​ 调用对象的成员时,先看看自己有没有该成员

​ 有就现调用自己,没有就调用类中的,类中没有就报错

(10)类和对象之间的注意点

​ 类中的成员只归属于当前这个类本身,对象可以调用其中的公有成员,但不能进行修改和删除,类中的成员不属于对象中,对象和类是两个不同的空间。

​ 类无法调用对象中的相关成员,但是对象可以调用类中的公有成员

# 公有属性和私有属性
class Car:
    color = "黑色的"
    __logo = "the car"

    def drive(self):
        print("我的小车可以载货,可以超速跑高速", self.color)

    def __info(self):
        print("我的车牌是保密的,价格和耗油量也是保密的")


obj = Car()
print(obj.color)
obj.drive()

# 添加成员属性
obj.color = "白色的"
print(obj.color)
print(Car.__dict__)
print(obj.__dict__)

# 添加成员方法
def chegulu(color):
    print("{}车轱辘".format(color))

obj.chegulu = chegulu
obj.chegulu("红色的")

# 对象方法调用对象动态添加(或类内的成员属性)的成员属性
def chegulu(obj, msg):
    print("{}车轱辘{}".format(obj.color, msg))

obj.chegulu = chegulu
obj.chegulu(obj, "霸气")

# 通过types的MethodType创建一个绑定方法,自动传递obj对象
# 使用方式types.MethodType(函数,对象)当调用方法时,自动把对象当成参数传递给该方法
import types
def chegulu(self, msg):
    print("{}车轱辘{}".format(self.color, msg))

obj.chegulu = types.MethodType(chegulu, obj)
obj.chegulu("超奇葩")

# 动态添加匿名函数
obj.yinqinggai = lambda: print("红色的引擎盖")
obj.yinqinggai()
print(obj.__dict__)

1.1 私有成员和删除成员的相关操作

(1)私有成员的定义和使用上面已经提过,这里不再赘述

(2)删除成员通过del方法删除:del + 要删除的成员属性或成员方法

​ 注:删除时应注意对象中是否创建了该属性或方法,其次再去类中删除

(3)使用私有成员的两种方式

​ 第一种:私有成员的改名策略[_类名__成员名](不推荐使用)

​ 第二种:利用类内的公有方法间接调用私有成员 (推荐)

class Plane():
    captain = "阿萨德"
    __airsistem = 3
    def fly(self):
        print("飞机会飞,百公里蚝油200升1")

    def fly2(self):
        print("飞机会费,百公里耗油200升2")

    def __plane_info(self):
        print("飞机上的空姐数量是保密的,个数%d" % (self.__airsistem))

    def __plane_info2(self):
        print("飞机上的空姐数量是保密的,个数%d" % (self.__airsistem))

    def pub_info(self):
        print(self.__airsistem)
        self.__plane_info()

    def pub_info2(self):
        print(self.__airsistem)
        self.__plane_info2()

obj = Plane()
obj.fly()
# Plane.fly2()
print(Plane.__dict__)

# 通过私有成员改名策略实现调用私有成员
print(obj._Plane__airsistem)
print(Plane._Plane__airsistem)

obj._Plane__plane_info()
Plane._Plane__plane_info2()

# 通过类内的公有方法调用私有成员
obj.pub_info()
Plane.pub_info2()

# 删除成员属性
obj.captain = "阿什顿"
print(obj.__dict__)
del obj.captain
print(obj.captain)
print(obj.__dict__)

# 删除方法
obj.func = lambda: print("开飞机是为了赚钱")
obj.func()
del obj.func
print(obj.__dict__)
del Plane.captain
print(obj.__dict__)

# 注意点
obj.fly = lambda: print("112233")
del Plane.fly
del obj.fly
obj.fly()

1.2 构造方法

魔术方法:在python中通过双下划线(__)包起来定义的方法都叫魔术方法(Magic Method)

类中定义__init__方法的是构造函数

触发:实例化对象,初始化的时候触发

功能:为对象添加成员

参数:参数不固定,至少一个self参数

返回值:无

类可以是一个,对象可以是多个,可以通过一个类实例化多个不同的对象

每创建一个对象,都会单独占用一个空间,创建的越多,占用的空间就越大

# 基本语法
class MyClass():
    def __init__(self):
        self.name = "阿三"

obj = MyClass()
print(obj.name)

# 带有多个参数的构造方法
class MyClass():
    def __init__(self, name):
        self.name = name

obj = MyClass("阿飞")
print(obj.name)

2、面向对象之继承

继承:一个类除了自身所拥有的属性方法之外,还获取了另外一个类的成员属性和方法

一个类继承另一个类,那么该类就是子类(衍生类),被继承的这个叫做父类(基类,超类)

继承的3种形式:单继承、多继承、菱形继承

注:python中所有类都默认继承object类(python3.X版本,python2.X则不会默认继承object类)

单继承

一个子类只继承一个父类

继承的特点(子类继承父类)

​ (1)子类可以调用父类所有的公有成员

​ (2)子类不可以调用父类的私有成员

​ (3)子类可以重写父类的同名方法

调用优先级(子类继承父类)

​ 如果子类里面有该成员属性方法,优先调用自己的

​ 如果没有该成员,调用父类的公有成员

​ 如果都没有,直接报错

class Human(object):
    hair = "金色"
    sex = "男"

    def eat(self):
        print("人类的天性吃吃吃")

    def la(self):
        print('人类天生就需要lalala')

    def __makebaby(self):
        print("人类天生就能繁衍后代")

# 继承
class Man(Human):
    pass

obj = Man()
print(obj.hair)

class WoMan(Human):
    def pub_makbaby(self):
        self.__makebaby()

obj = WoMan()
obj.eat()

# 方法重写
class Children(Human):
    sex = "女性"
    def eat(self):
        print("小孩出生只能喝奶")

obj = Children()
print(obj.sex)
obj.eat()

多继承

一个子类继承多个父类

super用法

(1)super本身是一个类,super()是一个对象,用于调用父类的绑定方法

(2)super()只应用在绑定方法中,默认自动传递self对象(前提:super所在作用域存在self)

(3)super作用:解决复杂的多继承调用顺序

super调用父类的相关公有成员,不会调用自己类中的成员,父类若没有,报错

注:super和self不同,super只调用父类

# 多继承
class Father:
    property = "玉树临风,风流倜傥,一枝梨花压海棠"
    def f_hobby(self):
        print("吃喝嫖赌抽,坑蒙拐骗偷,抽烟喝酒烫头")

class Mother:
    property = "闭月羞花,沉鱼落雁,一只红杏出墙来"
    def m_hobby(self):
        print("打麻将,敷面膜,跳广场舞")

class Daughter(Mother, Father):
    pass

obj = Daughter()
print(obj.property)
obj.m_hobby()
class Father:
    property = "玉树临风,风流倜傥,一枝梨花压海棠"
    def f_hobby():
        print("吃喝嫖赌抽,坑蒙拐骗偷,抽烟喝酒烫头")

class Mother:
    property = "闭月羞花,沉鱼落雁,一只红杏出墙来"
    def m_hobby(self):
        print("打麻将,敷面膜,跳广场舞")

class Son(Father, Mother):
    property = "打游戏,比如lol,wow,dota2"

    # 使用类调用父类成员
    def skill1(self):
        print(Mother.property)
        Father.f_hobby()

    # 使用对象调用父类成员
    def skill2(self):
        self.m_hobby()
        print(self.property)

    def skill3(self):
        print(super())
        print(super().property)
        super().m_hobby()

obj = Son()
obj.skill1()
obj.skill2()
print('<===>')
obj.skill3()

菱形继承

菱形继承:两个子类继承同一个父类,而又有子类同时继承两个父类

mro列表:super用途的体现,解决复杂的多继承调用顺序

类.mro返回的是方法调用顺序的列表,针对于多继承下的同名方法,按照列表的顺序依次调用

issubclass:判断是否存在子父关系(使用方法和isinstance的使用一模一样),在同一继承链中即可

isinstance:判断类型,在同一继承链中即可

class BigMan():
    pass

class Human():
    pty = 4
    def feelT(self):
        print("原始人类天热了,让后裔把太阳射下来1")
        print(self.pty)
        print("原始人类天冷了,让女娲补天,防止漏风2")

class Man(Human):
    pyt = 3
    def feelT(self):
        print("现代男人天热了,吹空调3")
        super().feelT()
        print("现代男人天冷了,吹暖气4")

class Woman(Human):
    pty = 2
    def feelT(self):
        print("现代女人天热了,洗洗澡5")
        super().feelT()
        print("现代男人天冷了,吃雪糕6")

class Children(Man, Woman):
    pty = 1
    def feelT(self):
        print("现代小朋友天热了,洗海澡7")
        super().feelT()
        print("现代小朋友天冷了,钻被窝8")

obj = Children()
obj.feelT()

# mro
lst = Children.mro()
print(lst)

# issubclass
ret1 = issubclass(Children, Man)
ret2 = issubclass(Children, Woman)
ret3 = issubclass(Children, Human)
ret4 = issubclass(Children, BigMan)
ret = issubclass(Children, (Man, Woman, Human, BigMan))
print(ret1, ret2, ret3, ret4, ret)

# isinstance
ret1 = isinstance(obj, Children)
ret2 = isinstance(obj, Man)
ret3 = isinstance(obj, Human)
ret4 = isinstance(obj, Woman)
ret = isinstance(obj, BigMan)
print(ret1, ret2, ret3, ret4, ret)

print(Children.__dict__)

ret = "feelT" in Children.__dict__
print(ret)

3、面向对象之多态

不同的子类对象调用相同的父类方法,产生了不同的执行结果

继承和改写

class Soldier:

    def attack(self):
        pass

    def back(self):
        pass


class Army(Soldier):
    def attack(self):
        print("[陆军]上来拼刺刀,你捅我我捅你,一刀999")
    def back(self):
        print("[陆军]撒腿就跑")

class Navy(Soldier):
    def attack(self):
        print("[海军]拿起鱼叉,进行投射,插死一个算一个")
    def back(self):
        print("[海军]直接跳海,下海喂鱼")

class AirForce(Soldier):
    def attack(self):
        print("[空军]拿起二营长的意大利炮对敌方开展猛烈地攻击")
    def back(self):
        print("[空军]弃机跳伞,落地成盒")

obj_army = Army()
obj_navy = Navy()
obj_airforce = AirForce()
lst = [obj_army, obj_navy, obj_airforce]

flag = True
while flag:
    num = input("将军请下令:")
    for i in lst:
        if num == "1":
            i.attack()
        elif num == '2':
            i.back()
        elif num == '3':
            if isinstance(i, AirForce):
                i.attack()
            else:
                i.back()
        elif num.upper() == "Q":
            print("将军,辛苦了")
            sign = False
            break
        else:
            print("风太大,我听不见!")
            break

__new__ 魔术方法

触发时机:实例化类生成对象的时候触发,触发在init方法的前面

功能:控制对象的创建过程

参数:至少一个cls接受当前的类,其他根据情况决定

返回值:通常返回对象或None

注意点:

​ __new__方法可以返回其他类的对象,也能返回自己类的对象

​ __new__方法在__init__方法的前面执行

​ __new__方法的参数需要根__init__方法的参数一一对应

特殊注意:返回的不是本类的对象,不会触发__init__构造方发

# __new__基本使用
class MyClass():
    a = 1

obj = MyClass()
print(obj)

class MyClass1(object):
    def __new__(cls):
        print(cls)
        # (1)借助父类object 类.方法()
        # obj = object.__new__(cls)
        # (2)返回其它类的对象
        # return obj
        # (3)不反回任何类的对象
        return None
obj = MyClass1()
print(obj)

# __new__方法在__init__方法的前面执行
class Boat:
    def __init__(self):
        print(2)
    def __new__(cls):
        print(1)
        return object.__new__(cls)
obj = Boat()

# __new__方法的参数需要根__init__方法的参数一一对应
# 一个参数
class Boat:
    def __new__(cls, name):
        return object.__new__(cls)
    def __init__(self, name):
        self.name = name

obj = Boat("泰坦尼克号")
print(obj.name)

# 多个参数
class Boat:
    def __new__(cls, *args, **kwargs):
        return object.__new__(cls)
    def __init__(self, name, age, sex):
        self.name = name
        self.age = age
        self.sex = sex
obj = Boat('eric', 18, '男')
print(obj.name, obj.age, obj.sex)

# 返回的不是本类的对象,不会触发__init__构造放发
class MyClass():
    a = 1
other_obj = MyClass()

class Boat():
    def __new__(cls):
        return other_obj

    def __init__(self):
        print("1234")

obj = Boat()

单例(态)模式

23种设计模式之一

定义:无论实例化多少次,都只有一个对象

目的:为了节省内存空间,仅仅是为了调用类中的成员

使用情景:不需要额外给对象添加任何成员,这个场景使用单例模式

操作数据库的增删改查这样的类是不需要的

使用原则:有对象就直接返回,没有对象就给你创建,保证只有一个

# 有对象就直接返回,没有对象就给你创建,保证只有一个
class Singleton:
    __obj = None
    def __new__(cls):
        if cls.__obj is None:
            cls.__obj = object.__new__(cls)
        return cls.__obj

obj1 = Singleton()
print(obj1)
obj2 = Singleton()
print(obj2)
obj3 = Singleton()
print(obj3)

# 单态模式 + 构造方法
class Singleton:
    __obj = None
    def __new__(cls, *args, **kwargs):
        if cls.__obj is None:
            cls.__obj = object.__new__(cls)
        return cls.__obj
    def __init__(self, name):
        self.name = name

obj1 = Singleton("eric")
obj2 = Singleton("apds")
print(obj1.name)
print(obj2.name)

连贯操作

通过不停的调用下一个对象的操作就是连贯操作

# 两层
class MyClass1:
    pty1 = 10

class MyClass2:
    def __init__(self, obj):
        self.pty2 = obj

obj1 = MyClass1()
obj2 = MyClass2(obj1)
print(obj2.pty2.pty1)

# 三层
class MyClass1:
    pty0 = 111
    def func1(self):
        print("123456")

class MyClass2:
    def __init__(self, obj):
        self.pty1 = obj

class MyClass3:
    def __init__(self, obj):
        self.pty2 = obj

obj1 = MyClass1()
obj2 = MyClass2(obj1)
obj3 = MyClass3(obj2)

pty = obj3.pty2.pty1.pty0
print(pty)
print(obj3.pty2.pty1.func1())

十九、魔术方法与魔术属性

定义:在Python中,所有以“__”双下划线包起来的方法,都统称为“Magic Method”(魔术方法)

1、__del__析构方法

触发时机:当对象被内存回收的时候自动触发

功能:对象使用完毕后资源回收

参数:一个self接收对象

返回值:无

自动触发的两种情况:

​ (1)页面执行完毕回收所有变量

​ (2)所有对象执行完毕回收所有变量

​ 注:没有任何变量指向或引用,这个值才是真正的被释放了

class LangDog():
    food = "吃肉"
    def __init__(self, name):
        self.name = name
    def __del__(self):
        print("析构方法被触发")
# (1)第一种情况页面执行完毕,回受所有变量
obj = LangDog("lazy")
print(obj.name)
# (2)第二种情况所有对象被del的时候
other_obj = obj
print(other_obj is obj)
print("start")
del obj
del other_obj
print("end")

# 模拟文件操作
import os
class ReadFile:
    def __new__(cls, filename):
        if os.path.exists(filename):
            return object.__new__(cls)
        else:
            return print("该文件不存在")

    def __init__(self, filename):
        self.fp = open(filename, mode='r', encoding="utf-8")

    def readcontent(self):
        content = self.fp.read()
        return content

    def __del__(self):
        self.fp.close()

obj = ReadFile("shopping.json")
ret = obj.readcontent()
print(ret)

2、__str__魔术方法

触发时机:使用print(对象)或者str(对象)的时候触发

功能:查看对象

参数:一个self接收当前对象

返回值:必须返回字符串类型

如果想使用repr(对象)触发__str__的魔术方法,在类中加入__repr__ = __str__

class Cat:
    gift = "传说中猫有九条命,喜欢卖萌和上树"

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

    def __str__(self):
        return self.cat_info()

    def cat_info(self):
        return "{}小猫有故事-{}".format(self.name, self.gift)

# 第一种方式
tom = Cat("Tom")
print(tom)
# 第二种方式
ret = str(tom)
print(ret)

# 如果想使用repr(对象)触发__str__的魔术方法
class Cat:
    gift = "传说中猫有九条命,喜欢卖萌和上树"

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

    def __str__(self):
        return self.cat_info()

    def cat_info(self):
        return "{}小猫有故事-{}".format(self.name, self.gift)

    __repr__ = __str__

tom = Cat("Tom")
print(tom)
ret = repr(tom)
print(ret)

3、__repr__魔术方法

触发时机:使用repr(对象)或ptrint(对象)的时候触发

功能:查看对象,与魔术方法__str__类似

参数:一个self接收当前对象

返回值:必须返回字符串类型

注意点:底层存在赋值调用给str的语法,所以能实现打印或者str强转对象的触发机制.

注:默认情况下repr可以使用str(对象)触发,但是反过来不行,如若str中未声明,str是不能被repr(对象触发的)

class Mouse:
    gift = "打洞"

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

    def __repr__(self):
        return self.mouse_info()

    def mouse_info(self):
        return "{}老鼠天赋是{},终极技能。".format(self.name, self.gift)


obj = Mouse("杰瑞")
ret = repr(obj)
print(ret)

ret = str(obj)
print(ret)

4、__call__魔术方法

触发时机:把对象当作函数调用的时候自动触发

功能:模拟函数化操作

参数:参数不固定,至少一个self参数

返回值:看需求

# 基本用法
class MyClass:
    a = 1
    def __call__(self):
        print("call魔术方法被触发")

obj = MyClass()
obj()

# 模拟洗衣服的过程
class Wash:
    def __call__(self, something):
        self.step1(something)
        self.step2()
        self.step3()

    def step1(self, something):
        print("脱衣服,洗{}".format(something))

    def step2(self):
        print("放水里,扔点洗衣液")

    def step3(self):
        print("扭干净,穿上")

obj = Wash()
obj("T-shirt")

# 自定制myint方法模拟内置的int
import math
class MyInt:
    def mycalc(self, num, sign=1):
        # 去掉左边多余的0
        strvar = num.lstrip("0")
        if strvar == "":
            return 0
        # 计算最终的结果
        return eval(strvar) * sign

    def __call__(self, num):
        # 判断是布尔类型
        if isinstance(num, bool):
            if num == True:
                return 1
            else:
                return 0
        # 判断是整型
        elif isinstance(num, int):
            return num
        # 判断是浮点型
        elif isinstance(num, float):
            return math.floor(num) if num >= 0 else math.ceil(num)

        elif isinstance(num, str):
            # 首字符是+或者- 后边的是纯数字字符串
            if (num[0] == "+" or num[0] == "-") and num[1:].isdecimal():
                if num[0] == "+":
                    sign = 1
                else:
                    sign = -1
                return self.mycalc(num[1:], sign)
            elif num.isdecimal():
                return self.mycalc(num)
            else:
                return "这个不能转~"

myint = MyInt()
print(myint(True))
print(myint(-3.2))
print(myint("0001"))

5、__bool__魔术方法

触发时机:使用bool(对象)的时候自动触发

功能:强转对象

参数:一个self接收当前对象

返回值:必须是布尔类型

类似的魔术方法还有

​ __complex__ 被complex强转对象时调用

​ __int__ 被int强转对象时调用

​ __float__ 被float强转对象时调用

class MyClass():
    def __bool__(self):
        return True

obj = MyClass()
ret = bool(obj)
print(ret)

6、__add__魔术方法

与其相关的方法__radd__反向加法

触发时机:使用对象进行运算相加的时候自动触发

功能:对象运算

参数:二个对象参数

返回值:运算后的值

类似的魔术方法还有

​ __sub__(self, other) 定义减法的行为:-

​ __mul__(self, other) 定义乘法的行为:*

​ __float__(self, other) 定义真除法的行为:/

class MyAdd:
    def __init__(self, num):
        self.num = num
    # 对象在加号+左侧的时,自动触发
    def __add__(self, other):
        return self.num + other
    # 对象在加号+右侧的时候,自动触发
    def __radd__(self, other):
        return self.num + other * 2

a = MyAdd(7)
ret = a + 7
print(ret)
ret = 7 + a
print(ret)
# 注意两个对象相加操作的时候
b = MyAdd(8)
ret = a+b
print(ret)

7、__len__魔术方法

触发时机:使用len(对象)的时候自动触发

功能:用于检测对象中或者类中成员的个数

参数:一个self接收当前对象

返回值:必须返回整数

类似的魔术方法还有

​ __iter__(self) 定义迭代容器中的元素行为

​ __reversed__(self) 定义当被reversed() 调用时的行为(反转)

​ __contains__(self, time) 定义当使用成员测试运算符(in或not in)时的行为

# 计算成员个数
class MyClass:
    pty1 = 1
    pty2 = 2
    __pty3 = 3

    def func1():
        pass
    def func2():
        pass
    def __func3():
        pass
    def __len__(self):
        # print(MyClass.__dict__)
        # lst = []
        # for i in MyClass.__dict__:
        #     print(i)
        #     if not(i.startswith("__") and i.endswith("__")):
        #         lst.append(i)
        # print(lst)
        # 简单方法
        lst = [i for i in MyClass.__dict__ if not (i.startswith("__") and i.endswith("__"))]
        return len(lst)

obj = MyClass()
print(len(obj))

8、与类相关的魔术属性

属性和反射

__dict__ 获取对象或类的内部成员结构

__doc__ 获取对象或类的内部文档

__name__ 获取类名函数名,当在本类中执行该文件 __name__ == "__main__",不在本类中就返回所在文件的文件名

__class__ 获取当前对象所属类

__bases__ 获取一个类直接继承的所有父类,返回元组;当获取的类只继承object的时候(只有一组空圆括号或什么都没有或只有object)元组中只有object;当继承的类中无object而是其它类的时候,返回的元组中没有object类

class Man:
    pass

class Woman:
    pass

class Children(Man, Woman):
    eye = "血轮眼"
    def skylight(self):
        print("下生就能使用天照")
    def moonread(self, func):
        print("下生就能使用月渎")
        print(func.__name__, type(func.__name__))
    def __makebaby(self):
        print("私有必杀技")

obj = Children()
# __dict__
print(obj.__dict__)
print(Children.__dict__)

# __doc__
print(obj.__doc__)
print(Children.__doc__)

# __class__
print(obj.__class__)

# __bases__
print(Children.__bases__)

9、反射

反射针对的是类、对象和模块

反射:通过字符串去操作类对象或者模块当中的成员(属性方法)

hasattr、getattr、setattr、delattr

(1)反射类、对象中的成员

​ hasattr:判断类或对象中是否存在指定的成员

​ 注:判定的必须是字符串形式的成员

​ getattr:获取对象/类成员的值

​ 注:通过类反射出来的是一个普通方法;通过对象反射出来的是绑定方法;

​ 当类对象中的成员不存在时,可以设置默认值(第三个参数是默认值参数)

​ setattr:设置对象/类成员的值

​ delattr:删除对象/类成员的值

(2)反射模块中的成员

​ 四个方法使用方式是和上面一样的

​ sys.moudles 返回一个系统的字典,加载系统模块展现出来

# (1)反射类、对象中的成员
class Man:
    pass

class Woman:
    pass

class Children(Man, Woman):
    eye = "red eyes"

    def skylight(self):
        print("出生就会天照")

    def moonread(self, func):
        print("出生就会月读")

    def __makebaby(self):
        print("私有秘密绝招")

# hasattr
# 对象
obj = Children()
ret = hasattr(obj, 'eye')
ret = hasattr(Children, 'eye')
print(ret)
# 类
ret = hasattr(Children, 'skylight')
# ret = hasattr(Children, '__makebaby')
print(ret)

# getattr
# 对象
func = getattr(obj, "skylight")
func()
# 类
func = getattr(Children, "skylight")
func()  # 报错:不是普通方法
# 第三种的不存在情况
func = getattr(obj, "asdf", "该成员不存在")
print(func)

# setattr
# 对象
setattr(obj, "eye", "white eye")
print(obj.eye)

# 类
setattr(Children, "eye", "white eye")
print(Children.eye)
setattr(Children, "tuck", lambda: print(1234))
Children.tuck()

# delattr
# 对象
delattr(obj, "eye")
print(obj.eye)

# 类
delattr(Children, "eye")
print(Children.eye)

# (2)反射模块中的成员
import sys
print(sys.modules)

# 获取本模块的对象
print(sys.modules["__main__"])
selfmodule = sys.modules["__main__"]

def func1():
    print("方法一")

def func2():
    print("方法二")

while True:
    strvar = input("请输入你要反射的方法")
    if hasattr(selfmodule, strvar):
        func = getattr(selfmodule, strvar)
        func()
    elif strvar.upper() == "Q":
        break
    else:
        print("没有这个方法")

廿、装饰器

装饰器:为原函数扩展新功能,用新功能去替代旧功能

作用(特点):在不改变原有代码的基础上,实现功能上的拓展

符号:@(语法糖)

核心原理:通过闭包实现

1、装饰器的八个步骤(要点):

(1)装饰器的基本用法

def kuozhan(func):
    def newfunc():
        print("倒水")
        func()
        print("放洗衣液泡,然后洗")
    return newfunc

def func():
    print("放衣服")

func = kuozhan(func)
func()

(2)@符号的使用

def kouzhan(func):
    def newfunc():
        print("起床")
        func()
        print("听闻哥赋诗一首")
    return newfunc

@kouzhan
def func():
    print("洗脸,刷牙")

func()

(3)装饰器的嵌套

​ 注:嵌套时要注意,由近及远,由内到外执行装饰器函数

def kouzhan1(func):
    def newfunc():
        print("吃饭")
        func()
        print("上课")
    return newfunc

def kouzhan2(func):
    def newfunc():
        print("起床")
        func()
        print("上厕所")
    return newfunc

@kouzhan2
@kouzhan1
def func():
    print("洗漱")

(4)用装饰器修饰带有参数的函数

​ 注:扩展的新功能和原函数的功能,在参数和返回值上要保持一致

def kouzhan(func):
    def newfunc(who, where):
        print("起床")
        func(who, where)
        print("精神百倍")
    return newfunc

@kouzhan
def func(who, where):
    print("{who}在{where}解手".format(who=who, where=where))

func("隔壁老王", "WC")

(5)用装饰器修饰带有参数返回值的函数

​ 注:扩展的新功能和原函数的功能,在参数和返回值上要保持一致

def kuozhan(func):
    def newfunc(*args, **kwargs):
        print("1")
        lst = func(*args, **kwargs)
        print("2")
        return lst
    return newfunc

@kuozhan
def func(*args, **kwargs):
    dic = {"asd": "阿萨德", "zxc": "祖贤成", "qwe": "蔷薇"}
    for i in args:
        print("WC在", i)
    return [dic[k] + "留下" + v + "黄金" for k, v in kwargs.items()]

ret = func("东边", asd="18g", zxc="18kg", qwe="18T")
print(ret)

(6)用类装饰器来拓展原函数

​ 有两种方法:一种是通过类.函数实现;另一种是通过触发call魔术方法实现

class Kuozhan:
    def __call__(self, func):
        return self.kuozhan2(func)

    def kuozhan1(func):
        def newfunc():
            print("吃饭")
            func()
            print("睡觉")
        return newfunc

    def kuozhan2(self, func):
        def newfunc():
            print("打游戏")
            func()
            print("睡觉")
        return newfunc

# 方法一
@Kuozhan.kuozhan1
def func():
    print("码砖进行时")
func()

# 方法二
@Kuozhan()
def func():
    print("码砖进行时")
func()

(7)带有参数的函数装饰器

def outer(num):
    def kuozhan(func):
        def newfunc1(self):
            print("洗涮1")
            ret = func(self)
            print("睡觉3")
            return ret
        def newfunc2(self):
            print("洗涮4")
            ret = func(self)
            print("睡觉6")
            return ret

        if num == 1:
            return newfunc1
        elif num == 2:
            return newfunc2
        elif num == 3:
            return "匆匆那年"
    return kuozhan

class MyClass:
    @outer(1)
    def func1(self):
        print("2")
    @outer(2)
    def func2(self):
        print("5")
    @outer(3)
    def func3():
        print("那样美丽的谣言")

obj = MyClass()
obj.func1()
obj.func2()
print(obj.func3)

(8)带有参数的类装饰器

class Kuozhan:
    ad = "水中贵族百岁山"
    def __init__(self, num):
        self.num = num
    def __call__(self,cls):
        if self.num == 1:
            return self.newfunc1(cls)
        elif self.num == 2:
            return self.newfunc2(cls)
    def money(self):
        print("就是小贵")

    def newfunc1(self, cls):
        def newfunc():
            cls.ad = Kuozhan.ad
            cls.money = Kuozhan.money
            return cls()
        return newfunc

    def newfunc2(self, cls):
        def newfunc():
            if "run" in cls.__dict__:
                ret = cls.run()
                cls.run = ret
                return cls()
        return newfunc
obj = Kuozhan(1)

# 情况一
@Kuozhan(1)
class MyClass:
    def run():
        return "亢龙有悔"
obj = MyClass()
print(obj.ad)
obj.money()

# 情况二
@Kuozhan(2)
class MyClass:
    def run():
        return "茅中贵族,百岁山"

obj = MyClass()
print(obj.run)

2、面向对象中的方法

普通方法:可以有参数或者无参数,当成正常函数调用

​ 普通方法(无参只能用类调用)

绑定方法(classmethod):(1)绑定到对象(自动传递参数为对象)(2)绑定到类(自动传递参数为类)

​ 绑定方法(一般用对象调用,类也能调用);

静态方法(staticmethod):无论是对象还是类,都可以调用,不会默认传递任何参数,如果有参数当成普通方法调用即可

class Dog:
    name = "旺财"
    # 普通方法
    def jiao():
        print("小狗汪汪叫")
    # 绑定方法(对象)
    def eat(self):
        print("小狗喜欢吃骨头")
    # 绑定方法(类)
    @classmethod
    def tail(cls):
        print(cls)
        print("小狗看到主人喜欢摇尾巴")
    # 静态方法
    @staticmethod
    def jump(num):
        print("小狗喜欢接飞盘")

obj = Dog()
# 普通方法
Dog.jiao()
# 绑定方法(对象)
obj.eat()
Dog.eat(1234)
# 绑定方法(类)
Dog.tail()
obj.tail()
# 静态方法
obj.jump(1)
Dog.jump(2)
# 在类外,为对象添加成员方法,默认都是静态方法
obj.func = lambda: print(1234)
obj.func()

3、property装饰器

property 可以把方法变成属性使用

作用:控制属性的获取,设置,删除等操作

变相的增加成员的安全性,可以通过自定义逻辑对成员进行控制

自动触发:要求:是同一个名字

注:可以通过pass对property进行权限限制,使用pass代表不会执行该操作

# 使用方法一
class MyClass:
    def __init__(self, name):
        self.name = name

    @property
    def username(self):
        # pass
        return self.name

    @username.setter
    def username(self, val):
        # pass
        print(112233)
        val = "阿萨德"
        self.name = val

    @username.deleter
    def username(self):
        # pass
        del self.name

# 获取属性 (自动触发获取方法 @property)
obj = MyClass("许多")
print(obj.username)

# 设置属性 (自动触发设置方法) val形参自动接收设置的值
obj.username = "阿萨"
print(obj.username)

# 删除属性
del obj.username
print(obj.username) # error

# 使用方法二
class MyClass:
    def __init__(self, name):
        self.name = name
	# 获取
    def get_username(self):
        return self.name
	# 设置
    def set_username(self, val):
        self.name = val
	# 删除
    def del_username(self):
        del self.name

    useranme = property(get_username, set_username, del_username)
# 获取
obj = MyClass("朴飘乐")
print(obj.useranme)
# 设置
obj.useranme = "朴一生"
print(obj.useranme)
# 删除
del obj.useranme
print(obj.useranme)

廿一、异常处理

注:语法报错无法抑制(无法抛出异常)

1、异常处理(30)

IndexError:索引超出序列的范围

KeyError:字典中查找一个不存在的关键字

NameError:尝试访问一个不存在的变量

IndentationError:缩进错误

AttributeError:尝试访问未知的对象属性s

Stoplteration:迭代器没有更多的值

AssertionError:断言语句(assert)失败 [测试领域]

​ assert断言,猜后面的表达式是否正确,如果猜对了,什么反应都没有,猜错了,直接报错

​ if和assert的区别:if判定时选择执行或不执行;assert判定时选择报错或不报错

EOFError:用户输入文件末尾标志EOF(Ctrl+d)

FloatingPointError:浮点计算错误

GeneratorExit:generator.close()方法被调用的时候

ImportError:导入模块失败的时候

KeyboardInterrupt:用户输入中断键(Ctrl+c)

MemoryError:内存溢出(可通过删除对象释放内存)

NotImplementedError:尚未实现的方法

OSError:操作系统产生的异常(例如打开一个不存在的文件)

OverflowError:数值运算超出最大限制

ReferenceError:弱引用(weak reference)试图访问一个已经被垃圾回收机制回收了的对象

RuntimeError:一般的运行时错误

SyntaxError:Python的语法错误

TabError:Tab和空格混合使用

SystemError:Python编译器系统错误

SystemExit:Python编译器进程被关闭

TypeError:不同类型间的无效操作

UnboundLocalError:访问一个未初始化的本地变量(NameError的子类)

UnicodeError:Unicode相关的错误(ValueError的子类)

UnicodeEncodeError:Unicode编码时的错误(UnicodeError的子类)

UnicodeDecodeError:Unicode解码时的错误(UnicodeError的子类)

UnicodeTranslateError:Unicode转换时的错误(UnicodeError的子类)

ValueError:传入无效的参数

ZeroDivisionError:除数为零

# IndexError
lst = [1, 2, 3]
print(lst[100])

# KeyError
dic = {"a": 1, "b": 2}
print(dic["c"])

# NameError
print(asd)

# IndentationError
if 5 == 5:
    print(1)
   print(2)

# AttributeError
class MyClass:
    a = 100
obj = MyClass
obj.abc

# Stoplteration
it = iter(range(2))
ret = next(it)
ret = next(it)
ret = next(it)

# AssertionError
assert 3 < 4
assert 3 > 4

if 3 < 4 :
    pass

2、异常处理的基本语法

(1)普通异常处理:

​ try ... except ...

​ 把有问题的代码放在try这个代码块中,如果出现了异常,直接执行except代码块

​ 作用:防止异常错误,终止程序

(2)带有分支的异常处理:

​ except + 异常错误类

​ 特指在发生这类异常错误时,要执行的分支

(3)处理迭代器的异常错误

​ StopIteration是异常错误类

​ StopIteration as e 给 StopIteration这个类的对象起一个别名叫做e

(4)异常处理的其它方法

​ try ... finally ... 不论代码是否报错,都必须要执行的代码放到finally中,finally中的代码块在报错误后会继续执行

​ try ... except ... else ... 如果try代码块没有报错,就执行else这个分支,如果有报错就不执行这个分支

(5)额外for/while ... else 如果遇到break异常终止了循环,不会执行else这个代码块

# (1)
try:
    lst = [1, 2, 3, 4]
    print(lst[100])
except:
    pass
    # print("This is an error")

# (2)
try:
    lst = [1, 2, 3, 4]
    print(lst[100])
    dic = {"a": 1, "b": 2}
    dic['ccc']
    print(ccc)
except IndexError:
    print("索引超出范围了")
except KeyError:
    print("字典的键不存在")
except:
    print("有异常错误")

# (3)
def mygen():
    yield 1
    yield 2
    return "pop 94 me"

gen = mygen()

try:
    ret = next(gen)
    ret = next(gen)
    ret = next(gen)
except StopIteration as e:
    print("迭代器取值错误,越界了")
    print(e)

# (4)
# try ... finally ... 
try:
    lst = [1, 2, 3, 4]
    print(lst[100])
finally:
    print(2)
    print(3)
    print("end结束")

print(666)
print(888)

# try ... except ... else ...
try:
    print(1)
    lst = [1, 2, 3, 4]
    # print(lst[100])
except:
    pass
else:
    print("正常执行结束")

# (5)
for i in range(10):
    print(i)
    if i == 5:
        break
else:
    print("循环结束")

3、主动异常抛出

(1)基本语法:

​ raise + 异常错误类 or 异常错误对象(默认不写raise接收的是BaseException)

​ BaseException 所有异常类的父类(基类,超类)(子类,衍生类)

​ Exception 常规异常类的父类

(2)自定义异常MyException:务必继承父类 BaseException

​ 系统的底层获取行数和文件名的函数(只有在程序异常时才能触发)

(3)一旦执行了raise语句,raise后面的语句将不能执行

# (1)
try:
    raise BaseException
except BaseException:
    pass

try:
    raise
except:
    print(11)

#(2)
#(了解)系统底层获取行数和文件名的函数( 只有在程序异常时才能触发 )
def return_errorinfo(n):
    import sys
    f = sys.exc_info()[2].tb_frame.f_back
    if n == 1:
        return str(f.f_lineno)  # 返回当前行数
    elif n == 2:
        return f.f_code.co_filename  # 返回文件名

# 只有在抛出错误的时候,里面的行号和文件名才能获取到
def get_value(n):
    try:
        raise
    except:
        return_errorinfo(n)

class MyException(BaseException):
    def __init__(self, num, msg, line, file):
        # 错误号
        self.num = num
        # 错误信息
        self.msg = msg
        # 错误行号
        self.line = line
        # 错误文件
        self.file = file

sex = "雌雄同体"
try:
    if sex == "雌雄同体":
        raise MyException(404, "人类没有雌雄同体", get_value(1), get_value(2))
except MyException as e:
    print(e.num)
    print(e.msg)
    print(e.line)
    print(e.file)

廿二、进程

进程是正在运行的程序,他是操作系统中,资源分配的最小单位

资源分配:分配的是cup和内存等物理资源

注:进程号是进程的唯一标识;同一个程序执行两次后是两个进程

进程和进程之间是彼此隔离的,通过socket通信

1、并行和并发

并发:一个cpu同一时间不停执行多个程序

并行:多个cpu同一时间不停执行多个程序

2、cpu的进程调度方法

先来先服务fcfs(fist come first server):先来的先执行

短作业优先算法:分配的cpu多,先把短的算完

时间片轮转算法:每一个任务就执行一个时间片的时间,然后就执行其他的

多级反馈队列算法

越是时间长的,cpu分配的资源越少,优先级靠后

越是时间短的,cpu分配的资源越多

3、进程三状态

(1)就绪(Ready)状态

​ 只剩下CPU需要执行外,其它所有资源都已分配完毕称为就绪态

(2)执行(Running)状态

​ cpu开始执行该进程时称为执行状态

(3)阻塞(Blocked)状态

​ 由于等待某个事件发生而无法执行时,便是阻塞状态,cpu执行其他进程。

​ 例如:等待I/O完成input、申请缓冲区不能满足等等

4、同步 异步/阻塞 非阻塞

场景在多任务中

同步:必须等我这件事干完了,你在干,同一时间只有一条主线,就是同步

异步:没等我这件事情干完,你就在干了,有两条主线,就是异步

阻塞:像input或time.sleep(),就是阻塞,必须要输入一个字符串或等待一段时间,否则代码不往下执行

非阻塞:没有任何等待,正常代码执行

同步阻塞:效率低,cup利用不充分

异步阻塞:比如socketserver,可以同时连接多个,但是彼此都有recv

同步非阻塞:没有类似input或time.slee()的代码,从上到下执行,默认的正常情况代码

异步非阻塞:效率最高,cup过度充分,过度发热

(1) 进程使用的基本语法(无参)

​ process 创建子进程,返回进程的对象p,
​ target指定要执行的任务
​ args指定传递的参数,args的类型是元组,多个参数之间用逗号隔开

# 无参
from multiprocessing import Process
def func():
    print("1.子进程id:{} 2.父进程id:{}".format(os.getpid(), os.getppid()))

if __name__ == "__main__":
    print("3.子进程id:{} 4.父进程id:{}".format(os.getpid(), os.getppid()))
    p = Process(target=func)
    p.start()

(2)创建带有参数的进程

# 有参
def func(n):
    for i in range(1, n+1):
        print("3.子进程id:{},4.父进程id:{}".format(os.getpid(), os.getppid()))

if __name__ == "__main__":
    print("1.子进程id:{},2.父进程:{}".format(os.getpid(), os.getppid()))
    n = 5
    p = Process(target=func, args=(n,))
    p.start()

    for i in range(1, n+1):
        print("*" * i)

(3)进程之间的数据隔离(进程和进程之间是相互独立的)

count = 100
def func():
    global count
    count += 1
    print("我是子进程count={}".format(count))

if __name__ == "__main__":
    p = Process(target=func)
    p.start()
    time.sleep(1)
    print(count)

(4)多个进程可以异步并发,子父进程之间的关系

  • 程序在异步并发任务时,因为cpu调度策略问题,不一定先执行谁或者后执行谁;整体而言,主进程速度快于子进程,cpu遇到阻塞立刻切换其它任务,等到进程的就绪状态在切回来

  • 主程序会默认等待所有的子进程执行结束后,再关闭程序,释放资源;若不等待,子程序并不方便管理,容易造成僵尸进程,在后台不停的占用系统的资源(cpu和内存),不清楚来源

def func(args):
    print("3子进程id:{},4父进程id:{}".format(os.getpid(), os.getppid()), args)

if __name__ == "__main__":
    for i in range(1, 11):
        Process(target=func, args=(i,)).start()
    print("主进程执行结束。。。")

(5)join的使用

​ 必须等待所有的子进程全部执行完毕之后,主进程任务在继续执行(用来同步子父进程的速度)

# (1) join 的基础语法
def func():
    print("发第一封邮件")

if __name__ == "__main__" :
    p = Process(target=func)
    p.start()
    p.join()
    print("发第二封邮件")
    
# (2) 多个子进程配合join使用
def func(index):
    print("发送第%s封邮件" % (index))

if __name__ == "__main__":
    lst = []
    for i in range(10):
        p = Process(target=func, args=(i,))
        p.start()
        lst.append(p)
    for i in lst:
        print(i)
        i.join()
    print("主进程最后一封邮件")

(6)使用自定义方式创建进程

​ 自定义类创建进程要求:

  • 必须继承Process这个父类

  • 所有进程执行任务的逻辑要写run方法里面

# (1) 基本语法
class MyProcess(Process):
    def run(self):
        print('1子进程id:{},2父进程id:{}'.format(os.getpid(), os.getppid()))

if __name__ == "__main__":
    p = MyProcess()
    p.start()
    print("3子进程id:{},4父进程id:{}".format(os.getpid(), os.getppid()))
    
# (2) 有参数的进程函数
class MyProcess(Process):
    def __init__(self, arg):
        # 手动调用一下父类的构造方法(实现进程的创建)
        super().__init__()
        self.arg = arg
    def run(self):
        print("1子进程id:{},2父进程id:{}".format(os.getpid(), os.getppid()))

if __name__ == "__main__":
    p = MyProcess("我是参数")
    p.start()
    print('3子进程id:{},4父进程id:{}'.format(os.getpid(), os.getppid()))

5、守护进程

可以给子进程贴上守护进程的名字,该进程会随着主进程代码执行完毕而结束(为主进程守护)

(1)守护进程会在主进程代码执行结束后终止

(2)守护进程内无法在开启子进程,否则抛出异常

守护进程守护的是主进程

通过进程对象.daemon = True 设置当前进程为守护进程

注:必须写在start()调用之前

默认情况下,主进程会等待所有子进程执行完毕后,关闭程序释放资源

守护进程在主进程代码执行结束后,直接杀掉

from multiprocessing import Process
# (1)基本用法
def func():
    print("start 当前子进程")
    print("end   当前子进程")

if __name__ == "__main__":
    p = Process(target=func)
    p.daemon = True
    p.start()
    print("主进程执行结束 ... ")

# (2)多个子进程的场景
import time
def func1():
    count = 1
    while True:
        print("*" * count)
        time.sleep(0.5)
        count += 1
    
def func2():
    print("start func2当前子进程")
    time.sleep(2)
    print("end   func2当前子进程")
    

if __name__ == "__main__":
    p1 = Process(target=func1)
    p2 = Process(target=func2)
    
    # 设置当前进程为守护进程
    p1.daemon = True
    
    p1.start()
    p2.start()
    
    time.sleep(1)
    
    # 非守护进程,主进程还是会默认等待的.
    print("主程序代码结束....")

# (3)守护进程的实际用途:监控报活
import time
# 监控报活
def alive():
    while True:
        print("给监控服务器发消息, 当前5号服务器功能正常 i am ok ~")
        time.sleep(1)

# 当前服务器正常完成的功能
def func():
    # while True:
    time.sleep(5)
    print("当前5号服务器功能,统计财务报表~")


if __name__ == "__main__":
    p1 = Process(target=func)
    p2 = Process(target=alive)
    p2.daemon = True
    
    p1.start()
    p2.start()
    
    # 等待p1子进程执行结束之后,下面的主程序的代码才会放行;
    p1.join()
    
    print("当前服务器状态:统计财务报表功能异常.....")

6、锁(Lock)

(1)进程锁:Lock

​ lock.acquire():上锁

​ lock.release():解锁

​ 调用方式:from multiprocessing import Process

​ 注意点:

​ 同一时间只允许一个进程上一把锁(Lock锁的特点)

​ 优点:加锁可以保证多个进程修改同一数据时,同一时间只能由一个任务执行修改,

​ 即串行修改,牺牲运行速度来保证数据安全

互斥锁Lock:互斥锁就是进程间互相排斥,谁先抢到资源,谁就先上锁改资源内容,保证数据的同步性

''' (1)基本使用 '''
# 创建一把锁
lock = Lock()
# 上锁
lock.acquire()
# 执行操作
# 解锁
lock.release()
# lock.acquire() 死锁阻塞.
print("执行程序 .... ")

''' (2) 模拟12306 抢票软件 '''

# 读取票数,更新票数
def wr_info(sign,dic=None):
	if sign == "r":
		with open("ticket",mode="r",encoding="utf-8") as fp:
			dic = json.load(fp)
		return dic
	elif sign == "w":
		with open("ticket",mode="w",encoding="utf-8") as fp:
			json.dump(dic,fp)
			
			
# 抢票方法
def get_ticket(person):
	# 获取数据库中实际数据
	dic = wr_info("r")

	# 模拟网络延迟
	time.sleep(0.5)	
	
	if dic["count"] > 0:
		print("%s抢到票了" % (person))
		dic["count"] -= 1
		# 更新数据库
		wr_info("w",dic)
	else:
		print("%s没有抢到这张票" % (person))
	

def run(person,lock):
	# 读取数据库中的实际票数
	dic = wr_info("r")
	print("%s 查询票数 : %s" % (person,dic["count"]))	


	# 上锁
	lock.acquire()
	# 抢票
	get_ticket(person)
	# 解锁
	lock.release()
	
if __name__ == "__main__":
	lock = Lock()
	lst =["刘思敏7","云超1","张恒2","尉翼麟3","王振4","黎建忠5","刘鑫炜6","李天兆8","魏小林9","李博10"]
	for i in lst:
		p = Process(target=run,args=(i,lock))
		p.start()

(2)信号量(Samephore)

​ lock.acquire():上锁

​ lock.release():解锁

​ 调用方式:from multiprocessing import Samephore

​ 注意点:

​ 多个锁一起上,不开锁,会造成死锁,上锁和解锁是一对

​ 优点:

​ Semaphore可以设置上锁数量;即同一时间允许多个进程上锁;

​ 创建进程的时候是异步的;执行任务的时候是同步的

from multiprocessing import Semaphore,Process
import time,random

def ktv(person,sem):
	sem.acquire()
	# 开始唱歌
	print("%s进入ktv,正在唱歌" % (person) )
	time.sleep(random.randrange(3,7)) # 3 4 5 6 
	print("%s离开ktv,唱完了" % (person) )
	sem.release()
	
if __name__ == "__main__":
	sem = Semaphore(4)
	for i in range(10):
		p = Process(target=ktv,args=("person%s" % (i),  sem))
		p.start()

7、事件(Event)

(1)阻塞事件

​ e = Event():生成事件对象

​ e.wait():动态给程序加阻塞,程序当中是否加阻塞取决于该对象中的is_set()

​ is_set:默认返回False

​ True是不加阻塞;False就是加阻塞

(2)控制属性的值

​ set()方法 将这个属性的值改成True

​ clear()方法 将这个属性的值改成False

​ is_set()方法 判断当前的属性是否为True (默认上来是False)

# (1) 基本使用
e = Event()
# 将阻塞事件中的值改成True
e.set()
print(e.is_set())
e.wait()
print("程序运行中1... ")

# 将阻塞事件中的值改成False
e.clear()
e.wait()
print("程序运行中2... ")

e = Event()
# 参数: 最多等待时间是5秒,过了5秒之后阻塞放行
e.wait()
print("程序运行中3... ")

# (2) 模拟红绿灯效果
import time,random
def traffic_light(e):
	print("红灯亮")
	while True:
		if e.is_set():
			# 绿灯状态,亮1秒钟
			time.sleep(1)
			print("红灯亮")
			# 把True => False
			e.clear()
		else:
			# 红灯状态,亮1秒钟
			time.sleep(1)
			print("绿灯亮")
			# 把False => True
			e.set()

# e = Event()
# traffic_light(e)

def car(e,i):
 
	if not e.is_set():
		# 走到这个分支里面来,一定是红灯状态,车要停
		print("car%s 在等待" % (i))
		# 加阻塞
		e.wait()
	print("car%s 通行了" % (i))

"""
if __name__ == "__main__":
	e = Event()
	p1 = Process(target=traffic_light,args=(e,))
	p1.start()
	
	# 开始创建小车
	for i in range(1,21):
		time.sleep(random.randrange(0,2)) # 0 1
		p2 = Process(target=car,args=(e,i))
		p2.start()
"""

# (3) 改造红绿灯 (在跑完小车之后,把红绿灯给我炸了)
if __name__ == "__main__":
	lst = []
	e = Event()
	p1 = Process(target=traffic_light,args=(e,))
	# 把红绿灯变成守护进程
	p1.daemon = True
	p1.start()

	# 开始创建小车
	for i in range(1,21):
		time.sleep(random.randrange(0,2)) # 0 1
		p2 = Process(target=car,args=(e,i))
		p2.start()
		lst.append(p2)
		
	# 让所有的小车都通过之后,在终止交通灯;
	for i in lst:
		i.join()

	print("程序结束 ... ")

8、进程间通信(IPC)

IPC:IPC Inter-Process Communication

(1)实现进程之间通信的两种机制:

​ 管道 Pipe

​ 队列 Queue

(2)队列使用方式:from multiprocessing import Queue

​ put():存放

​ get():获取

​ get_nowait():拿不到报异常

​ 注:存在兼容性问题(windows好用 linux不好用 不推荐使用)

​ put_nowait():非阻塞版本的put

​ q.empty() 检测是否为空

​ q.full() 检测是否已经存满

''' (1) 基本使用 '''
q = Queue()
# 1.put 往队列中存值
q.put(111)
q.put(222)
q.put(333)
# 2.get 从队列中取值
res = q.get()
print(res)
res = q.get()
print(res)
res = q.get()
print(res)
# 3.队列里面没数据了,在调用get会发生阻塞
res = q.get()
print(res)

''' (2) 可以限定Queue队列的长度 '''
q1 = Queue(3)
q1.put(1)
q1.put(2)
q1.put(3)
# 超出了队列的长度,会发生阻塞
# q1.put(4)
# 如果列表满了,还往里面添加数据会直接报错.
q1.put_nowait(4)

''' (3)进程之间通过队列交换数据 '''
def func(q2):
	# 2.子进程取数据
	res = q2.get()
	print(res)
	# 3.子进程存数据
	q2.put("123")
	
	
if __name__ == "__main__":
	q2 = Queue()
	p = Process(target=func,args=(q2,))
	p.start()
	
	# 1.主进程添加数据
	q2.put("456")
	
	# 为了等待子进程把数据塞到队列中,在获取,要加一个join
	p.join()
	
	# 2.主进程获取数据
	res = q2.get()
	print("主程序执行结束:值为{}".format(res))

9、生产者与消费者

生产者 -----> 内存缓冲区 -----> 消费者

爬虫例子:

​ 1号进程负责抓取页面中的内容放到队列里
​ 2号进程负责把内容取出来,配合正则表达式,扣取关键字

​ 1号进程可以理解成生产者
​ 2号进程可以理解成消费者

​ 相对理想的生产者和消费者模型:
​ 追求彼此的速度相对均匀

从程序上来说:
生产者负责存储数据(put)
消费者负责获取数据(get)

(1)Queue

# (1)基本模型
from multiprocessing import Process,Queue
import random,time

# 消费者模型
def consumer(q,name):
	while True:
		food = q.get()
		time.sleep(random.uniform(0.1,1))
		print("%s 吃了一个%s" % (name,food))
	
	
# 生产者模型
def producer(q,name,food):
	for i in range(5):
		time.sleep(random.uniform(0.1,1))
		print("%s 生产了 %s%s" % (name,food,i))
		q.put(food+str(i))
	
	
if __name__ == "__main__":
	q = Queue()
	# 消费者1
	p1 = Process(target=consumer,args=(q,"张恒"))
	p1.start()

	# 生产者1
	p2 = Process(target=producer,args=(q,"尉翼麟","黄金"))
	p2.start()	

	
# (2)优化模型

# 消费者模型
def consumer(q,name):
	while True:
		food = q.get()
		if food is None:
			break
		time.sleep(random.uniform(0.1,1))
		print("%s 吃了一个%s" % (name,food))
	
	
# 生产者模型
def producer(q,name,food):
	for i in range(5):
		time.sleep(random.uniform(0.1,1))
		print("%s 生产了 %s%s" % (name,food,i))
		q.put(food+str(i))
	
	
if __name__ == "__main__":
	q = Queue()
	# 消费者1
	p1 = Process(target=consumer,args=(q,"张恒"))
	p1.start()
	# 消费者2
	a2 = Process(target=consumer,args=(q,"云超"))
	a2.start()
	
	# 生产者1
	p2 = Process(target=producer,args=(q,"尉翼麟","黄金"))
	p2.start()
	
	# 生产者2
	b2 = Process(target=producer,args=(q,"刘思敏","钻石"))
	b2.start()
	
	# 在生产完所有的数据之后,在队列的末尾塞入一个None
	p2.join()
	b2.join()
	# 消费者模型如果获取的是None,代表停止消费
	q.put(None)
	q.put(None)

(2)JoinableQueue

​ put 存储
​ get 获取
​ task_done 队列计数减1
​ join 阻塞

​ 使用方式:from multiprocessing import JoinableQueue

​ task_done 配合 join 一起使用
​ put 一次 每存放一个值,队列计数器加1
​ get 一次 通过task_done让队列计数器减1
​ join 函数,会根据队列中的计数器来判定是阻塞还是放行
​ 如果计数器变量是0,意味着放行,其他情况阻塞;

# (1) 基本使用
jq = JoinableQueue()
# put 会让队列计数器+1
jq.put("a")
print(jq.get())
# 通过task_done,让队列计数器-1
jq.task_done()
# 只有队列计数器是0的时,才会放行
jq.join() # 队列.join
print("finish")

# (2) 改造生产者消费者模型
import random,time

# 消费者模型
def consumer(q,name):
	while True:
		food = q.get()		
		time.sleep(random.uniform(0.1,1))
		print("%s 吃了一个%s" % (name,food))
		q.task_done()
	
# 生产者模型
def producer(q,name,food):
	for i in range(5):
		time.sleep(random.uniform(0.1,1))
		print("%s 生产了 %s%s" % (name,food,i))
		q.put(food+str(i))
	
	
if __name__ == "__main__":
	q = JoinableQueue()
	# 消费者1
	p1 = Process(target=consumer,args=(q,"张恒"))
	p1.daemon = True
	p1.start()

	# 生产者1
	p2 = Process(target=producer,args=(q,"尉翼麟","黄金"))
	p2.start()	
	
	# 把生产者所有的数据都装载到队列中
	p2.join()
	
	# 当队列计数器减到0的时候,会立刻放行
	# 必须等待消费者模型中所有的数据都task_done之后,变成0了就代表消费结束.
	q.join()
	
	print("程序结束....")

10、数据共享

除了上面的通过Queue队列文件进行数据共享外

还可以使用Manager(list列表,dict字典)进行进程之间的共享数据

from multiprocessing import Process,Manager,Lock
def work(data,lock):
	# 1.正常写法
	"""
	lock.acquire()
	# data["count"] -= 1
	data[0] += 1
	lock.release()
	"""
	
	# 2.使用with 语法简化上锁解锁操作
	with lock:
		data[0] += 1
	
if __name__ == "__main__":
	m = Manager()
	# 创建一个共享的字典
	data = m.dict( {"count":20000} )
	# 创建一个共享的列表
	data = m.list([1,2,3])
	
	# print(data)
	
	lst = []
	lock = Lock()
	for i in range(100):
		p = Process(target=work,args=(data,lock))
		p.start()
		lst.append(p)
		
	# 确保所有的进程执行完毕,然后在向下运行,打印数据,否则报错;
	for i in lst:
		i.join()
		
	print(data) # [101, 2, 3]

廿三、线程

线程是计算机中调度的最小单位

线程的特点:线程比较轻量级,能干更多的活,一个进程中的所有线程资源是共享的

注:一个进程至少有一个线程在工作

目前配置最高的主机,5万个并发已经是上线

1、线程的基本使用

(1)一份进程资源中可以包含多个线程

(2)多线程的速度远远快于多进程

(3)多线程:共享同一份进程资源

(4)用类定义线程:继承线程父类Thread

from threading import Thread
from multiprocessing import Process
import os

# (1)
def func(num):
	print('当前线程{},所归属的进程id号{}'.format(os.getpid(),num))

for i in range(10):
	# 异步创建10个子线程
	t = Thread(target=func,args=(i,))
	t.start()

# 主线程执行任务
print(os.getpid())

# (2)
import time
def func(num):
    print('当前线程{},所归属的进程id号{}'.format(num, os.getpid()))

if __name__ == "__main__":
    starttime = time.time()
    lst = []
    for i in range(100):
        t = Thread(target=func, args=(i,))
        t.start()
        lst.append(t)
    for i in range(100):
        p = Process(target=func, args=(i,))
        p.start()
        lst.append(p)
        
    for i in lst:
        i.join()
    endtime=time.time()
    print("多线程执行时间:", endtime-starttime)

# (3)
num =1000
def func():
	global num
	num -=1

for i in range(1000):
	t=Thread(target=func)
	t.start()

print(num)

# (4)
class MyThread(Thread):
	def __init__(self,name):
		# 手动调用父类的构造方法
		super().__init__()
		self.name = name

	def run(self):
		time.sleep(1)
		print("当前进程正在执行runing ... " , self.name)
		
if __name__ == "__main__":
	t = MyThread("机器今天会再次爆炸么?")
	t.start()
	print("主线程执行结束 ... ")

2、线程的缺陷

python中的线程可以并发但不能并行;同一个进程下的多个线程不能分开被多个cpu同时执行

原因:

​ 全局解释器锁(Cpython解释器特有)GIL锁:

​ 同一时间一个进程下的多个线程只能被一个cpu执行

​ 不能执行线程的并行操作

​ python是解释型语言,不能一次性全部编译成功,不能提前规划,都是临时调度,容易造成cpu执行调度异常,所以加了一把锁叫GIL

实现并行的方法:

​ (1)用多进程间接实现线程的并行

​ (2)换一个Pypy,Jpython解释器

线程分为计算密集型和IO密集型:

​ 计算密集型会大量占用cpu资源,对于python来说负担大

​ IO密集型像网页,爬虫,OA办公,python处理起来就很方便

3、线程的相关函数

(1)is_alive():检测线程是否仍然存在

(2)setName():设置线程名

(3)getName():获取线程名

(4)currentThread().ident:查看线程id号

(5)enumerate():返回目前正在运行的线程列表

(6)activeCount():返回目前正在运行的线程数量

# 
def func():
	time.sleep(1)
if __name__ == "__main__":
	t = Thread(target=func)
	t.start()
	print(t.is_alive()) # False
	print(t.getName())
	t.setName("1234")
	print(t.getName())

# currentThread().ident
def func():
	print("子线程id",currentThread().ident  , os.getpid())

if __name__ == "__main__":
	Thread(target=func).start()
	print("主线程id",currentThread().ident , os.getpid())

# enumerate(),activeCount()
from threading import enumerate
from threading import activeCount # (了解)
def func():
	print("子线程id",currentThread().ident  , os.getpid())
	time.sleep(0.5)
	
if __name__ == "__main__":
	for i in range(10):
		Thread(target=func).start()
	lst = enumerate()
	print( activeCount() )

4、守护线程

注:等待所有线程全部执行完,才终止程序,守护的是所有线程

from threading import Thread
import time
def func1():
	while True:
		time.sleep(0.5)
		print("我是func1")
	
def func2():	
	print("我是func2 start ... ")
	time.sleep(3)
	print("我是func2 end ... ")
	
t1 = Thread(target=func1)
t2 = Thread(target=func2)

# 在start调用之前,设置守护线程
t1.setDaemon(True)

t1.start()
t2.start()

print("主线程执行结束 ... ")

5、线程锁

Lock和信号量Semaphore

保证线程数据安全

注:在创建线程的时候是异步创建,在执行的时候因为Semaphore加了锁,所以线程之间变成同步

# Lock
from threading import Lock,Thread
import time
n = 0

def func1(lock):
	global n
	
	lock.acquire()
	for i in range(1000000):
		# 方法一		
		n -= 1
	lock.release()
	
def func2(lock):
	global n
	# with 自动完成上锁+解锁
	with lock:
		for i in range(1000000):
			# 方法二				
			n += 1

if __name__ == "__main__":
	lst = []
	lock = Lock()
	
	time1 = time.time()
	for i in range(10):
		t1 = Thread(target=func1,args=(lock,))
		t2 = Thread(target=func2,args=(lock,))
		t1.start()
		t2.start()
		lst.append(t1)
		lst.append(t2)
	
	# 等待所有的子线程执行结束之后, 在打印数据
	for i in lst:
		i.join()
	time2 = time.time()
	print("主线程执行结束..." , n ,time2 - time1)

# semaphore
from threading import Semaphore,Thread
import time

def func(i,sm):
	with sm:	
		print(i)
		time.sleep(3)

if __name__ == "__main__":
	sm = Semaphore(5)
	for i in range(20):
		Thread(target=func,args=(i,sm)).start()

6、死锁,递归锁,互斥锁

死锁:加了一把锁,但是没有与解锁对应或出现了间隔又通过一把锁间隔了而没有对应的解锁,产生程序阻塞无法运行,所以产生了死锁

互斥锁:加一把锁就对应解一把锁,就是互斥锁

递归锁:递归锁专门用于解决死锁,但只是一种应急处理方法,临时用于快速解决项目因死锁问题不能正常运行的场景,用于处理异常死锁,和互斥锁一样都可以使用 with 的形式加锁。

注:不到万不得已的特殊情况不能使用锁的循环嵌套

# 死锁
noodle_lock = Lock()
kuaizi_lock = Lock()
def eat1(name):
	noodle_lock.acquire()
	print("%s 拿到面条啦!!" % (name))
	kuaizi_lock.acquire()
	print("%s 拿到筷子啦!!" % (name))
	
	print("开始享受这碗面条.. ")
	time.sleep(0.5)
	
	kuaizi_lock.release()
	print("%s 放下筷子了啊~" % (name))
	noodle_lock.release()
	print("%s 放下面条了啊!!" % (name))
	
def eat2(name):
	kuaizi_lock.acquire()
	print("%s 拿到筷子啦!!" % (name))
	noodle_lock.acquire()
	print("%s 拿到面条啦!!" % (name))
	
	print("开始享受这碗面条.. ")
	time.sleep(0.5)
	
	noodle_lock.release()
	print("%s 放下面条了啊!!" % (name))
	kuaizi_lock.release()
	print("%s 放下筷子了啊~" % (name))


if __name__ == "__main__":
	
	name_lst1 = ["王振","王赢钱"]
	name_lst2 = ["李天照","魏小林"]
	for name in name_lst1:
		Thread(target=eat1,args=(name,)).start()

	for name in name_lst2:
		Thread(target=eat2,args=(name,)).start()

# 互斥锁
mylock = Lock()
def eat1(name):
	mylock.acquire()
	print("%s 拿到面条啦!!" % (name))
	print("%s 拿到筷子啦!!" % (name))
	
	print("开始享受这碗面条.. ")
	time.sleep(0.5)	

	print("%s 放下筷子了啊~" % (name))	
	print("%s 放下面条了啊!!" % (name))
	mylock.release()
	
def eat2(name):
	mylock.acquire()
	print("%s 拿到筷子啦!!" % (name))
	print("%s 拿到面条啦!!" % (name))
	
	print("开始享受这碗面条.. ")
	time.sleep(0.5)	

	print("%s 放下面条了啊!!" % (name))
	print("%s 放下筷子了啊~" % (name))
	mylock.release()

if __name__ == "__main__":
	
	name_lst1 = ["王振","王赢钱"]
	name_lst2 = ["李天照","魏小林"]
	for name in name_lst1:
		Thread(target=eat1,args=(name,)).start()

	for name in name_lst2:
		Thread(target=eat2,args=(name,)).start()

# 递归锁
rlock = RLock() # 递归锁
lock = Lock() # 互斥锁
def func():
	"""
	死锁: 只上锁不解锁是死锁.
	lock.acquire()
	lock.acquire()
	print(333)
	lock.release()
	lock.release()
	"""
	rlock.acquire()
	rlock.acquire()
	print(111)
	rlock.release()
	rlock.release()
	print(222)
func()

# 使用递归锁解决死锁
noodle_lock = kuaizi_lock = RLock()
def eat1(name):
	noodle_lock.acquire()
	print("%s 拿到面条啦!!" % (name))
	kuaizi_lock.acquire()
	print("%s 拿到筷子啦!!" % (name))
	
	print("开始享受这碗面条.. ")
	time.sleep(0.5)
	
	kuaizi_lock.release()
	print("%s 放下筷子了啊~" % (name))
	noodle_lock.release()
	print("%s 放下面条了啊!!" % (name))
	
def eat2(name):
	kuaizi_lock.acquire()
	print("%s 拿到筷子啦!!" % (name))
	noodle_lock.acquire()
	print("%s 拿到面条啦!!" % (name))
	
	print("开始享受这碗面条.. ")
	time.sleep(0.5)
	
	noodle_lock.release()
	print("%s 放下面条了啊!!" % (name))
	kuaizi_lock.release()
	print("%s 放下筷子了啊~" % (name))


if __name__ == "__main__":
	
	name_lst1 = ["王振","王赢钱"]
	name_lst2 = ["李天照","魏小林"]
	for name in name_lst1:
		Thread(target=eat1,args=(name,)).start()

	for name in name_lst2:
		Thread(target=eat2,args=(name,)).start()

7、Event事件

​ wait:动态加阻塞

​ clear:将阻塞事件的值改成False

​ set:将阻塞事件的值改成True

​ is_set:获取阻塞事件的值

# 模拟连接远程数据库
"""连接三次数据库,如果都不成功,直接抛出异常"""
def check(e):
	
	# 模拟网络延迟
	time.sleep(random.randrange(1,6))
	
	# 开始检测链接合法性
	print("开始检测链接合法性")
	e.set()

def connect(e):
	sign = False
	for i in range(1,4):
		# 设置最大等待时间 1
		e.wait(1)
		if e.is_set():
			print("数据库链接成功 ... ")
			sign = True
			break
		else:
			print("尝试链接数据%s次失败" % (i) )
	
	if sign == False:
		# 主动抛出异常
		raise TimeoutError	
		
	
e = Event()

# 线程1负责执行检测任务
Thread(target=check,args=(e,)).start()
# 线程2负责执行连接任务
Thread(target=connect,args=(e,)).start()

8、线程队列

常用的线程队列有 queue、LifoQueue、PriorityQueue

queue:先进先出

LifoQueue:后进后出(按照栈的特点设计的)

PriorityQueue:按照优先级排序(从小到大);要么全是数字,要么全是字符串,不能混合,混合就报错:error

put:向队列中存,村的超出长度就阻塞

get:在队列中取,取得超出长度就阻塞

put_nowait:存,超出了队列长度,报错

get_nowait:取,没数据取不出来,报错

# (1) Queue
q = Queue()
q.put(1)
q.put(2)
print(q.get())
print(q.get())
# 取不出来,阻塞
# print(q.get())
print(q.get_nowait())

q2 = Queue(3)
q2.put(11)
q2.put(22)
q2.put(33)
# 放不进去了,阻塞
# q2.put(44)
q2.put_nowait(44)

# (2)LifoQueue
from queue import LifoQueue
lq = LifoQueue(3)
lq.put(11)
lq.put(22)
lq.put(33)
# print(lq.put_nowait(444))

print(lq.get())
print(lq.get())
print(lq.get())

# (3)PriorityQueue 
from queue import PriorityQueue

# 数字,默认从小到大排序
pq = PriorityQueue()
pq.put(13)
pq.put(3)
pq.put(20)
print(pq.get())
print(pq.get())
print(pq.get())

# 字符串按照ascii编码排序
pq1 = PriorityQueue()
pq1.put("chinese")
pq1.put("america")
pq1.put("latinos")
pq1.put("blackman")

print(pq1.get())
print(pq1.get())
print(pq1.get())
print(pq1.get())

pq3 = PriorityQueue()
# 元组中的第一个元素排序
pq3.put( (20,"wangwen") )
pq3.put( (18,"wangzhen") )
pq3.put( (30,"weiyilin") )
pq3.put( (40,"xiechen") )

print(pq3.get())
print(pq3.get())
print(pq3.get())
print(pq3.get())

9、线程池和进程池(改良版)

注: 线程池是由子线程实现的;进程池是由主进程实现的

线程池/进程池:

​ 实例化线程池:ThreadPoolExcutor (推荐5*cpu_count)

​ 异步提交任务:submit / map

​ 阻塞直到任务完成:shutdown

​ 获取子线程的返回值:result

​ 使用回调函数:add_done_callback

默认如果一个进程短时间内可以完成更多的任务,就不会创建额外的新的进程,以节省资源

回调函数:

​ 就是一个参数,将这个函数作为参数传到另一个函数里面.
​ 函数先执行,再执行当参数传递的这个函数,这个参数函数是回调函数

功能:
打印状态: a属性
支付状态: b属性
退款状态: c属性
转账的状态: d属性
把想要的相关成员或者相关逻辑写在自定义的函数中
支付宝接口在正常执行之后,会调用自定义的函数,来执行相应的逻辑
那么这个函数就是回调函数

''' 进程池和线程池 '''
from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
import os,time
def func(i):
	print("任务执行中... start" , os.getpid())
	time.sleep(10)
	print("任务结束... end" , i)
	return i

# (1) ProcessPoolExecutor 进程池基本使用
'''
"""默认如果一个进程短时间内可以完成更多的任务,就不会创建额外的新的进程,以节省资源"""
if __name__ == "__main__":
	lst = []
	# print(os.cpu_count()) # 8 cpu逻辑核心数
	# (1) 创建进程池对象
	"""进程池里面最多创建os.cpu_count()这么多个进程,所有任务全由这几个进程完成,不会额外创建进程"""
	p = ProcessPoolExecutor()
	
	# (2) 异步提交任务
	for i in range(10):
		res = p.submit(func,i)
		lst.append(res)		

	# (3) 获取当前进程池返回值
	# for i in lst:
		# print(i.result())
	
	# (4) 等待所有子进程执行结束
	p.shutdown()  # join	
	
	print("主程序执行结束....")
'''
	
	
# (2) ThreadPoolExecutor 线程池的基本用法
'''
"""默认如果一个线程短时间内可以完成更多的任务,就不会创建额外的新的线程,以节省资源"""
from threading import current_thread as cthread
def func(i):
	print("thread ... start" , cthread().ident,i)
	time.sleep(3)
	print("thread ... end" , i )	
	return cthread().ident
	
	
if __name__ == "__main__":
	lst = []
	setvar = set()
	# (1) 创建线程池对象
	"""限制线程池最多创建os.cpu_count() * 5 = 线程数,所有任务全由这几个线程完成,不会额外创建线程"""
	tp = ThreadPoolExecutor()# 我的电脑40个线程并发
	
	# (2) 异步提交任务
	for i in range(100):
		res = tp.submit(func,i)
		lst.append(res)
	
	# (3) 获取返回值
	for i in lst:
		setvar.add(i.result())
	
	# (4) 等待所有子线程执行结束
	tp.shutdown()
	
	print(len(setvar) , setvar)
	print("主线程执行结束 ... ")
'''
	
# (3) 线程池 map
from concurrent.futures import ThreadPoolExecutor
from threading import current_thread as cthread
from collections import Iterator
def func(i):
	# print("thread start ... ",cthread().ident)
	# print("thread end ... ",i)
	time.sleep(0.5)
	return "*" * i
if __name__ == "__main__":
	setvar = set()
	lst = []
	tp = ThreadPoolExecutor(5)
	# map(自定义函数,可迭代性数据) 可迭代性数据(容器类型数据,迭代器,range对象)
	it = tp.map(func,range(20))
	# 判定返回值是否是迭代器
	print(isinstance(it,Iterator))
	
	tp.shutdown()	
	
	for i in it:	
		print(i)


# add_done_callback 原型
class Ceshi():
	def add_done_callback(self,func):
		print("执行操作1 ... ")
		print("执行操作2 ... ")
		func(self)
		
	def result(self):
		return 123456

def call_back3(obj):
	print(obj)
	print(obj.result())
	
obj = Ceshi()
obj.add_done_callback(call_back3)

from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
from threading import current_thread as cthread
import os,time

def func1(i):
	print("Process start ... ",os.getpid())
	time.sleep(0.5)
	print("Process end ... ",i)
	return "*" * i

def func2(i):
	print("thread start ... ",cthread().ident)
	time.sleep(0.5)
	print("thread end ... ",i)
	return "*" * i
	
		
def call_back1(obj):
	print("<==回调函数callback进程号:===>",os.getpid())
	print(obj.result())


def call_back2(obj):
	print("<==回调函数callback线程号:===>",cthread().ident)
	print(obj.result())


# (1) 进程池的回调函数: 由主进程执行调用完成
"""
if __name__ == "__main__":
	p = ProcessPoolExecutor(5)
	for i in range(1,11):
		res = p.submit(func1,i)
		# 进程对象.add_done_callback(回调函数) 
		'''
		add_done_callback 可以把res本对象和回调函数自动传递到函数里来
		'''
		res.add_done_callback(call_back1)
	p.shutdown()
	print("主进程执行结束 ... " , os.getpid())
"""


# (2) 线程池的回调函数: 由当前子线程执行调用完成
if __name__ == "__main__":
	tp = ThreadPoolExecutor(5)
	for i in range(1,11):
		res = tp.submit(func2,i)
		# 进程对象.add_done_callback(回调函数) 
		'''
		add_done_callback 可以把res本对象和回调函数自动传递到函数里来
		'''
		res.add_done_callback(call_back2)
	tp.shutdown()
	print("主线程执行结束 ... " , cthread().ident)

廿四、协程

1、协程也叫纤程:

​ 协程是线程的一种实现

​ 指的是一条线程能够在多任务之间来回切换的一种实现.
​ 对于CPU、操作系统来说,协程并不存在.
​ 任务之间的切换会花费时间.
​ 目前电脑配置一般线程开到200会阻塞卡顿.

2、协程的实现

​ 协程帮助你记住哪个任务执行到哪个位置上了,并且实现安全的切换
​ 一个任务一旦阻塞卡顿,立刻切换到另一个任务继续执行,保证线程总是忙碌的,更加充分的利用CPU,抢占更多的时间片

​ 一个线程可以由多个协程来实现,协程之间不会产生数据安全问题

3、协程模块

greenlet gevent的底层,协程,切换的模块

gevent 直接用的,gevent能提供更全面的功能

# (1) 用协程改成一下生产者消费者模型
"""
def producer():
	# 数据范围0 ~ 999
	for i in range(1000):
		yield i
		
def counsumer(gen):
	for i in range(10):
		print(next(gen))

# 初始化生成器函数
gen = producer()	
counsumer(gen)
counsumer(gen)
counsumer(gen)
"""

# (2) 协程的具体实现
"""
switch 遇到阻塞时,只能手动调用该函数进行函数切换,不能自动实现切换,来规避io阻塞;
"""
from greenlet import greenlet
import time

"""
def eat():
	print("eat 1")
	g2.switch()
	time.sleep(3)
	print("eat 2")
	
def play():
	print("play one")	
	time.sleep(3)
	print("play two")
	g1.switch()
	
	
g1 = greenlet(eat)
g2 = greenlet(play)
g1.switch()
"""

# (3) gevent 
"""gevent 可以自动切换,但是不能够自动识别time.sleep这样的阻塞"""
import gevent
"""
def eat():
	print("eat 1")
	time.sleep(3)
	print("eat 2")
	
def play():
	print("play one")	
	time.sleep(3)
	print("play two")

# 利用gevent.spawn创建协程对象g1
g1 = gevent.spawn(eat)
# 利用gevent.spawn创建协程对象g2
g2 = gevent.spawn(play)

# 阻塞,必须g1协程执行完毕为止
g1.join()	
# 阻塞,必须gg协程执行完毕为止
g2.join()

print("主线程执行完毕 ... ")
"""
# (4) gevent.time 添加阻塞,让他实现自动切换
"""
def eat():
	print("eat 1")
	gevent.sleep(3)
	print("eat 2")
	
def play():
	print("play one")	
	gevent.sleep(3)
	print("play two")

# 利用gevent.spawn创建协程对象g1
g1 = gevent.spawn(eat)
# 利用gevent.spawn创建协程对象g2
g2 = gevent.spawn(play)

# 阻塞,必须g1协程执行完毕为止
g1.join()	
# 阻塞,必须gg协程执行完毕为止
g2.join()
print("主线程执行完毕 ... ")
"""
	
# (5) 终极大招 彻底解决不识别阻塞的问题
from gevent import monkey
monkey.patch_all() # 把下面所有引入的模块中的阻塞识别一下
import time
import gevent 

	
def eat():
	print("eat 1")
	time.sleep(3)
	print("eat 2")
	
def play():
	print("play one")	
	time.sleep(3)
	print("play two")
	
# 利用gevent.spawn创建协程对象g1
g1 = gevent.spawn(eat)
# 利用gevent.spawn创建协程对象g2
g2 = gevent.spawn(play)

# 阻塞,必须g1协程执行完毕为止
g1.join()	
# 阻塞,必须gg协程执行完毕为止
g2.join()
print("主线程执行完毕 ... ")

4、协程的例子

(1) spawn(函数,参数1,参数2,数3 .... ) 启动协程

(2) join 阻塞,直到某个协程任务执行完毕之后,再放行

(3) joinall 等待所有协程任务都执行完毕之后,在放行

g1.join()  g2.join()   <=>  gevent.joinall( [g1,g2] )(推荐:比较简洁)

(4) value 获取协程任务中的返回值 g1.value g2.value 获取对应协程中的返回值

(5) 注:python中语句和语句之间可以用分好;隔开写在一行;

# (1) 协程的其他方法
# 把下面所有引入的模块中的阻塞识别一下
"""
from gevent import monkey ; monkey.patch_all() 

import time
import gevent 

def eat():
	print("eat 1")
	time.sleep(3)
	print("eat 2")
	return "吃完了"
	
def play():
	print("play one")	
	time.sleep(3)
	print("play two")
	return "玩完了"

# 利用gevent.spawn创建协程对象g1
g1 = gevent.spawn(eat)
# 利用gevent.spawn创建协程对象g2
g2 = gevent.spawn(play)

# 默认:主线程不会等待所有的协程都执行完毕就会终止程序
# 等待g1 g2协程任务完毕之后再向下执行;
gevent.joinall( [g1,g2] ) # 一个列表参数
print("主线程执行结束 ... ")

print(g1.value)
print(g2.value)
"""

# (2) 利用协程爬取数据
"""requests 抓取页面数据模块"""

"""
HTTP 状态码
	200 ok
	404 not found
	400 bad request
"""

# 基本语法
from gevent import monkey ; monkey.patch_all()
import time
import gevent
import requests
"""
response = requests.get("http://www.baidu.com")
print(response)
# 获取状态码
print( response.status_code )
# 获取网页中的字符编码
res = response.apparent_encoding
print( res )
# 设置编码集,防止乱码
response.encoding = res
# 获取网页当中的内容
res = response.text
print(res)
"""

url_list = [
"http://www.baidu.com",
"http://www.4399.com/",
"http://www.7k7k.com/",
"http://www.taobao.com/",
"http://www.jingdong.com/"]
def get_url(url):
	response = requests.get(url)
	if response.status_code == 200:
		# print(response.text)
		time.sleep(0.1)
# (1) 正常爬取
'''
starttime = time.time()
for i in url_list:
	get_url(i)
endtime = time.time()
print("执行时间:" ,endtime - starttime ) # 18.780214071273804
'''
"""
import re
strvar = '<img lz_src="http://i5.7k7kimg.cn/cms/cms10/20200609/113159_2868.jpg"'
obj = re.search(r'<img lz_src="(.*?)"',strvar)
print(obj.groups()[0])
"""

# (2) 用协程的方式爬取数据

lst = []
starttime = time.time()
for i in url_list:
	g = gevent.spawn(get_url,i)
	lst.append(g)

gevent.joinall(lst)	

endtime = time.time()

print("执行时间:" ,endtime - starttime )
posted @ 2020-04-28 21:25  HashFlag  阅读(838)  评论(0编辑  收藏  举报