Python基础
程序设计语言
自然语言:
- 人 与 人 交流的 语言
- 中文 英文 韩文
机器语言:
- 机器 能 读懂的语言。
程序设计语言:
- 人 与 机器 进行交流的 媒介。人能读懂,机器也能懂。
- c c++ java,php net, c#,javascript,python
* python历史
1.创建 1989年 Rossum Guido
2.发展:python2.7版本 官方规定2020年停止支持。
3.python3.5 上课 后期学习过程中 逐渐过渡 3.6+
* python特点
1.结构简单,简洁,优雅。
2.强大的类库。
3.胶水语言。(可以调用JAVA,C的类库)
4.面向对象编程语言
5.跨平台。
- 平台 操作系统 os windows系统 linux, unix poxis,android ios,塞班。。。
* python应用
1.python基础语法
2.web开发 python 前端后端-python框架,django,flask,redis。。。
3.爬虫 数据 + 大数据 数据分析 数据挖掘
4.AI 人工智能 算法
5.运维 测试
* python运行机制
1.解释型
- 逐行解释执行。边解释 边 执行。
- 优点:跨平台。 javascript 解释型语言。
- 缺点:相较编译型 运行速度 较慢
- cmd中 进入到py文件所在的当前目录下
python 文件名.py
2.编译型
- 生成一个机器码文件。可以直接执行。.exe windows系统。mac系统 dmg
- 优点:执行速度快。
- 缺点: 不能跨平台
- cmd:
- python -m py_compile 文件名.py 生成.pyc
- 进入到该文件所在的当前目录下,解释执行 python 文件名.pyc
3.先编译后解释
- .py文件 源码
- compiler:编译器
- 字节码文件:通过编译器生成的文件。 .pyc文件
- 解释器:解释执行 PVM python virtual machine
- 处理器:processor
- cmd:进入到py文件所在的当前目录下
python -m 文件名.py
* python解释器
1.Cpython 解释器
- python官方规定的解释器。最标准,最官方的解释器。 C语言。
2.Jpython
- 基于 java 解释器
3.Ironpython
- 基于 .net 解释器
优化 解释器:
- Pypy 解释器
* 变量
概念: 本质 内存单元。 真正保存的 地址
- 标签 容器 保存单一数据。数量 - 单一,一个。
- 弱类型 动态
使用规则:
- 声明 并 赋值,然后才能使用。 num = 10
声明方式:
- 多个变量 同时声明
- 变量间赋值
a = 11
b = a # 11 传递 的 首地址
- 变量可以重新赋值。
a = 22
print(a) # 22
* 标识符 命名规则
1.由数字 字母 下划线 组成。其中 数字不能作为开头。
- 数字不能作为开头
2.英文字母,区分大小写 敏感。
3.尽量 不要用中文
- python2 编码 ASCII
- python3 编码 unicode编码
4.不要使用关键字 保留字
and or not if else def class in is not ....
- import keyword
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']
保留字:print input int str float bool tuple list....
5.长度没有限制。
开发习惯:
- - 望文生义 见名知意
- - 普通变量名、(函数名): 小驼峰命名法 helloWorldGood 第一个单词首字母小写,之后单词的首字母大写
- - 类名:大驼峰。HelloWorldGood 第一个单词首字母大写
- - 包名:全小写
- - 常量:全大写
* 数据类型
整型 浮点型 布尔型 字符串型 None
1.整型 int
- 不带小数点 数字
2.浮点型 float
- 带小数点 数字
表示方式:
- 直接表示 0.001
- 科学e计法 E/e 表示10
- f4 = 123.45
f5 = 1.2345e2
print(f5)
f6 = 0.00012345
f7 = 1.2345e-4
print(f7)
- 无穷大 inf
3.布尔型 bool
- True 真
- False 假
4.字符串型 str
- - 引号包起来
- - 单引号 双引号 三引号
- - 单引号:''
- - 双引号: “字符” 打印字符串 "123" 单双引号 交叉嵌套使用
- - 三引号:
三单引号
三双引号
- 多行字符串
- 文档注释 help() 查看的API
- 补充:
如果使用 单引号 双引号 也想实现多行字符串,使用 \n 转义字符
5.None
- 空值
* 查看数据类型
type()
- 功能:检测数据类型
- 数据类型 返回
* 数据类型转换
int() float() str() bool()
1.其他类型 转为 int类型
- float 转为 int
- 把 float小数点后去掉,保留整数。
>>> int(1.222)
1
>>> int(1.99)
1
>>>
- str 转为 int
- 字符串中包含纯整数数字字符 ,可以成功转换
print(int('123.45')) # ValueError -----------------------------
print(int('-123')) # -123
- bool 转为 int
- True 1
- False 0
2.其他类型 转为 float
- int 转为 float
- print(float(0)) #0.0
print(float(10)) # 10.0
- str 转为 float
- print(float('123'))
print(float('123.45')) #123.45
print(float('-123')) # -123.0
- bool 转为 float---------------------------------
- True 1.0
- False 0.0
3.其他类型 转为 str
- int 转为 str
print(str(10))
- float 转为 str
print(str(1.2345)) # '1.2345'
- bool 转为 str
print(str(True)) # 'True'
print(str(False)) # 'False'
- None 转为 str
print(str(None)) # 'None'
- 所有类型都可以转换为 字符串
4.其他类型 转为 bool
- int 转为 bool
- 0 转为 False
- 非0 转为 True
- float 转为 bool
- 0.0000 转为 False
- 非 0.0 转为 True
- str 转为 bool-----------------
- 空串 转为 False
- 非空串 转为 True
- ‘None' True
- None 转为 bool
- 转为 False
复习:
- python特点
- python运行机制
- 先编译后解释
- 编译型
- 解释型
- python 变量
- 使用规则:声明并赋值
- 数据类型
- int float bool str None
- 数据类型转换
- int() 转为 整型
- bool() 转为 布尔型
- str() 转为字符串 ‘None’
- float() 转为浮点型
* 表达式
表达式:由 数值 变量 通过 一定的运算符 连接起来的 式子
* 运算符
1.算术运算符
+ - * / % // **
+:
- 进行加法运算
- - print(10+20)
- - print(10+True) # 11 True
- - print(10.0+20) # 30.0
- - print(10+'10') # TypeError 不能相加
- 拼接
- print('12'+'456') # '12456'
- a + '456' 变量与字符串拼接 注意引号的问题。
s1 = 'text'
num1 = 10
print('<input type="'+ str(num1) + '">')#双引号 里面 单引号 里面str()
print('<input type="'+ s1 + '">')
-:
减法运算
- print(10-3) # 7
- print(10.0-3) # 7.0
*:
- 乘法运算
- 拼接:
- '12'*3 #'121212'
注意:此时 * 后面的 只能是大于0的整数,从而实现 字符串的拼接。
不会报错,但不显示 >>> print('nihao'*0)
>>>
/:
- 除法运算
//:
- 真除 地板除
- 向下取整。
print(-10//6) # -2
>>> print('10.5//2')
10.5//2
>>> print(125//10)
12
>>> print(189//10)
18
>>> print(14.9//10)
1.0
%:
- print(9.0%2) # 1.0
**:
幂运算
2.赋值运算符
- = 不同于数学等号,赋值。把等号右边的 赋值给 等号左边。
3.增强型赋值运算符
+= -= *= /= //= %= **=
a += 1 等同于: a = a + 1
4.逻辑运算符
not : 非
- not True # False
- not 0 # True
and : 与 和 且
- and左右两侧 都要为真,结果才为真 True
- 10 > 7 > 3 等同于: 10 > 7 and 7 > 3
or : 或
or左右两侧 只要一侧为 真, 结果则为真True
- print(10 > 7 or 10 < 2) # True
特殊案例:
短路:
and:
print(0 and 1) # 0
print(1 and 0) #0
print(1 and 2) # 2--------------------------------------
or:
print(0 or 1) # 1
print(1 or 0) # 1
print(1 or 2) # 1----------------------------------------
5.比较运算符 关系运算符
> < >= <= != ==
!= : 不等于
==: 等于
- 最终结果:True False
6.成员运算符
in
not in
检测某个成员是否在某个对象里,结果 True False
'1' in '123' #True
'1' not in '123' # False
7.三元运算符-------------------------------
值1 if 布尔表达式 else 值2
如果 布尔表达式为真,则取 值1,否则,取 值2
s1 = '学python' if int(a) > 18 else '家里蹲'
8.位运算符
- 十进制 与 二进制 转换
十进制:逢10进1 0-9
二进制:逢2进1 0 1
十进制 二进制
水仙花数字 三位数 每个位上的立方之和 等于 自身。
优先级问题:--------------------幂,数,比,逻
()括号 提升优先级
幂运算 **
算术运算符:先乘除后加减
比较运算符
逻辑运算符
- not and or 优先级依次降低
True and True or True not False
True and True or True True
True or True
条件分支
三大结构:
顺序结构:从上到下 逐行 默认。
选择结构: 条件分支
循环结构:for while 循环
1.if分支
if 布尔表达式:
代码块1
- 如果 布尔表达式成立/为真/True, 执行 代码块1;否则,不会执行。
2.if else
if 布尔表达式:
代码块1
else:
代码块2
- 如果 布尔表达式成立/True,执行代码块1;否则,执行 代码块2
3.if elif elif ... else
else if - 合并 elif
if 布尔表达式1:
代码块1
elif 布尔表达式2:
代码块2
elif 布尔表达式3:
代码块3
。。。
else:
代码块N
如果 布尔表达式1成立,则执行代码块1,执行完毕后,该if结构结束;
否则,继续判断 布尔表达式2是否成立,如果成立则执行代码块2,
然后if结构结束;以此类推。
如果以上布尔表达式均不成立,则执行else后的代码块N。
注意:每个if分支是 互斥的,只要执行其中一个,整个结构就结束。------------------------------------
4.if 嵌套
* 循环
重复执行某个代码块。
while循环
- while 布尔表达式:
代码块/循环体
注意:避免死循环。注意 循环体内要有 变量步进改变(递增,递减)
for循环
- for 变量 in 可迭代对象:
代码块/循环体
可迭代对象:能用for循环
字符串 - 可迭代对象
* range
range() 生成的对象 是可迭代对象---------------------------------
range(start,stop,step)
- start: 起始值 包含该值
- 可以省略,默认为0
- stop: 结束值 不包含
- step: 步进 默认1 可以省略
注:
range(0) 符合语法规范,生成range对象中没有数字
以上三个值都可以为负数。
* break&continue
break:
- 功能: 结束当前循环结构
continue:
- 功能: 结束 本次循环,继续 执行 下次循环。
* 列表
容器:保存数据
变量:保存单一数据
列表:保存多个数据。
list
python中,弱类型语言。------------------------
列表中 数据 数据类型可以多种。
列表中数据,习惯上 称为 元素。
* 列表创建
~~~markdown
两种方式
1.直接创建
- 方括号 标志。 多个数据之间 逗号隔开
[10]
[10,True,40,'123']
[] # 空列表
2.工厂函数
list(iterable) 把其他数据类型 转换为 列表
iterable:可迭代对象
- 字符串
- range()
- list
* 列表长度 元素的个数
len(list)
- 检测list的长度,元素的个数
* 元素操作-增删改查
下标/index/位置:从0开始的,一系列正整数 包含0
第N个元素,下标 N-1.
列表的长度 比 列表的最大下标 大 1
1.查 访问 查看 单一元素访问
- 列表名[index]
2.改 修改
列表名[index] = 新值
3.删 删除
del 列表名[index]
4.增 增加
l1[6] = '杨聪' # indexError index out of range
手动增加 不能操作。
* 列表循环/遍历
对列表中的元素进行逐一访问,列表循环、遍历。
方式一:
for i in l1:
print(i)
方式二:
length = len(l1) # 长度
for i in range(length): # 0 length-1
print(l1[i])
* 列表 分片 切片 slice
分片/切片:列表中元素进行截取,形成一个新的列表。原列表没有改变。
l1[0:5:1]
列表名[start:stop:step]
- start:起始位置 包含该位置
- 可以省略,默认从0开始。0-下标
- stop:结束为止 不包含
- 可以省略,默认 到 末尾。
- step:步进 默认为1 可以省略
总结:
- 切片 标志 [:] :冒号
- 如果[]出现负数,注意 倒数-从右往左看。倒数第一个,表示为 列表名[-1].慎用。
- 切片 形成一个新列表。
print(l1[0:5]) print(l1[0:5:2]) print(l1[:5]) print(l1[1:5]) print(l1[:]) print(l1[::]) print(l1[5:0:-1]) print(l1[5::-1]) print(l1[-5:-2]) print(l1[-5])
* for-else
for 变量 in 可迭代对象:
循环体
else:
代码块
- for循环正常完全执行完毕之后,则会执行else代码块
- for循环受到强制中断break,else代码块不会被执行。
demo1:
for i in range(10):
print(i)
if i == 6:
break
else:
print('我是else代码块')
* 元组 tuple
列表 相似
- 可以保存 多个 数据。
- 下标 有序
- 支持切片
- 可迭代对象 for循环
- 不可修改
- 操作符:+ * 成员运算符,比较运算符+逻辑运算符
* 创建
元组中 元素 数据类型 可以多种,类似 列表。
两种方式:
1.直接创建 标志:逗号
- - t=(10,20,30) 多个元素之间逗号隔开。
- - t=(10,) 元素只有一个,元素后面必须加逗号,才可以为元组。----------------------------------
- - t=(10) 没有逗号情况下,解析为 int 整型数字10-----------------------------------------------
- - t=1,2,3,4 如果没有小括号,t依然是 元组-------------------------
- 建议:使用该方式创建元组时,建议大家使用标准规范 () + 逗号。
2.工厂函数 把其他数据类型 转换为 元组类型
- - tuple(iterable)
- - tuple('123')
- - tuple(range(5))
- - tuple([1,2,3,4])
- - tuple((10,))
>>> tuple('123')
('1', '2', '3')
>>> tuple(range(5))
(0, 1, 2, 3, 4)
>>> tuple([1,2,3])
(1, 2, 3)
>>> tuple([123])
(123,)
>>> tuple(123)
Traceback (most recent call last):
File "<pyshell#4>", line 1, in <module>
tuple(123)
TypeError: 'int' object is not iterable
>>> tuple((123))
Traceback (most recent call last):
File "<pyshell#5>", line 1, in <module>
tuple((123))
TypeError: 'int' object is not iterable
>>> tuple((123,))
(123,)
type(t) # tuple
len(t) # tuple的长度,元素的个数
* 元素操作-增删改查
1.查 访问
- 元组名[index]
- index变量 也可以接收到负数,负数-倒数,从右往左。
2.修改 删除 增加
- 元组不支持
* 元组切片
元组支持切片,与 列表 使用方式 一致。
切片结果:截取原元组中元素形成一个新的元组。原元组不变。
* 元组遍历/循环
~~~markdown
for i in t1:
print(i) # 每个元素
~~~
* 元组 操作符
1.+
- 功能:实现 拼接 形成一个新的 元组
- 原 元组 不变
t1 = (1,2,3)
t2 = (4,5,6)
print(t1 + t2) # (1,2,3,4,5,6)
2.*
- 功能:实现 拼接 元组中元素翻倍拼接 形成一个新的 元组
- 原 元组 不变
t1 = (1,2,3)
print(t1 * 3) # (1,2,3,1,2,3,1,2,3)
3.成员操作符
- in
- not in
print(1 in t1) # True
print(4 not in t1) #uanf
4.逻辑运算符 比较运算符
- print(t1 > t2)
print(t1 < t2)
print(t1 < t2 and t1 > t2)
* 总结
- 下标-有序
- 支持切片
- 可迭代对象 for
- 不可修改
* 创建字符串
1.直接创建
- s1 = '123'
2.工厂函数
- - str(obj)
- print(str('123')) # '123'
- print(str(10)) # '10'
- print(str(10.0)) # '10.0'
- print(str(True)) # 'True'
- print(str(None)) # 'None'
- print(str([1,2,3]),type(str([1,2,3]))) # '[1,2,3]'
- print(str((1,2,3))) # '(1,2,3)'
* 转义字符
转义字符:\ 标志。\后面的字符,会通过\被转义,失去了原来普通字符的意义,具备其他特殊功能。
- \n:换行符
- \t 制表符
- \\: 转义\
- \":转义"
- \':转义'
- \v:纵向制表符
- \r:换行符 行首
- \b:退格
- \0:空格
* 原始字符串
原始字符串:在字符串前加r
s2 = r'abc\nabc'
print(s2) # abc\nabc
s3 = r'D:\AI161\day05\字符串初识05.py'
* 二进制字符串
二进制字符串:普通字符串前加b
s3 = b'abc'
print(s3) # b'abc'
print(s3.decode()) # 'abc' decode解码 encode编码
* 字符串格式化
+:拼接 变量 字符串 拼接
1.format() 与 {} 配合使用
- - 功能:实现 字符串格式化。
- - 使用方式一: ‘{}’.format(实际数据)
- - 注意:{}数量 需要 与 实际数据 的数量 一致,并且顺序要一致。一一对应
- demo:
- s4 = '我叫{},我今年{}岁了。我有个列表{}'
- print(s4.format('张亚哲',18,[1,2,3,4]))
- - 使用方式二: '{0}'.format(实际数据)
- - 注意:花括号内数字从0开始(包含0) 正整数 0 1 2 3 。。。。 {}内 不能没有数值。
- - format() 括号内 实际数据,可以理解为一个元组。元组中元素的下标 即为 花括号内可选填的数值。
- demo:
- s5 = '我叫{4},我今年{1}岁了。我有个列表{2},我生日是{3}年,{0}月,{4}日'
- print(s5.format('张亚哲',18,[1,2,3],2001,5))
- - 使用方式三:‘{变量名}’.format(变量名=值)
- - 注意:{}里都要有变量名
- - format()括号内,需要使用 变量名=值 形式来传数据,此时多个 变量名=值 这样式子,没有前后书写顺序问题。
- demo:
- name = '康恒'
- age = 18
- # print(s6.format(name='康恒',age=18))
- print(s6.format(a=10,name=name,age=age))
- 注意:此时{} 不再是普通字符,而具有了 特殊含义。
2.格式化符号 需要与% 组合使用。
- - 如果需要传递多个数据,% 后面 元组,元组内元素就是所需要的数据。
- demo:
- s1 = '我叫%s' % '王晓敏'
- print(s1) #我叫王晓敏
- s2 = '我叫%s,我有个%s' % ('王晓敏','王晓辉')
- - 格式化符号
- - %s string 格式化 字符串
- - 万能 不确定其他格式化符号,都用%s
- - %d digit 格式化整型数字
- s5 = '%d' % 18
- print(s5)
- s6 = '%d' % 18.56789
- print(s6)
- s7 = '%d' % -18
- print(s7)
- s8 = '%d' % True
- print(s8)
- - %f float 格式化浮点型
- - 默认小数点后保留6位。也可通过 辅助命令符指定保留位数。.n
- s1 = '%f' % 18
- print(s1) # 18.000000
- s1 = '%f' % 1.234567890
- print(s1) # 1.234568
- s1 = '%f' % True
- print(s1) # 1.000000
- - %e 格式化 e计法
- s1 = '%e' % 123
- print(s1) # 1.230000e+02
- s1 = '%e' % 0.00123
- print(s1) # 1.230000e-03
- - %g 自动选择使用 %d %f %e
- - 显示数据长度6位 涉及 四舍五入
- s1 = '%g' % 1234567890
- print(s1) #123 1.23457e+09
- s1 = '%.7g' % 1234516.35678
- print(s1) # 1.23452e+06 1.2e+06 1.235e+06 1234516
- - %x 格式化 十六进制 无符号
- - s1 = '%#x' % 255
- print(s1) # ff 0xff 带符号
- - 注意,如果想要有符号显示,可以使用辅助命令符 #
- - %o 格式化 八进制 无符号
- - 注意,如果想要有符号显示,可以使用辅助命令符 #
- s1 = '%#o' % 255
- print(s1) # 377 0o377带符号
- print(s1.isdecimal(),'56行')
- - %i 格式化 十进制
- s1 = '%i' % 255
- print(s1)
- # 判断是否是isdecimal()
- # s1 = '12345'
- print(s1.isdecimal())
- - 辅助命令符:
- - #: 显示符号
- - 十六进制 '%#x' 0x
- - 八进制 '%#o' 0o
- - -: 左对齐
- - 默认 右对齐
- s1 = '%-10.1f' % 124.5678905
- print(s1) # 124.6 空格
- - m.n:
- - m:显示最小宽度。 如果原数据的宽度大于m,此时正常显示原数据,m设置无效。
- - n:保留的小数点后位数。
- demo:
- s1 = '%10f' % 12.456789123
- print(s1) # 12.4
- - 0:
- 实际数据宽度 比 要求显示数据宽度 小,此时用0填充。
- demo:
- s1 = '%010.1f' % 124.56789123
- print(s1) # 00000124.6
- - %s string 格式化 字符串
函数
- - 特定功能的代码块 function
- - 特点:
- - 复用性高
- - 代码量减少
- - 提高了开发效率。
- - 代码结构清晰,维护方便。
* 函数声明
1.语法结构:
def 函数名(参数1,参数2。。。):
代码块
* 函数使用
1.语法结构:
函数名(参数1,参数2,参数3。。。)
注:
- - 使用规则:函数不调用不执行
- - 函数可以多次调用,什么时候需要什么调用即可。
* 参数 表现形式:
- 形参:形式参数。声明函数时,函数名后面括号里的。普通变量。
- 实参:实际参数。调用函数时,函数名后面括号里的。
- 实参 对 形参 进行 赋值。
1.位置参数
- - 实参 形参 位置顺序要一一对应,数量也要一致。
2.关键字参数
- - 调用函数时,通过 变量赋值 形式 来传实参。
- - 避免 位置参数 中 位置顺序混乱问题。
- - 与 位置参数混用,位置参数必须在关键字参数 前面,否则报SyntaxError
- - 同一个形参赋值只能一次。
def fn1(name, age):
print('我叫{},我今年{}岁了。'.format(name, age))
fn1(name='宋泽凯', age=21) #我叫宋泽凯,我今年21岁了。
fn1(age=21, name='宋泽凯') #我叫宋泽凯,我今年21岁了。
fn1(21,age='宋泽凯') #我叫21,我今年宋泽凯岁了。
fn1(21,name='宋泽凯') #TypeError 不能给name赋值两个值
3.默认参数
- - 声明函数时,形参赋值 值-默认值
- - 调用函数时,如果默认参数没有重新赋值,使用默认值;如果重新赋值,即使用最新的值。
- - 函数声明时,注意:默认参数放在位置参数后面
4.可变长参数 收集参数
- - *args
- - args:约定俗成 变量名称 也可自定义
- - 调用函数传实参时,位置参数传参即可,实参会打包成元组形式传入函数。
- - **kwargs
- - kwargs:变量名称 约定俗成
- - 调用函数传实参时,使用关键字参数方式,实参会打包成 字典 传入函数。
总结:
- 声明函数参数排序规则: 位置参数 默认参数 可变长参数-*args 可变长参数-**kwargs
* return
return:返回
- - 功能: 返回 结束函数结构。 类似 break-循环 中断
- - return后面可以跟 数据/值, 称为 返回值
- - 返回值 数据类型可以多种 根据需要
- - 函数中没有手动写return,系统会在执行完函数内代码后默认执行 return None.
- - return后面可以跟 数据/值, 称为 返回值
* 函数嵌套调用
一个函数内,可以调用另一个函数。 函数嵌套调用
* 作用域
作用域:变量 函数 起作用的区域、范围
分类:
- - 全局作用域
- - 定义在顶格
- - 从定义行开始,整个文件
- - 全局变量
- - 局部作用域-函数作用域:函数 限制作用域
- - 从函数内部,定义行开始,到 函数代码块结束
- - 局部变量
- - 总结:
- - 如果全局变量 与 局部变量 同名,局部作用域下访问变量,优先访问到 局部变量。
- - 局部作用域下访问变量,优先从当前作用域查找,如果有直接使用,如果没有,逐级往上一级作用域查找,直到找到全局作用域,如果还没有,报错NameError
- - 中心开花式(zz)
1.局部作用作用域下 操作 全局变量 ,有问题
- 场景一:
a = 10 def fn1(): # print(a) a += 20 print(a) #不允许操作 报错 unboundlocalError fn1()
- 场景二:
a = 10 def fn1(): print(a) a = 20 # 不允许操作 fn1()
- 场景三:
a = 10 def fn1(): # print(a) a = a + 10 # 不允许操作 fn1()
- 使用关键字
- - global 全球的 全局的
- - 语法: global 全局变量
- - 功能:允许局部作用域操作 修改 全局变量
- - 注意:global 只能在当前层作用域起效,如果下一级作用域仍想修改,需要再次使用global
1.局部变量,在下一级作用域 做修改 操作
使用关键字:
nonlocal 非局部的
- - 语法: nonlocal 局部变量
- - 功能:可在下一级作用域来对上一级局部变量 操作 修改
- - 注意:nonlocal起作用 仅限当前层作用域。
* 函数内部定义函数 内嵌函数
函数内部 定义了一个函数
def fn1(): def fn2(): print(123) fn2() fn1()
* 匿名函数
没有名字的函数
lambda表达式
* 闭包
closure
外层函数返回的,持有 其外层函数局部变量的 函数(对象)
def fn1(): a = 10 def fn2(): print(a) # 10 print(fn2) # return fn2 f = fn1() f()
特点:
- - 开辟了独立空间,不会被全局污染,也不会污染全局
- - 提高了数据的隐蔽性
- - 提供了给外界接口,允许外界来访问
- - 应用:装饰器 集群
- - 延长变量生命周期,大量使用闭包,严重浪费内存,慎用。
- - 生命周期
* 重载
python中 没有 重载概念。
自定义函数时,注意 不要出现同名,否则,后者会覆盖前者。
def fn1(name): print(123,name) def fn1(name, age): print('我叫{},我今年{}'.format(name, age)) fn1('马冬梅',30)
* 高阶函数
1.filter(function or None,iterable)
- - filter 过滤
- - 功能:生成一个filter对象,该对象是迭代器对象。- function:函数名。或者 lambda表达式
- 元素为 Iterable中 经过function处理为真 或者 自身为真的 元素。
2.map(func,*iterables)
- - 功能:生成一个map对象
- - 该对象是迭代器对象。
- 元素为 *iterables中对应下标元素传入func处理之后生成的最终结果。
- 生成元素个数 由 *iterables中 长度最短的iterable决定。
print(list(map(lambda x:x+2,[1,2,3,4]))) # [3, 4, 5, 6]
print(list(map(lambda x,y:x+y,[1,2,3,4],(5,6,7,8)))) # [6, 8, 10, 12]
print(list(map(lambda x,y:x+y,[1,2,3,4],(5,6,7)))) # [6, 8, 10]
3.reduce(func,seq,[initial])
- - 功能:把seq中元素从左到右依次传入func做计算,最终减少到一个值。
- - 返回值:计算最终的结果
- - initial:参数可选。
- - 计算的初始值 初始元素
import functools print(functools.reduce(lambda x,y:x+y,[1,2,3,4,5])) # 15 print(functools.reduce(lambda x,y:x*y,[1,2,3,4])) # 15
* 字典
- 查字典:拼音 偏旁部首
- 映射 一一对应
- 映射类型
- 键值对 数据成对:一个键 对应 值
- 可迭代对象
- 可修改
- 不支持下标 无序
- 不支持切片
* 字典创建
字典 dictionary dict
1.直接创建
- d1 = {} # class dict
- d2 = {键1:值1,键2:值2,键3:值3....}
- 注意:键不可重复,如果一旦重复,后者会覆盖前者。
- d2 = {'item1':1,'item2':2,'item3':3, 'item3':4}
- # {'item2': 2, 'item3': 4, 'item1': 1}
2.工厂函数(死记一下格式,其他格式有报错,目前)
- dict()
- - 功能:把其他数据类型 转换为 字典
- - dict() # 空字典
- - dict(mapping) # mapping 映射对象
- - dict(iterable)
- - 注意:iterable中元素,有长度概念,长度为2.
- - print(dict(['qa']))
- print(dict(['as','df']))
- print(dict(['12','夫人','rf']))
- >>>
- {'q': 'a'}
- {'d': 'f', 'a': 's'}
- {'r': 'f', '夫': '人', '1': '2'}
- 注意:# #print(dict([12,23]))#报错
- - dict(**kwargs)
- - 可变长参数:
- dict(a=10,b=20,c=30) # {'c': 30, 'b': 20, 'a': 10}
* 字典操作
字典支持 增删改查 操作
d1 = {1:'郭道文',2:'安岗',3:'张常兵','和雁雁':18} print(id(d1)) # 1.查 print(d1[1],d1['和雁雁']) # 2.修改 d1[1] = True print(d1) print(id(d1)) # 3.增 d1['item1'] = None print(d1) print(id(d1)) d1[3] = '石金' print(d1) # 删除 del d1[3] print(d1)
集合
集合:
- 标志{} 跟字典一样
- 元素 数据类型 不可变 字典中键
- 可迭代对象
- 可修改
- 不支持下标 无序
- 不支持切片
- 不可重复 唯一
数据类型
- - 不可变:int float bool None tuple str frozenset
- - 可变:list dict set
面向对象
OOP: Object oriented programming 面向对象编程思想
面向过程:
VS.: 面向对象 对 面向过程的 封装
* 对象
对象: 现实世界 在 计算机中 的 反映。 解决 现实世界的实际需求
- - python 万物皆对象
- - 构成:
- - 属性 表示 对象的一些特征 特性 对象是什么?
- - 方法 表示 对象 能干什么?功能?
* 类 与 对象
类 是 对象 的 模板 ---------------------------------
对象 是 类的 实例 (具化)------------------------------------
* 创建类
1.语法
class 类名:
name = '郭师傅'
def eat(self):
#代码块
类名:大驼峰
* 创建对象
1.语法
对象1 = 类名(参数)
2.使用属性 方法
- 点语法
- 对象1.属性
- 对象1.方法()
3.需要几个对象,就创建几个即可。类 与 对象 是 一对多 的关系。------------------
* 属性
1.类属性
- - 类内部,方法之外-------------
- - 所有实例对象 可以 共用。
- - 一旦改变,所有实例对象都会受到影响。---------------------------------
2.实例属性
- - 类内部,方法内部 - 只有在实例对象调用该方法之后,该属性才会创建。-------------
- - self.实例属性 = 值
- - 实例属性发生改变,不会影响其他其他实例对象。
- - 实例对象 修改 类属性,创建自身的类属性-实例属性。 不会影响其他对象--------------
- -类本身对其内属性进行修改,会影响类修改后的实例对象------已验证
注意:
- - 实例属性 与 类属性 同名,实例对象访问时,优先访问到实例属性。(重点)
- - 实例属性 与 方法 同名, 实例对象调用包含实例属性方法之后,再使用该名字,会以实例属性方式来访问。
- - 类属性 与 方法 同名,实例对象 访问时, 会以方法方式来访问。
* 方法
方法创建时,会有一个参数。self-----------------------一般编译器自动写入
- - self 形参 变量名 约定俗成 self-自己
- - self: - 如果 类调用 方法,self需要手动传实参。
- 实例对象调用方法时,系统会自动把 当前实例对象 地址 传递给self---------------------------
- - self 指 当前实例对象
* init
__init__() 特殊方法 初始化方法 构造方法 魔法方法
- - 功能:初始化 实例对象属性
- - 创建对象时,会自动调用该方法,如果手动设置了方法,就会执行当前设置方法,覆盖掉系统默认的init方法。
- - 该方法返回值 必须是 return None. -------------
- - 类中 有一个该方法 即可。否则会出现覆盖。--------------
* 组合
一个对象中使用其他对象。组合使用
后续会再深入涉及组合使用问题。
*类 和 对象
- 类 对象 关系:
- 类是对象的模板,对象是类的实例。类 对象 一对多 关系------------
- 类 创建
- 属性 是什么?特征
- 方法 能干什么?功能
- 实例对象
- 实例对象 共用 类 属性
- 属性
- 类属性
- 实例属性
- 方法
- self
- 方法内 参数 形参 变量名
- self 功能:实例对象调用方法时,系统自动把 实例对象的地址传递 self。
self 指 当前实例对象
- __init__
- 初始化方法 构造方法 魔法方法
- 创建实例对象时,自动调用该方法。
手动初始化实例属性时,手动书写__init__,创建实例对象时,此时调用手写__Init__,会覆盖掉系统默认的__init__.
- __init__仅有一个。return None
- 组合
- 大对象 中 属性 是 小对象
~~~
* 私有属性
python中 属性 特殊写法
语法:
- 双下划线 + 属性名 私有属性
- 范围:只能在本类 中访问 -------------------
- 私有化属性 操作
- 通过 get set del 方法来完成 操作
python中 所谓属性私有,并不能实现真正私有。外部可以间接访问--------------
- 实例名._类名__属性名 --------------
~~~
* 三大特性
封装 继承 多态
封装:
- 封闭包装 对象 把 属性 方法 限制在 边界内部。-------------------
- 方法 一类特殊函数,绑定在对象上。
- 函数: 面向过程的封装 功能代码块
- 对象:更高一级封装。
- 属性 私有化 最高级别封装。
继承:
- 子承父业
- 概念
- 功能:减少重复代码,同时扩展子类的功能,设置类与类的关系--------------
- 被继承的类 父类 基类 超类 object 类
- 继承的 类 子类 派生类
- 继承的类 与 被继承的类 关系 is a
- 人类 是 动物类
- 学生类 人类
- 小学生类 学生类
- 语法:
- class 子类名(父类1,父类2。。。):
- 如果创建类时,没有手动设置父类,默认情况下 继承自 object 类,顶层类。---------------
注意点:
- 父类中 私有属性 原则上 子类不会继承。间接可以--------------
- 子类 与 父类中 方法、属性 同名,访问时,优先本类来进行查找。如果本类没有,逐级往上查找,直到找到顶层类object,直接报错。
- 子类中 重写 父类的方法,又想继续使用父类该方法。
- 父类.方法() # 需要手动传入参数----------------------即:父类.方法(self)
- super().方法() # 不需要手动传参,系统会自动传入
- 继承 分类
- 单继承
- 单一继承。父类只有1个。Student类 父类 Person类
- 继承功能 单一,继承链 逻辑清晰
- 多继承
- 继承自多个父类。 Student类 父类 Person类 Teacher类
- 功能更丰富,容易出现继承关系混乱。典型 菱形继承 、 钻石继承问题
- 菱形继承问题:
- 多继承关系中,重写同名方法时,本类想调用自身方法时,可以继续使用父类该方法,但是发现,会造成父类方法多次调用情况,资源浪费。
父类.方法()
- super().方法()
- super(type,obj)
- type:类型 默认当前类
- obj:对象 默认 当前对象
- mixIn 为了避免多继承混乱问题。 设计类,不依托于子类实现。单一功能类随机动态组合。
多态:
- 多种形态
- 向 不同的对象 发送 相同的指令需求,会做出不同的响应/回复。--------zz:老师发相同卷子,收的卷子却不同
- 手机:小米 华为 oppo 诺基亚 vivo,iphone,三星 ,黑莓。。。
~~~
装饰器
内置装饰器:
- @classmethod 单独成行。在 被装饰方法/函数的上面
- 类方法:cls - class __new__() 中也有。跟self类似。类调用方法,不需要手动传参,系统会自动把类的地址传递
- 实例方法:实例对象调用方法 self自动传入。类可以调用,不推荐
- @staticmethod 静态方法
- 普通函数
- @property
- 两种使用方式 property
- property(fget,fset,fdel) method
- @property
异常
异常:Exception
异常 处理异常
尽量避免程序出现异常 ,程序中需要保证 健壮性,异常兼容处理。 不仅仅错误。
- 报错
- 常见异常:
- - Exception
- - syntaxError
- - NameError 名字未定义 not defined
- - valueError 值异常 remove(val)
- - TypeError 类型异常 str + int
- - KeyError 键异常 d1[jian]
- - indexError index 超出范围
- - attributeError 属性异常 对象.属性 不存在
- - importError 导入异常 模块未找到
- - unboundlocalError 局部作用域内 修改全局变量
- - unicodeError
- - ZeroDivisionError
- 自定义异常
- 捕获异常 处理异常- 结构:
1.try except
try:
测试代码段
except 异常类型:
捕获处理
- 如果 try 后的 测试代码 没有异常,正常执行完毕,try-except结构即结束。
- 一旦 try后的 测试代码中有异常,程序直接进入到except进行 异常类型 判断,
如果异常类型一致,则捕获成功,tryexcept之后代码正常执行;
否则 python系统进行异常捕获处理。
2. try except as
try:
测试代码段
except 异常类型 as 变量名:
变量名 该异常详细提示信息
3. try 多个except
try:
测试代码段
except 异常类型1 as 变量名:
变量名 该异常详细提示信息
except 异常类型2 as 变量名:
变量名 该异常详细提示信息
except 异常类型3 as 变量名:
变量名 该异常详细提示信息
....
- 一旦测试代码段中出现异常,程序会逐一到 except中进行异常判断,如果有匹配成功,则捕获成功。
- 异常 存在继承关系。Exception 常见异常的父类
- 多个except 时,尽量 子类异常(小范围) 放在前,父类异常(大范围)在后。
防止 父类异常在前 直接捕获,而让子类except失去意义。
- 特例:
try:
测试代码
except:
相关信息
等同于
try:
测试代码
except Exception: Exception 父类
相关信息
4. try-except-finally finally 最终 最后
try:
测试代码
except 异常类型1:
捕获信息
finally:
代码段
- finally必须放在当前结构最后
- 不管 测试代码是否有异常,都会执行finally。
- 如果测试代码有异常,并且 except未能成功捕获,finally结构也会正常执行,执行完毕之后系统才会进行捕获。
5.手动 自定义异常 模拟 系统异常 异常提示信息 类
异常分类:
- 系统定义异常
- 自定义异常
raise 举起 提升 提出 抛出异常
- raise 自定义异常名
- 结合使用 try except 结构 对 该异常进行 捕获处理
~~~
模块
模块:
- 一个个py 文件
- 存在意义?
- 一个程序 代码量繁重 一个py文件中,程序庞大。
- 减少代码量 结构 模块清晰化 分解成 多个 py文件-模块
- 复用性高 维护性高-----------------
- 模块开辟了独立的命名空间------------------
- 函数:判断闰年函数。功能 复用性高。
- 模块 组成
- 尽量 模块:变量 函数 类----------------
- 如果有 测试代码,一定要特别注意。 建议放在 main中。------------------
- 如果当前py模块,作为导入模块,main之内的代码不会执行。 __name__ == '__main__'
- __name__
- 值1: __main__ 当前py文件作为主程序来运行
- 值2: 模块名 当前py文件作为导入模块来使用
- 模块使用 导入
- import 模块名1,模块2.。。。
- 标识符命名规则 尽量英文命名 数字 module_base
- 关于 数字开头的 模块名 特别注意 需要导入importlib模块来完成导入 比较麻烦。不建议数字开头
- import 模块名1 as 别名,模块名2 as 别名
- 注意:别名尽量不要跟 当前py文件中 变量 出现命名冲突。
- 以上两种 都需要是 类似 对象访问属性 点语法方式来使用。
- from 模块名 import 标识符名称1,标志名称2.。。
- 导入后,直接操作 标识符名称即可。
- from 模块名 import *
- * 通配符 全部 导入 该模块中 所有的数据都导入
- package:
- 包 类似 文件夹 文件管理者 标志 __init__.py
- 包名 全小写
- - import 包名.模块名1,包名.模块名2
- - import 包名.模块名 as 别名
- - from 包名 import 模块名
- - from 包名.模块名 import 标识符名称 不推荐
- - from 包名 import * 不要这样写。这样写 导入 该包内的 __init__.py
- 建议: 使用包名时,尽量能够把 模块所在包的完整包路径全部写入。
- time模块
- random模块 随机整数 小数 随机选择某个元素 列表
- 上课 做作业
- datetime() 模块
文件
import pickle #pickle 泡菜 f = open('t.txt','wb') # f.write('字符串') l1 = [1,2,3,4] # f.write # 数据对象 任意对象 pickle.dump(l1,f) f.close() f1 = open('t.txt','rb')result1 = pickle.load(f1) print(result1)
os模块(directory)
- - getcwd() 返回当前工作目录
- - listdir(url) 返回指定路径下的目录
- url 路径
- path路径
- - remove(url) 删除指定路径
- -删除 文件
- - mkdir(url)
- 创建单层空目录 目录名注意不能冲突
- - rmdir(url) 删除为空的目录 - makedirs(p) 创建多层目录
- 目录必须为空
- 目录必须为空
- - removedirs(p) 删除多层空目录
- - rename(old, new)重命名
- - system() 类似黑窗口操作指令 dir cmd - sep 返回当前操作系统的路径分隔符
- curdir 表示当前目录
- pardir 表示上一级目录
- - linesep 返回当前操作系统的换行分隔符
- - name 返回当前操作系统的名字
import os # help(os) #url 统一资源定位符 Uniform Resource Locator # 1.getcwd() current working directory print(os.getcwd()) # D:\AI161\day17 # 2.listdir(url) print(os.listdir('D:\AI161\day17')) # ['day17.md', 'os模块01.py', '__init__.py'] # 3.remove(url) os.remove('txt.py') os.remove(r'D:\AI161\day17\os') # 4.mkdir() os.mkdir('so') # 相对路径下 当前目录下创建so空目录 os.mkdir('D:\AI161\day17\os\path1') os.mkdir('D:\AI161\day17\st\st1') os.mkdir('D:\AI161\day17\st') # 5.rmdir() remove 删除 os.rmdir('D:\AI161\day17\st') # 必须删除空目录 os.rmdir('st') # 必须删除空目录 os.rmdir('D:\AI161\day17\ss') # 6.makedirs() os.makedirs('mulu\mul1\mulu2') os.makedirs('D:\AI161\day17\os\path1\path2\path3') # 7.removedirs() 删除多层空目录 递归式删除 os.removedirs('D:\AI161\day17\os') os.removedirs(r'D:\AI161\day17\mulu\mul1\mulu2') # 递归式删除 os.removedirs('ss') # 8.rename(old,new) 不建议绝对路径相对路径混用。保证 当前目录下进行重命名 os.rename('D:\AI161\day17\st\st1.py','D:\AI161\day17\st\st2.py') os.rename('D:\AI161\day17\st\st2.py','st1.py') os.rename('st1.py','D:\AI161\day17\st\st2.py') os.rename('st2.py','st1.py') # 9.system() print(os.system('cmd')) # 10.curdir current directory 当前目录 print(os.curdir) # . # 11.pardir parent directory 上级目录 print(os.pardir) # .. print(os.listdir('.')) print(os.listdir('..')) os.remove('../day16/t.txt') os.remove('st1.py') # 12.name 当前操作系统名字 print(os.name) # windows下 nt microsoft network technology mac:posix # 13.sep seperator 当前系统下路径分隔符 print(os.sep) #\ windows系统 linux mac / # 14.linesep 当前操作下换行符 print(os.linesep) # \n \r windows系统 Linux mac: \n
os.path模块
- basename(path) 返回 去掉文件路径的文件名
- 去掉文件路径的文件名+后缀
- dirname(path)
返回去掉文件名后缀之外的目录路径 不包含文件名
- split(path)
- 返回 元组。
- 两个元素:目录路径 文件名+后缀
- splitext(path)
- 返回 分隔路径 元组
- 两个元素:路径+文件名 后缀
- join(path1,path2..)
- 返回 多个路径组合为一个新路径
- getsize(url)
- 返回 文件大小 字节
- exists(path) 判断某个路径是否存在
- isabs() 判断是否为绝对路径
- isdir() 判断是否为目录
- isfile() 判断是否为文件
- ismount() 判断是否为盘符 E:\\ /
- samefile(path1,path2) 判断两个路径是否为相同的文件
- 判断是否为相同路径 相同目录 文件
import os.path as p
import os
# 1.basename() base 基础 基本 print(p.basename('D:\AI161\day17\os模块01.py')) # os模块01.py #2.dirname() dir目录 print(p.dirname('D:\AI161\day17\os模块01.py')) # D:\AI161\day17 # 3.split() 分隔 print(p.split('D:\AI161\day17\os模块01.py')) # ('D:\\AI161\\day17', 'os模块01.py') # 4.splitext() extend extra print(p.splitext('D:\AI161\day17\os模块01.py')) # ('D:\\AI161\\day17\\os模块01', '.py') # 以上 4个 相对路径 print(p.basename('os模块01.py')) print(p.dirname('day17/os模块01.py')) print(p.split('day17/os模块01.py')) print(p.split('os模块01.py')) print(p.splitext('os模块01.py')) # 5.join() 连接 print(p.join('D:\AI161\day17\st','a')) # D:\AI161\day17\st\a path = p.join('D:\AI161\day17\st','a') os.mkdir(path) #st文件必须先创建 print(p.join('a','b\c')) # a\b path1 = p.join('a','b\c') os.makedirs(path1) # 6.getsize() 查看文件 大小 print(p.getsize('os模块01.py')) # 7.exists() 判断路径是否存在。可以是判断文件,目录 print(p.exists('os模块01.py')) # True print(p.exists('D:\AI161\day17\s')) # True #8.isabs() absolute绝对的 是否为绝对路径 文件 目录 均可 print(p.isabs('os模块01.py')) print(p.isabs('./os模块01.py')) print(p.isabs('D:\AI161\day17\os模块01.py')) print(p.isabs('D:\AI161\day17')) print(p.isabs('.')) # 9.isdir() 是否为目录 print(p.isdir('D:\AI161\day17')) print(p.isdir('..')) print(p.isdir('../day15')) print(os.listdir('../day15')) # 10.isfile() 判断路径是否为文件 print(p.isfile('../day15/else10.py')) print(p.isfile('D:\AI161\day15\复习01.py')) # 11.ismount() mount 挂载点 # 判断是否为 盘符 windows 系统下 # linux mac / print(p.ismount('D:\\')) # 12.samefile(path1,path2) 不同路径下 相同文件 文件名一样 print(p.samefile(r'D:\AI161\day17\a.py',r'D:\AI161\day17\a\a.py')) print(p.getsize(r'D:\AI161\day17\a.py')) print(p.getsize(r'D:\AI161\day17\a\a.py')) print(p.samefile(r'D:\AI161\day17\txt1.txt',r'D:\AI161\day17\a\txt1.txt')) print(p.samefile(r'D:\AI161\day17\txt1.txt','txt1.txt'))
正则表达式
import re
# 正则表达式 re模块 findall() print(re.findall('a','161abc,.abcacbca')) print(re.findall('ab','161abc,.abcacbca')) print(re.findall('abca','161abc,.abcacbca')) print(re.findall('abcaa','161abc,.abcacbca')) print(re.findall('[a-z]','1612345abc,.+-')) # ['a', 'b', 'c'] print(re.findall('[0-9]','161234509abc,.+-')) # ['1', '6', '1', '2', '3', '4', '5', '0', '9'] print(re.findall('[^0-9]','161234509abc,.+-')) # ['a', 'b', 'c', ',', '.', '+', '-'] print(re.findall('[^0-9]','161234509abc,.+-')) # ['a', 'b', 'c', ',', '.', '+', '-'] print(re.findall('\d','1611234abcd,.')) # ['1', '6', '1', '1', '2', '3', '4'] print(re.findall('\w','161abcABC_,.+-')) # ['1', '6', '1', 'a', 'b', 'c', 'A', 'B', 'C', '_'] print(re.findall('\s',' ab c\ncd\t45\r+- ')) # [' ', ' ', '\n', '\t', '\r', ' ']
# 行首匹配 print(re.findall('^a','abca1234a')) print(re.findall('^a','bca1234a')) print(re.findall('^abc','abc123abc')) print(re.findall('bc$','abcbcbc')) print(re.findall('^a$','abaaa')) print(re.findall('^a$','aa')) print(re.findall('^a$','a')) print(re.findall('^abc$','abc')) print(re.findall('^abc$','abbc'))
print(re.findall('ab|ec','abcdecab,.-=')) print(re.findall('ab|bc','abcdecab,.-=')) print(re.findall('bc|ab','abcdecab,.-='))
# 爬网页 非常长字符串 # 图片 <img src='http://(,,,,.............................png)'> # 书写 验证手机号 正则 print(re.findall('[0-9]{11}','123456789012334qagabb')) print(re.findall('^[0-9]{11}$','a2345678901')) # qq号 大等于5位 print(re.findall('[0-9]{5,}','12345678abcdefa')) print(re.findall('[0-9]{5,}','1234abcdefa')) print(re.findall('[0-9]{5,}','12345abcdefa')) print(re.findall('[0-9]\w{2,5}','12345a_abcA')) print(re.findall('[0-9]\w{,5}','12345a_abcA')) print(re.findall('a*','abcabca')) # ['a', '', '', 'a', '', '', 'a', ''] print(re.findall('a+','abcabca')) print(re.findall('ba?a','abcabcabc')) # ['a', '', '', 'a', '', '', 'a', '', '', '']
print(re.findall('(ab)cd','abcefabcddfabcd'))#['ab', 'ab']
练习
# 练习: # 手写 验证座机号 # 手机号复杂 11位 1开头 # QQ号 5位以及以上 # 用户名:6-16位 数字 字母 下划线 # 邮箱: 6-10位数字字母下划线@qq.com sina.com 163.com 126.com 139.com 2-3位.com或 .cn # 座机号 ''' 010-12345678 0123-1234567 0123-12345678 ''' pattern1 = '^0\d{2}-\d{8}$' print(re.findall(pattern1,'010-12345678'))
import re ''' 量词: - * >=0 贪婪模式 - + >= 1 贪婪模式 - ? 0或1 非贪婪 ''' print(re.findall('ba*','babaa,.123')) print(re.findall('ba*','babaa,.123')) print(re.findall('ba+','babaaaaaaaa....b')) print(re.findall('\w0*','12300')) print(re.findall('\w0+','12300')) print(re.findall('\w0?','12300'))
re模块常用方法
findall(pattern,string,flags)
-
match() 匹配
- 从行首进行匹配,如果行首能匹配成功,则直接返回Match对象,如果不成功,返回None
- 返回值:
- 匹配成功 match对象
- 匹配不成功,None
search() 搜查搜寻
- 与 match()区别,不仅限于行首匹配。匹配字符串个数,仅限一个,匹配成功直接返回,不会继续匹配。
- 返回值:
- 匹配成功,match对象
- 匹配不成功,None
finditer()
- 检索整个字符串,只要有匹配,全部返回 成为iterator对象的元素。
- 返回值:
- iterator对象,迭代器对象。不管是否匹配成功
- 查看该对象元素,元素:match对象。
- 如何查看iterator对象元素?for循环
- iterator对象 迭代器对象是可迭代对象
sub(pattern,repl,string[,count])
- 参数:
- pattern:正则
- repl:replace 替换字符
- string:被检索的字符长串
- count :替换次数
- 返回值:替换后的新字符串
subn(pattern,repl,string[,count])
- 参数:同上 sub()
- 返回值: 元组
- 元组中元素: 替换后的新字符串 , 替换次数
split(pattern,string[,maxsplit])
- 参数:
- pattern:正则 分隔符
- string:字符串
- maxsplit:指定分隔符数量/次数 最大值
- 返回值:列表 元素:分隔符分隔成的子串
compile(pattern)
- 生成 正则表达式对象
# 1.match() print(re.match('bac','bac1234bac')) print(re.match('bac','bac1234bac')) print(re.match('bacd','bac1234bac')) print(re.match('bac','1234bac')) # 2.search() print(re.search('bacd','1234bac')) print(re.search('bac','bac1234bac')) print(re.search('bac','1234bacbac')) # match对象 result1 = re.search('bac','1234bacbac') # group() 查看匹配成功的字符串 print(result1.group()) # 匹配字符串 # start() 查看匹配成功字符串的起始位置(包含) print(result1.start()) # 4 # end() 查看 匹配成功字符串的结束位置(不包含) print(result1.end()) # 7 # span() 元组形式 元素:start end print(result1.span()) # (4, 7) # finditer() print(re.finditer('bac','bac123bcaer')) print(re.finditer('bacd','bac123bcaer')) result1 = re.finditer('bac','bac123bcaerbacbacbac') for i in result1: print(i) #sub() 替换 replace() print(re.sub('abc','***','abc123abc456',1)) # ***123***456 # subn() print(re.subn('abc','***','abc123abc456abc',2)) # ('***123***456', 2) #split() print(re.split('#','abc#abc#cd',5)) # ['abc', 'abc', 'cd'] # split()字符串 print('abc#bc#dcc'.split('#',3)) #compile() p1 = re.compile('\w') print(p1) print(p1.findall('abc1'))
'''
re模块下 compile() 生成正则对象
自取名 = re.compile('正则表达式', re.模式修饰符)--------自总
正则对象 常用方法 同名 于 re模块下常用方法
findall()
finditer()
match()
search()
sub()
subn()
split()
'''
'''
模式修饰符:
S: 让.识别换行\n
I: 忽略英文字符大小写
M: 跨行检索 ^$起作用
X: 正则式子格式实现跨行,格式更友好,方便易读
'''
import re p1 = re.compile('abc',re.I) print(p1.findall('abcABCABC')) # ['abc', 'ABC', 'ABC'] p1 = re.compile('.',re.S) print(p1.findall('.abc123\n-+, ')) # M # s1 = '1234abc' \ # '789' # s1 = ''' # 123abc # 789 ''' p1 = re.compile('^\d',re.M) print(p1.findall(s1)) # x s1 = ''' # ^0\d{2}-\d{8}$| # ^0\d{3}-\d{7}$| # ^0\d{3}-\d{8}$ ''' # s1 = ''' ^0 \d{2} - \d{8}$ ''' p1 = re.compile(s1,re.X) print(p1.findall('0123-4567890'))
线程进程
* 电脑基础知识
电脑:任务调度
CPU-人的大脑。计算机的中枢
进程-正在进行/运行的程序
程序 如何运行?
你的电脑同时启动了多个程序。pycharm wps vnc fscapture typora
每个程序运行,前提 抢到了 时间片。时间片 电脑分配时间片,时间片长短问题不一定一样。
轮转式争抢时间片。
宏观并行,微观串行。(单核CPU情况)
~~~
* 进程
概念:一个程序正在运行的状态 一个个正在运行程序
进程特点:
- 动态性 正在运行状态
- 独立性 进程之间相互独立
- 并发性
- 结构性
- 进程 构成:程序 数据资源 控制块/指令集
- - 程序: 描述 进程应该执行的 任务/规则 标准
- - 数据资源:描述 进程过程中 所需要的数据
- - 控制块:描述 进程执行过程中 执行控制单元。
判断 进程 执行进度,进行调控。
进程之间相互独立,进程之间争抢时间资源。
进程之间切换,相对来说,比较笨重,比较慢,消耗资源相较线程大/多。
~~~
* 线程
概念:
轻量级的进程。
进程中包含线程。
线程是 执行任务的基本单元,系统资源分配的基本单位。----------------------
单线程: 进程中只有一个线程。进程内所有资源都供该线程使用。
多线程:进程中有多个线程,多个线程 共享 进程中资源,在该进程内要争抢资源。
- 主线程: 程序一启动,自动创建进程,即进程开启,立刻生成一个线程,即为主线程。
- 子线程: 相对于主线程来说的其他线程。
通过thread 创建线程 是 子线程。
- 一个进程中至少有一个线程,主线程。
* 线程状态
初始状态 就绪状态 运行 消亡
阻塞/暂停
~~~
import threading,time def fn1(name,n): print('fn1 开始时间为 %s' % time.ctime()) for i in range(n): print('%s 中的 %s' % (name,i)) print('fn1 结束时间为 %s' % time.ctime()) def fn2(name,n): print('fn2 的开始时间为 %s' % time.ctime()) for i in range(n): print('%s 中的 %s' % (name,n)) print('fn2 的结束时间为 %s' % time.ctime()) def fn3(name,n): print('fn3 的开始时间为 %s' % time.ctime()) for i in range(n): print('%s 中的 %s' % (name,n)) print('fn3 的结束时间为 %s' % time.ctime()) if __name__ == '__main__': print('主进程的开始时间为%s' % time.ctime()) fn1('召唤师1',10) fn2('召唤师2',10) print('主进程的结束时间为%s'%time.ctime())
手动创建线程
1.方式一:
类 继承自 threading.Thread类 重写run方法,调用改方法时需要使用start()进行调用.
def __init__(self,group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None)
daemon:
setDaemon()设置守护线程
-必须在start()之前设置
-当期主线程结束任务,该守护线程会跟随结束
import threading import time class Movive(threading.Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print('{}开始时间为{}'.format(self.name,time.ctime())) for i in range(15): print('{}中的第{}集'.format(self.name,i)) time.sleep(0.1) print('{}结束时间为{}'.format(self.name,time.ctime())) class Movie1(threading.Thread): def __init__(self,name): super().__init__() self.name = name def run(self): print('{}开始时间为{}'.format(self.name,time.ctime())) for i in range(20): print('{}中的第{}集'.format(self.name,i)) print('{}结束时间为{}'.format(self.name,time.ctime())) if __name__ == '__main__': print('主程序开始时间为%s' % time.ctime()) #创建的对象 t1 = Movive('小猪----佩奇') t2 = Movie1('乡村爱情') t1.start() t2.start() print('主程序结束时间为{}'.format(time.ctime())) #发现小猪小猪佩奇的线程(语句)在前面,可能优先执行但是由于其中有睡眠语句, # 故而被其他线程抢占时间片,其他线程(语句)虽然位置靠后但抢占了时间片,抢先执行完了
线程创建方式二:
-直接通过 Thread类 直接创建
def __init__(self,group=None, target=None, name=None,
args=(), kwargs=None, *, daemon=None)
- -group:预留参数
- -target:目标任务 函数
- -name:线程名字
- -args:参数 必须是元祖
- -kwargs:参数 关键字方式传参
- -*:其他需求处理
- -daemon:设置是否为守护线程 True False
相关方法 属性
- -jion() 阻塞当前主线程,等待 调用该方法的线程执行完毕后,在执行当前线程
- -setName()设置线程名字 也可以 在插件线程时通过参数设置
- -getName()获取线程名字
- -currentTread() 查看当前线程信息 名字
- -ident 查看线程的id
- -is_alive()判断线程是否存活
- -setDaemon()设置守护线程
- -必须在start()之前设置
- -当期主线程结束任务,该守护线程会跟随结束
import threading import time def fn1(name, n): print('fn1的开始时间为{}',format(time.ctime())) for i in range(n): print('{}的第{}集'.format(name,i)) # time.sleep(0.1) print('fn1的结束时间为{}'.format(time.ctime())) def fn2(name,n): print('fn2的开始时间为{}',format(time.ctime())) for i in range(n): print('{}的第{}集'.format(name,i)) time.sleep(0.2) print('fn2的结束时间为{}'.format(time.ctime())) if __name__ == '__main__': print('主线程的开始时间为{}'.format(time.ctime()),threading.currentThread()) #创建线程 # t1 = threading.Thread(target=fn1, args=('小猪佩----佩奇',10),daemon=True) # t2 = threading.Thread(target=fn2,args=('乡村爱情',10),daemon=True) #没有守护线程 t1 = threading.Thread(target=fn1, args=('小猪佩----佩奇', 10)) t2 = threading.Thread(target=fn2, args=('乡村爱情', 10)) t2.setDaemon(True) t1.start() t2.start() # t1.join() # t2.join()#t2执行时间较长,用该语句,阻塞主线程,把线程t2加(jion)进来,等线程t2执行完在结束主线程 print('主线程结束时间{}'.format(time.ctime()))
* 线程同步
1 import threading,time 2 ''' 3 数据脏读 数据污染 4 14 17行 代码 不可分割业务逻辑 - 原子操作 不被破坏 数据保证一致。 5 ''' 6 7 class MyList: 8 def __init__(self): 9 self.l = ['A','B','','',''] 10 self.index = 2 11 12 def add_member(self,val): 13 14 self.l[self.index] = val # ABC index 2 ABD 15 print('%s 进来了 %s' % (threading.currentThread(),self.l)) 16 time.sleep(0.01) # 时间片到期 t2 17 self.index += 1 18 19 def get_list(self): 20 return self.l 21 22 if __name__ == '__main__': 23 24 # 主线程 25 print('%s start %s' % (threading.currentThread(),time.ctime())) 26 27 # 实例对象 28 mylist = MyList() 29 30 # 创建 线程对象 31 t1 = threading.Thread(target=mylist.add_member,args=('C',)) 32 t2 = threading.Thread(target=mylist.add_member,args=('D',)) 33 34 t1.start() 35 t2.start() 36 37 t1.join() 38 t2.join() 39 40 print(mylist.get_list()) 44 print('%s end %s' % (threading.currentThread(),time.ctime()))
有 睡语句time.sleep(0.01) 时
输出结果-->
<_MainThread(MainThread, started 428)> start Thu May 9 22:19:30 2019
<Thread(Thread-1, started 2020)> 进来了 ['A', 'B', 'C', '', '']
<Thread(Thread-2, started 10228)> 进来了 ['A', 'B', 'D', '', '']
['A', 'B', 'D', '', '']
<_MainThread(MainThread, started 428)> end Thu May 9 22:19:30 2019
无 睡语句time.sleep(0.01) 时
输出结果-->
<_MainThread(MainThread, started 12520)> start Thu May 9 22:19:54 2019
<Thread(Thread-1, started 11092)> 进来了 ['A', 'B', 'C', '', '']
<Thread(Thread-2, started 3944)> 进来了 ['A', 'B', 'C', 'D', '']
['A', 'B', 'C', 'D', '']
<_MainThread(MainThread, started 12520)> end Thu May 9 22:19:54 2019
分析:
锁 对 线程 有影响!!!
对列表 l 也有影响!
锁 对 进程 无影响!!!
线程t1,t2在一个进程中,共享进程中的资源
*进程同步
1 import multiprocessing,time,os 2 ''' 3 数据脏读 数据污染 4 14 17行 代码 不可分割业务逻辑 - 原子操作 不被破坏 数据保证一致。 5 ''' 6 lock = multiprocessing.Lock() 7 class MyList: 8 def __init__(self): 9 self.l = ['A','B','','',''] 10 self.index = 2 11 12 def add_member(self,val): 13 lock.acquire() 14 print('%s 申请到lock' % os.getpid()) 15 self.l[self.index] = val # ABC index 2 ABD 16 print('%s 进来了 %s' % (os.getpid(),self.l)) 17 time.sleep(0.01) # 时间片到期 t2 18 self.index += 1 19 print('%s 释放了lock' % os.getpid(),self.l) 20 lock.release() 21 print('%s 释放了lock' % os.getpid(), self.l) 22 def get_list(self): 23 return self.l 24 25 if __name__ == '__main__': 26 27 # 28 print('%s start %s' % (os.getppid(),time.ctime())) 29 30 # 实例对象 31 mylist = MyList() 32 33 # 34 p1 = multiprocessing.Process(target=mylist.add_member,args=('C',)) 35 p2 = multiprocessing.Process(target=mylist.add_member,args=('D',)) 36 37 p1.start() 38 p2.start() 39 40 p1.join() 41 p2.join() 42 43 print(mylist.get_list()) 44 45 46 47 print('%s end %s' % (os.getppid(),time.ctime()))
12460 start Thu May 9 22:25:12 2019
3284 申请到lock
3284 进来了 ['A', 'B', 'C', '', '']
10588 申请到lock
10588 进来了 ['A', 'B', 'D', '', '']
3284 释放了lock ['A', 'B', 'C', '', '']
3284 释放了lock ['A', 'B', 'C', '', '']
10588 释放了lock ['A', 'B', 'D', '', '']
10588 释放了lock ['A', 'B', 'D', '', '']
['A', 'B', '', '', '']
分析:
锁 对 进程 无影响!!! 进程间的执行间是相互独立的!!! 对列表l也无影响
锁 对 线程 有影响!!!
锁 对 列表 l 也有影响!
*加锁问题2
import threading,time lock1 = threading.Lock() lock2 = threading.Lock() def fn1(): print('fn1 start %s' % time.ctime()) print('fn1准备申请lock1') lock1.acquire() print('fn1申请了lock1' ) time.sleep(3) print('fn1 释放了 lock1') lock1.release() print('fn1准备申请lock2') lock2.acquire() print('fn1 申请了lock2') print('fn1 释放了 lock2') lock2.release() def fn2(): print('fn2 start %s' % time.ctime()) print('fn2准备申请lock2') lock2.acquire() print('fn2申请了lock2') print('fn2 释放了 lock2') lock2.release() print('fn2准备申请lock1') lock1.acquire() print('fn2 申请了lock1') print('fn2 释放了 lock1') lock1.release() if __name__ == '__main__': print('main start %s' % time.ctime()) t1 = threading.Thread(target=fn1) t2 = threading.Thread(target=fn2) t1.start() t2.start() t1.join() t2.join() print('main end %s' % time.ctime())
运行结果
main start Wed May 8 19:50:55 2019 fn1 start Wed May 8 19:50:55 2019 fn1准备申请lock1 fn1申请了lock1 fn2 start Wed May 8 19:50:55 2019 #(1) fn2准备申请lock2 fn2申请了lock2 fn2 释放了 lock2 fn2准备申请lock1 fn1 释放了 lock1 fn1准备申请lock2 fn1 申请了lock2 fn1 释放了 lock2 fn2 申请了lock1 fn2 释放了 lock1 main end Wed May 8 19:50:58 2019
分析:
函数是自上而下进行,线程是枪时间片执行的,
当函数中有sleep()时,以该函数设置的线程进入睡眠,故而以其他函数为设置的线程将抢占时间片,执行线程函数.
fn1先执行,当fn1在执行到睡眠语句进行睡眠时,fn2申请可用锁,当申请的锁没有被fn1释放,故而进行等待,过来一会,fn1执行了释放锁clock1的语句,此时因fn2执行到申请锁clock1的语句时,要等待fn1释放锁clock1,从而fn2被迫进行等待,时间片交给了fn1,让fn1去执行释放锁clock1的操作语句,故而线程1得到了时间片,直到执行完毕fn1之后(之后的fn1中再没sleep()语句),线程1先结束,之后时间片全用来执行fn2,线程2再结束,由于t1.join(),t2.join()语句的存在,主线程最后结束。
*线程绑定对象
import threading, time l = threading.local() # 全局变量 局部变量 l.val = 'main' def fn1(value): l.val = value # l.自取名 print('%s 中 %s' % (threading.currentThread(), l.val)) if __name__ == '__main__': print('main start %s' % time.ctime()) t1 = threading.Thread(target=fn1, args=('t1',)) t2 = threading.Thread(target=fn1, args=('t2',)) t1.start() t2.start() t1.join() t2.join() print(l.val) print('main end %s' % time.ctime())
输出结果
main start Wed May 8 20:36:22 2019
<Thread(Thread-1, started 9476)> 中 t1
<Thread(Thread-2, started 7640)> 中 t2
main
main end Wed May 8 20:36:22 2019
*进程池
import multiprocessing,os,time ''' 进程: - Pool 类 水池 - 进程池 批量创建 进程 - 规定进程数量,用于调用 - 规定数量之内5(进程池未满),进程正常创建,执行任务; 进程池一旦满了,新进程等待进程池中某个进程执行任务完毕,再进入进程池 - 方法: - apply() - python3.0后 废弃 - apply_asyn() - apply 应用,申请 创建进程 执行任务 - 异步 非阻塞 - close() - 关闭进程池。不允许新的进程进入 - join() - 阻塞当前进程,等待子进程执行任务完毕之后,再执行当前进程。 ''' def work(name): print('%s 看了 %s' % (os.getpid(), name)) time.sleep(1) if __name__ == '__main__': print('%s main start %s' % (os.getppid(), time.ctime())) # 创建进程池 pools = multiprocessing.Pool(5) # callback 回调函数 for i in range(10): pools.apply_async(work,args=('复联4',)) pools.close() # 阻塞当前进程 pools.join() print('%s main end %s' % (os.getppid(), time.ctime()))
*进程-Queue
import multiprocessing,time,os ''' 进程特点:相互独立 - 进程之间相互通信 传输数据 Queue: 队列 - 先进先出 - put() 放入 - get() 取 拿 - terminate() 中断任务执行 ''' def put_val(q): for i in ['A','B','C','D']: print('%s 传入queue' % i) q.put(i) time.sleep(0.5) def get_val(q): while True: val = q.get() print('从queue中 取 %s ' % val) if __name__ == '__main__': print('%s main start %s' % (os.getppid(), time.ctime())) q = multiprocessing.Queue() p1 = multiprocessing.Process(target=put_val, args=(q,)) p2 = multiprocessing.Process(target=get_val, args=(q,)) p1.start() p2.start() p1.join() # p2.join() # 中断当前任务执行 p2.terminate() print('%s main end %s' % (os.getppid(), time.ctime()))
*进程-Pipe
import multiprocessing,time,os ''' 进程 通信: Pipe 管道 Pipe(duplex) duplex = True/False - 元组:两个元素, 管道两端 con1 con2 - True:全双工模式。conn1 conn2 既可以发送数据,也可接收数据 - False:非双向模式。conn1 接收数据 conn2发送数据 - send() 发送 - 发送数据 对象 - recv() 接收 ''' def send_val(p): for i in ['A','B','C','D']: print('%s 发送到Pipe' % i) p[1].send(i) # p[1]代表的是啥?不明白
time.sleep(0.5) def recv_val(p): while True: val = p[0].recv() # p[0]代表的是啥?不明白
print('从pipe中 接收 %s ' % val) if __name__ == '__main__': print('%s main start %s' % (os.getppid(), time.ctime())) p = multiprocessing.Pipe() t1 = multiprocessing.Process(target=send_val, args=(p,)) t2 = multiprocessing.Process(target=recv_val, args=(p,)) t1.start() t2.start() t1.join() t2.terminate() print('%s main end %s' % (os.getppid(), time.ctime()))
*消费者模型
import queue,time,threading ''' 生产者消费者模型: - 生活 供需平衡 - 生产者 生产 消费者 消费 生产过剩 供大于求 需求大于生产 供不应求 - 高并发 - 仓储 中间内存缓存区 - 生产者 生产数据 把 数据 传输给 仓储缓存区,消费者 需要数据 从 仓储缓存区 取 数据,从而避免 生产者消费者直接通信。 进程通信:Queue Pipe 单独模块:queue Queue ''' def producer(q,name): n = 1 while True: print(' %s 生产 第 %s个商品' % (name, n)) q.put(n) time.sleep(2) n += 1 # def consumer(q,name): while True: val = q.get() print('%s 消费 第 %s' % (name, val)) if __name__ == '__main__': print('%s start %s' % (threading.currentThread(), time.ctime())) q = queue.Queue() t1 = threading.Thread(target=producer,args=(q,'张润之')) t2 = threading.Thread(target=consumer,args=(q,'李志坤')) t3 = threading.Thread(target=consumer,args=(q,'郝鹏')) t4 = threading.Thread(target=consumer,args=(q,'宋泽凯')) l = [] l.append(t1) l.append(t2) l.append(t3) l.append(t4) for i in l: i.start() print('%s end %s' % (threading.currentThread(), time.ctime()))
输出结果:
<_MainThread(MainThread, started 1620)> start Wed May 8 21:13:23 2019
张润之 生产 第 1个商品
李志坤 消费 第 1 个商品
<_MainThread(MainThread, started 1620)> end Wed May 8 21:13:23 2019
张润之 生产 第 2个商品
李志坤 消费 第 2 个商品
张润之 生产 第 3个商品
郝鹏 消费 第 3 个商品
张润之 生产 第 4个商品
宋泽凯 消费 第 4 个商品
张润之 生产 第 5个商品
李志坤 消费 第 5 个商品
张润之 生产 第 6个商品
郝鹏 消费 第 6 个商品
张润之 生产 第 7个商品
宋泽凯 消费 第 7 个商品
张润之 生产 第 8个商品
李志坤 消费 第 8 个商品
张润之 生产 第 9个商品
郝鹏 消费 第 9 个商品
张润之 生产 第 10个商品
宋泽凯 消费 第 10 个商品
张润之 生产 第 11个商品
李志坤 消费 第 11 个商品
张润之 生产 第 12个商品
郝鹏 消费 第 12 个商品
张润之 生产 第 13个商品
宋泽凯 消费 第 13 个商品
张润之 生产 第 14个商品
李志坤 消费 第 14 个商品
张润之 生产 第 15个商品
郝鹏 消费 第 15 个商品
张润之 生产 第 16个商品
宋泽凯 消费 第 16 个商品
张润之 生产 第 17个商品
李志坤 消费 第 17 个商品
张润之 生产 第 18个商品
郝鹏 消费 第 18 个商品
张润之 生产 第 19个商品
宋泽凯 消费 第 19 个商品
张润之 生产 第 20个商品
李志坤 消费 第 20 个商品
张润之 生产 第 21个商品
郝鹏 消费 第 21 个商品
张润之 生产 第 22个商品
宋泽凯 消费 第 22 个商品
张润之 生产 第 23个商品
李志坤 消费 第 23 个商品
张润之 生产 第 24个商品
郝鹏 消费 第 24 个商品
张润之 生产 第 25个商品
宋泽凯 消费 第 25 个商品
分析:
if __name__ == '__main__': 后面的语句是顺序执行的(张润之生产,按李,郝,宋的顺序依次消费),因为生产的慢,所以消费的速度跟着生产走。
该例子很好体现:
- 仓储 中间内存缓存区 - 生产者 生产数据 把 数据 传输给 仓储缓存区,消费者 需要数据 从 仓储缓存区 取 数据。
理解2
import queue,time,threading def producer(q, name): n = 1 while True: print(' %s 生产 第 %s个商品' % (name, n)) q.put(n) time.sleep(0.5) n += 1 def consumer(q, name): while True: val = q.get() time.sleep(2) print('%s 消费 第 %s 个商品' % (name, val)) if __name__ == '__main__': print('%s start %s' % (threading.currentThread(), time.ctime())) q = queue.Queue() t1 = threading.Thread(target=producer, args=(q, '张润之')) t2 = threading.Thread(target=consumer, args=(q, '李志坤')) t3 = threading.Thread(target=consumer, args=(q, '郝鹏')) t4 = threading.Thread(target=consumer, args=(q, '宋泽凯')) l = [] l.append(t1) l.append(t2) l.append(t3) l.append(t4) for i in l: i.start() print('%s end %s' % (threading.currentThread(), time.ctime()))
输出结果:
<_MainThread(MainThread, started 7768)> start Wed May 8 21:09:53 2019
张润之 生产 第 1个商品
<_MainThread(MainThread, started 7768)> end Wed May 8 21:09:53 2019
张润之 生产 第 2个商品
张润之 生产 第 3个商品
张润之 生产 第 4个商品
李志坤 消费 第 1 个商品
张润之 生产 第 5个商品
郝鹏 消费 第 2 个商品
张润之 生产 第 6个商品
宋泽凯 消费 第 3 个商品
张润之 生产 第 7个商品
张润之 生产 第 8个商品
李志坤 消费 第 4 个商品
张润之 生产 第 9个商品
郝鹏 消费 第 5 个商品
张润之 生产 第 10个商品
宋泽凯 消费 第 6 个商品
张润之 生产 第 11个商品
张润之 生产 第 12个商品
李志坤 消费 第 7 个商品
张润之 生产 第 13个商品
郝鹏 消费 第 8 个商品
张润之 生产 第 14个商品
宋泽凯 消费 第 9 个商品
张润之 生产 第 15个商品
张润之 生产 第 16个商品
李志坤 消费 第 10 个商品
张润之 生产 第 17个商品
郝鹏 消费 第 11 个商品
张润之 生产 第 18个商品
宋泽凯 消费 第 12 个商品
张润之 生产 第 19个商品
张润之 生产 第 20个商品
李志坤 消费 第 13 个商品
张润之 生产 第 21个商品
郝鹏 消费 第 14 个商品
张润之 生产 第 22个商品
宋泽凯 消费 第 15 个商品
张润之 生产 第 23个商品
张润之 生产 第 24个商品
李志坤 消费 第 16 个商品
张润之 生产 第 25个商品
郝鹏 消费 第 17 个商品
张润之 生产 第 26个商品
宋泽凯 消费 第 18 个商品
张润之 生产 第 27个商品
张润之 生产 第 28个商品
李志坤 消费 第 19 个商品
张润之 生产 第 29个商品
郝鹏 消费 第 20 个商品
张润之 生产 第 30个商品
宋泽凯 消费 第 21 个商品
张润之 生产 第 31个商品
张润之 生产 第 32个商品
分析:
if __name__ == '__main__': 后面的语句是顺序执行的(张润之生产,按李,郝,宋的顺序依次消费),因为生产的快,所以消费的速度跟不上生产的速度,消费是按生产的顺序消费的。
* 数据安全
线程同步:
- - 同步
- - 线程A在执行某个任务时,线程B需要等待线程A执行完毕之后,才能执行。
- - 异步
- - 线程A在执行某个任务时,线程B不需等待可以执行其他任务,然后等到线程A给予回应之后,再来执行。
数据污染/脏读:
- 进程中多个线程共享进程资源,所以在操作同一个业务逻辑时,有可能因为同时操作,造成数据前后不一致。
原子操作:
- 不可分割的业务逻辑。
加锁:
- 保护原子操作 不被破坏。锁 同步锁 互斥锁
- - lock对象 acquire() release()
加锁问题:
- - 死锁问题1:
- - 两个或以上线程执行过程中,都因为申请对方所持有的锁标记,从而造成一种相互等待情况。
- - 当前线程多次申请同一把锁,因为程序/锁并没有记录上次自己已经申请过该锁,从而导致死锁情况。
- - RLock 可重入锁
* 线程绑定对象
threading.local()
线程 并发库中高级对象
- - 优势:既拥有了全局变量的性质,又兼有局部变量的特性
- - 该对象 在使用过程中,因为绑定到各自线程上,所以互不影响,各自独立。
* GIL锁
global interpreter lock 全局解释器锁
- Cpython下存在的,官方规定 标准。
- - 解释器:Jpython Ironpython pypy
- GIL锁 导致了 python多线程 几乎变成了 单线程。
一个进程中有一个GIL锁,只有获取了GIL锁的线程,才能进入到进程CPU执行任务
- - 影响多核性能,多核等同于单核。
- - 作用:
- - 线程安全
- - 一个进程中会有多个线程,多个线程在多核情况下能够达到并行,但是:GIL锁
- 一个进程中有一个GIL锁,只有获取了GIL锁的线程,才能进入到进程CPU执行任务。单核
- - 如果是多核,假设当前CPU1线程释放了gil锁标记,其他CPU下线程会被唤醒抢gil锁标记,很有可能 CPU1某个线程抢到gil锁标记,此时被唤醒其他核的线程都是唤醒状态等到任务调度,影响核执行效率,性能。
多线程:
- - 应用于 IO操作。 IO文件读写,网络爬虫 。
多进程:
- - 应用于 CPU密集型。 计算 视频编解码
协程
协程概念:
协程 为 非抢占式 多任务 产生子程序 的计算机程序组件。
协程允许 不同入口点 在不同位置 暂停或开始 执行程序。
单线程下的并发,微线程。
线程切换时,非常耗资源,尤其当线程特别多时,我们可以使用协程。
宏观上,当前只有一个主程序,但是其实是每个子线程中的一小段任务一小段任务执行,通过yield把结果返回给主线程。
特点:
- - 切换特别快速,不耗费资源。避免了线程间的切换。
- - 处理高并发,低成本。
- - 也没有线程中 锁的机制。因为 只有一个线程。
缺点:
- - 如果发生阻塞,会阻塞整个程序
- - 无法使用多核性质。
可迭代Iterable与迭代器Itertor
可迭代:
在Python中如果一个对象有__iter__( )方法或__getitem__( )方法,则称这个对象是可迭代的(Iterable);其中__iter__( )方法的作用是让对象可以用for ... in循环遍历,__getitem__( )方法是让对象可以通过“实例名[index]”的方式访问实例中的元素。换句话说,两个条件只要满足一条,就可以说对象是可迭代的。显然列表List、元组Tuple、字典Dictionary、字符串String等数据类型都是可迭代的。当然因为Python的“鸭子类型”,我们自定义的类中只要实现了__iter__( )方法或__getitem__( )方法,也是可迭代的。“鸭子类型”的概念请参考本系列的第九篇文章。
迭代器:
在Python中如果一个对象有__iter__( )方法和__next__( )方法,则称这个对象是迭代器(Iterator);其中__iter__( )方法是让对象可以用for ... in循环遍历,__next__( )方法是让对象可以通过next(实例名)访问下一个元素。注意:这两个方法必须同时具备,才能称之为迭代器。列表List、元组Tuple、字典Dictionary、字符串String等数据类型虽然是可迭代的,但都不是迭代器,因为他们都没有next( )方法。
上图通过isinstance( )函数分别判断列表、元组、字典、字符串是不是可迭代或迭代器。通过对定义的分析和比较我们得知:迭代器都是可迭代的,但可迭代的不一定是迭代器;可用for ... in循环的都是可迭代的,可用next( )遍历的才是迭代器;next( )是单向的,一次只获取一个元素,获取到最后一个元素后停止;在可迭代的对象中提前存储了所有的元素,而迭代器是惰性的,只有迭代到了某个元素,该元素才会生成,迭代之前元素可以是不存在的,迭代之后元素也可以被销毁,因此迭代器在处理大量数据甚至无限数据时具有加载数据快、占用内存小等优势。
生成器对象 是 迭代器对象 特殊迭代器对象
生成器对象 是 可迭代对象
迭代器对象 是 可迭代对象
可迭代对象 不一定是 迭代器对象。
生成器对象:优势?
- - 节省内存,next() list generator
生成迭代器的几种方式
1.类中实现__iter__( )方法和__next__( )方法,归功于Python的鸭子类型,我们只要在自定义的类中实现了这两个方法,这个类的实例对象就是迭代器,不仅可以用for ... in循环,也可以用next( )遍历。在__next__( )方法中必须对迭代进行检查,超出范围则触发 StopIteration 异常。
2.iter( )函数,Python中的iter( object[, sentinel])函数可用来返回一个迭代器对象,iter( )函数只传入一个参数时,参数必须为可迭代对象;当使用第二个参数sentinel(哨兵)时,第一个参数必须是一个可调用对象。
3.生成器函数,定义生成器函数generator与定义普通函数是一样的,唯一的不同生成器函数中有一个或多个yield,yield与return类似都是用来返回数据,两者的区别是return返回数据后直接退出当前函数,而yield将数据返回后仍然继续运行函数,因此最后生成器函数返回的是一个迭代器对象。下图的代码定义了一个生成器函数用于对字符串的顺序进行翻转。