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.算术运算符
  + - * / % // **
  +:
    - 进行加法运算

  1. - print(10+20)
  2. - print(10+True) # 11 True
  3. - print(10.0+20) # 30.0
  4. - 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

列表 相似

  1. 可以保存 多个 数据。
  2. 下标 有序
  3. 支持切片
  4. 可迭代对象 for循环
  5. 不可修改
  6. 操作符:+ * 成员运算符,比较运算符+逻辑运算符

* 创建

元组中 元素 数据类型 可以多种,类似 列表。
两种方式:
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

 

函数

  • - 特定功能的代码块 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.

* 函数嵌套调用

一个函数内,可以调用另一个函数。 函数嵌套调用

* 作用域

作用域:变量 函数 起作用的区域、范围
分类:

  • - 全局作用域
    • - 定义在顶格
    • - 从定义行开始,整个文件
    • - 全局变量
  • - 局部作用域-函数作用域:函数 限制作用域
    • - 从函数内部,定义行开始,到 函数代码块结束
    • - 局部变量
  • - 总结:
    • - 如果全局变量 与 局部变量 同名,局部作用域下访问变量,优先访问到 局部变量。
    • - 局部作用域下访问变量,优先从当前作用域查找,如果有直接使用,如果没有,逐级往上一级作用域查找,直到找到全局作用域,如果还没有,报错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
异常 处理异常
尽量避免程序出现异常 ,程序中需要保证 健壮性,异常兼容处理。 不仅仅错误。
- 报错
  - 常见异常:

  1. - Exception
  2. - syntaxError
  3. - NameError 名字未定义 not defined
  4. - valueError 值异常 remove(val)
  5. - TypeError 类型异常 str + int
  6. - KeyError 键异常 d1[jian]
  7. - indexError index 超出范围
  8. - attributeError 属性异常 对象.属性 不存在
  9. - importError 导入异常 模块未找到
  10. - unboundlocalError 局部作用域内 修改全局变量
  11. - unicodeError
  12. - 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情况)
~~~

 

* 进程

概念:一个程序正在运行的状态 一个个正在运行程序
进程特点:
- 动态性 正在运行状态
- 独立性 进程之间相互独立
- 并发性
- 结构性
- 进程 构成:程序 数据资源 控制块/指令集

    1. - 程序: 描述 进程应该执行的 任务/规则 标准
    2. - 数据资源:描述 进程过程中 所需要的数据
    3. - 控制块:描述 进程执行过程中 执行控制单元。

判断 进程 执行进度,进行调控。
进程之间相互独立,进程之间争抢时间资源。
进程之间切换,相对来说,比较笨重,比较慢,消耗资源相较线程大/多。
~~~

* 线程

概念:

轻量级的进程。
进程中包含线程。

线程是 执行任务的基本单元,系统资源分配的基本单位。----------------------
单线程: 进程中只有一个线程。进程内所有资源都供该线程使用。

多线程:进程中有多个线程,多个线程 共享 进程中资源,在该进程内要争抢资源。


- 主线程: 程序一启动,自动创建进程,即进程开启,立刻生成一个线程,即为主线程。
- 子线程: 相对于主线程来说的其他线程。

            通过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将数据返回后仍然继续运行函数,因此最后生成器函数返回的是一个迭代器对象。下图的代码定义了一个生成器函数用于对字符串的顺序进行翻转。

 

 

posted on 2019-04-22 09:55  kristong  阅读(341)  评论(0编辑  收藏  举报

导航